ValueError in Thread callback function - Trying to

2019-09-17 16:57发布

问题:

I'm new to threads and having some trouble getting my worker function to update the GUI. I have two variables 'bytes_so_far' and 'size' that I'm trying to pass to back to the GUI instance but when I run it I get a 'ValueError: too many values to unpack'.

import time
import os, sys, wx
from ftplib import FTP_TLS

from threading import Thread
from wx.lib.pubsub import Publisher

########################################################################
class TestThread(Thread):
    """Test Worker Thread Class."""

    #----------------------------------------------------------------------
    def __init__(self):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.start()    # start the thread

    #----------------------------------------------------------------------
    def run(self):
        """Run Worker Thread."""
        # This is the code executing in the new thread.
        HOST = '127.0.0.1'
        USERID = 'User'
        PASSWD = 'Passwd'
        FILE = r'C:\Myfile.zip'


        BLOCKSIZE = 57344
        try:
            ftp = FTP_TLS(HOST)
            ftp.login(USERID, PASSWD)
            ftp.prot_p()
            ftp.voidcmd("TYPE I")
            f = open(FILE, 'rb')
            datasock, esize = ftp.ntransfercmd(
                    'STOR %s' % os.path.basename(FILE))
            size = os.stat(FILE)[6]
            bytes_so_far = 0
            while 1:
                buf = f.read(BLOCKSIZE)
                if not buf:
                    break
                datasock.sendall(buf)
                bytes_so_far += len(buf)
                msg = [bytes_so_far, size]
                Publisher().sendMessage("update", msg)
        except: raise
        finally:
            try:
                datasock.close()
                f.close()
                ftp.voidresp()
                ftp.quit()
                print 'Complete...'
            except: pass

        wx.CallAfter(Publisher().sendMessage, "update", "Thread finished!")

########################################################################
class MyForm(wx.Frame):

    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here")
        self.btn = btn = wx.Button(panel, label="Start Thread")
        self.gauge = wx.Gauge(panel, -1, 100, size=(370, 24))

        btn.Bind(wx.EVT_BUTTON, self.onButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.displayLbl, 0, wx.ALL|wx.CENTER, 5)
        sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        sizer.Add(self.gauge, 0, wx.ALL|wx.CENTER, 5)
        panel.SetSizer(sizer)

        # create a pubsub receiver
        Publisher().subscribe(self.updateDisplay, "update")

    #----------------------------------------------------------------------
    def onButton(self, event):
        """
        Runs the thread
        """
        TestThread()
        self.displayLbl.SetLabel("Thread started!")
        btn = event.GetEventObject()
        btn.Disable()

    #----------------------------------------------------------------------
    def updateDisplay(self, msg):
        """
        Receives data from thread and updates the display
        """
        print msg.data
        bytes_so_far, size = msg.data
        k = 100 * bytes_so_far / size
        self.displayLbl.SetLabel("\rSent %d of %d bytes %.1f%%\r" % (bytes_so_far, size, 100 * bytes_so_far / size))
        self.gauge.SetValue(k)
        self.btn.Enable()

#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

The error is as follows:

Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 14640, in <lambda>
    lambda event: event.callable(*event.args, **event.kw) )
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 750, in sendMessage
    self.__topicTree.sendMessage(aTopic, message, onTopicNeverCreated)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 423, in sendMessage
    deliveryCount += node.sendMessage(message)
  File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\pubsub1\pub.py", line 261, in sendMessage
    listener(message)
  File "F:\Programming\Tests\wxThread_FTP_Funtion.py", line 98, in updateDisplay
    bytes_so_far, size = msg.data
ValueError: too many values to unpack

回答1:

The message

  File "G:\Programming\Tests\wxThread_FTP_Funtion.py", line 126, in updateDisplay
    bytes_so_far, size = list

indicates that the error is occurring when the variable list is assigned to variables bytes_so_far, size. The too many values to unpack error indicates that list has more than 2 elements in it.

But your code does not seem to match that error, since there is no line bytes_so_far, size = list in your sample, so it seems there is some other version of the code out there somewhere that might make the reason for the error more clear.

EDIT:

Thanks for the update. The error should be due to this call:

wx.CallAfter(Publisher().sendMessage, "update", "Thread finished!")

When updateDisplay runs this line of code,

bytes_so_far, size = msg.data

msg.data contains the string "Thread finished!", python attempts to unpack the string into its individual characters in order to pass those into each of the assigned variables bytes_so_far and size, which leads to the too many values to unpack message even though on the face of it there is only one value to assign (the string) and you might expect a need more than x values to unpack message.