【问题标题】:Updating the wx.gauge without while-loop在没有 while 循环的情况下更新 wx.gauge
【发布时间】:2010-01-12 21:04:56
【问题描述】:

这几天一直在想这个问题:

我有一个这样的基本 wxpython 程序:

from MyModule import *

class Form(wx.Panel):
  def __init__(self, parent, id):
    self.gauge = wx.Gauge(...)
    ...
  def ButtonClick(self, event):
    proc = LongProcess()
    while (LongProcess):
      self.gauge.SetValue(LongProcess.status)
      wx.Yield()

其中导入了 MyModule.py:

from threading import *

class LongProcess(self):
  def __init__(self):
    Thread.__init__(self)
    self.start()
  def run(self):
    for i in range(100):
      Do_something()
      self.status = i  

这会根据 LongProcess.status 的值更新仪表,正如预期的那样。但是 while 循环似乎并不合适,因为整个程序使用 100% 的 cpu 负载,因为它会不断检查状态(这并不奇怪)。有没有办法将状态发送回“母程序”而无需每秒执行数百万次?

【问题讨论】:

    标签: python wxpython


    【解决方案1】:

    您可以从非 GUI 线程实例化自定义事件,然后将 wx.PostEvent 实例化回 GUI 线程。这是一个线程安全的操作。我的用例通常是这样工作的:

    • 启动工作线程 - 自定义事件“启动操作”
    • 开始处理
    • 发布事件返回更新进度“已解析第 435 行,共 15000 行”

    然后我绑定自定义事件以更新对话框或 textctrl/log 或其他任何内容。这非常容易做到。如果您愿意,我可以发布一些小测试用例的示例代码,这是我不久前在搞清楚这些东西时写的。

    --编辑:

    好的,这里有一些代码,首先是线程示例:

    #!usr/bin/env python
    
    import wx
    import threading
    import Queue
    import random
    import time
    
    TextEventType = wx.NewEventType()
    EVT_THREAD_TEXT_EVENT = wx.PyEventBinder(TextEventType, 1)
    
    global_queue = Queue.Queue()
    
    def threadStart(numthrds, queue, window):
        for i in range(numthrds):
            i = TextThread(queue, window)
    
    class TextThread(threading.Thread):
        def __init__(self, queue, output_window):
            threading.Thread.__init__(self)
            self.inqueue = queue
            self.output_window = output_window
            self.start()
    
    
        def run(self):
            word = self.inqueue.get()
            self.setName(word.upper())
            wait = random.randrange(1, 10)
            time.sleep(wait)
            msg = 'Thread: ' + self.getName() + '--wait= ' + str(wait) + ' ' + word
            evt = NewTextEvent(TextEventType, -1)
            evt.setText(msg)
            wx.PostEvent(self.output_window, evt) #post EVT_THREAD_TEXT_EVENT
            #self.inqueue.task_done() #may not need this if non-blocking
    
    
    
    class NewTextEvent(wx.PyCommandEvent):
        def __init__(self, evtType, id):
            wx.PyCommandEvent.__init__(self, evtType, id)
    
            self.msg = ''
    
        def setText(self, text):
            self.msg = text
    
        def getText(self):
            return self.msg
    
    class TextFrame(wx.Frame):
        def __init__(self, parent, id, *args, **kwargs):
            wx.Frame.__init__(self, parent, id, *args, **kwargs)
            self.queue = Queue.Queue()
            framesizer = wx.BoxSizer(wx.VERTICAL)
            self.panel = ThreadPanel(self, wx.ID_ANY)
            framesizer.Add(self.panel, 0, wx.EXPAND)
            self.SetSizerAndFit(framesizer)
    
            self.Bind(EVT_THREAD_TEXT_EVENT, self.OnThreadText)
    
        def OnThreadText(self, evt):
            msg = evt.getText()
            self.panel.out_tc.AppendText(msg + '\n')
    
    class ThreadPanel(wx.Panel):
        def __init__(self, parent, id, *args, **kwargs):
            wx.Panel.__init__(self, parent, *args, **kwargs)
            vsizer = wx.BoxSizer(wx.VERTICAL)
            self.wordtc = wx.TextCtrl(self, id=wx.ID_ANY, value='', size=(350, -1))
            self.inst_text = wx.StaticText(self, wx.ID_ANY,
                label='Enter a list of space-separated words')
            self.out_tc = wx.TextCtrl(self, id=wx.ID_ANY, size=(350, 300), 
                value='', style=wx.TE_MULTILINE)
            self.start_button = wx.Button(self, wx.ID_ANY, label='Start Threads')
    
            vsizer.Add(self.inst_text, 0, wx.ALIGN_LEFT)
            vsizer.Add(self.wordtc, 0, wx.EXPAND)
            vsizer.Add(self.start_button)
            vsizer.Add((100,100))
            vsizer.Add(self.out_tc, 0, wx.EXPAND)
            self.SetSizer(vsizer)
            self.Bind(wx.EVT_BUTTON, self.OnStartButton, self.start_button)
    
        def OnStartButton(self, evt):
            self.out_tc.Clear()
            text = self.wordtc.GetValue()
            self.wordtc.Clear()
            if not text.count(','):
                text = text.split(' ')
            num_thrds = len(text)
            for word in text:
                word = word.strip()
                self.GetParent().queue.put(word)
            threadStart(num_thrds, self.GetParent().queue, self.GetParent())
    
    
    
    
    if __name__ == "__main__":
        app = wx.App()
        frame = TextFrame(None, wx.ID_ANY, 'Thread test')
        frame.Show()    
        app.MainLoop()
    

    还有第二个更简单的自定义事件示例:

    #!usr/bin/env python
    
    import wx
    import random
    
    colorEventType = wx.NewEventType()
    EVT_COLOR_EVENT = wx.PyEventBinder(colorEventType, 1)
    
    class ButtonPanel(wx.Panel):
        def __init__(self, parent, *args, **kwargs):
            wx.Panel.__init__(self, parent, *args, **kwargs)
    
            vsizer = wx.BoxSizer(wx.VERTICAL)
            self.rstbutt = wx.Button(self, wx.ID_ANY, label='Restore')
            self.rstbutt.Disable()
            self.Bind(wx.EVT_BUTTON, self.OnButt, self.rstbutt)
            vsizer.Add(self.rstbutt, 0, wx.ALIGN_CENTER)
            vsizer.Add((500,150), 0)
            self.SetSizer(vsizer)
    
        def OnButt(self, evt):
            self.SetBackgroundColour(wx.NullColor)
            self.GetParent().Refresh()
            self.rstbutt.Disable()
    
    class ColorEvent(wx.PyCommandEvent):
        def __init__(self, evtType, id):
            wx.PyCommandEvent.__init__(self, evtType, id)
            self.color = None
    
        def SetMyColor(self, color):
            self.color = color
    
        def GetMyColor(self):
            return self.color
    
    class MainFrame(wx.Frame):
        def __init__(self, parent, *args, **kwargs):
            wx.Frame.__init__(self, parent, *args, **kwargs)
            framesizer = wx.BoxSizer(wx.VERTICAL)
            self.panel = ButtonPanel(self, wx.ID_ANY)
            framesizer.Add(self.panel, 1, wx.EXPAND)
    
            menubar = wx.MenuBar()
            filemenu = wx.Menu()
            menuquit = filemenu.Append(wx.ID_ANY, '&Quit')
            menubar.Append(filemenu, 'File')
            colormenu = wx.Menu()
            switch = colormenu.Append(wx.ID_ANY, '&Switch Color')
            menubar.Append(colormenu, '&Color')
            self.SetMenuBar(menubar)
    
            self.Bind(wx.EVT_MENU, self.OnQuit, menuquit)
            self.Bind(wx.EVT_MENU, self.OnColor, switch)
            self.Bind(EVT_COLOR_EVENT, self.ColorSwitch)
            self.SetSizerAndFit(framesizer)
    
        def OnQuit(self, evt):
            self.Close()
    
        def OnColor(self, evt):
            colevt = ColorEvent(colorEventType, -1) 
            colors = ['red', 'green', 'blue', 'white', 'black', 'pink', 
                (106, 90, 205), #slate blue
                (64, 224, 208), #turquoise
                ]
            choice = random.choice(colors)
            colevt.SetMyColor(choice)
            self.GetEventHandler().ProcessEvent(colevt)
            #evt.Skip()
    
        def ColorSwitch(self, evt):
            color = evt.GetMyColor()
            #print(color)
            self.panel.SetBackgroundColour(color)
            self.Refresh()
            self.panel.rstbutt.Enable()
    
    
    
    if __name__ == "__main__":
        app = wx.App()
        frame = MainFrame(None, wx.ID_ANY, title="Change Panel Color Custom Event")
        frame.Show(True)
    
        app.MainLoop()
    

    【讨论】:

    • 感谢您的回答。我明白了,但我还没有使用 PostEvent,示例代码会有很大帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 2021-11-12
    • 2013-09-20
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多