【问题标题】:Adding panels to GUI using threading in wxPython使用 wxPython 中的线程将面板添加到 GUI
【发布时间】:2014-03-03 11:43:16
【问题描述】:

级别:初学者

我已经使用 wxPython 一个月了。我几乎完全使用了我的 GUI 应用程序,但是在线程方面我被卡住了。我正在使用 python v2.7 和 wxPython v3.0,操作系统是 windows 7。

我的 GUI 应用程序:在我的 GUI 应用程序中,我从服务器读取一些值,然后根据这些值的数量在我的 GUI 中创建面板。然后这些面板中的每一个都将以staticText 的形式表示值。例如:如果我从服务器接收 1,2,3 值,那么我创建 3 面板,每个面板显示 1、2 和 3 分别。到这里一切正常。

问题:我想每 5 秒检查一次服务器以获取值并相应地更新我的 GUI,即。我必须添加新面板来显示新值。我在 SC 上阅读了一些关于使用线程的教程和帖子,我也了解了一些基本事实。不幸的是,我不明白如何将这个概念应用于我的问题。我想我必须将检查来自服务器的值的代码放在线程循环中。但我不明白还能做什么。我想我可能必须更改代码的逻辑(创建面板部分)才能使用线程。 如果我能为我的这个特定问题找到一个工作示例,这样我就可以将其应用于我的其余应用程序,那将是非常棒的。

代码:我为这个特定问题创建了一个简短的示例代码。 class labels 中的 getLabels() 通过生成一些随机值并将它们返回到列表中来模仿服务器。然后createPanels() 使用这些值来创建面板并显示这些值。如果有人能教我如何使用线程将新面板添加到我的 GUI 而不会冻结/阻塞我的 GUI,那就太好了。

下载:整个代码可以在下面找到,也可以是downloaded from here以避免任何识别问题。

#!/usr/bin/env python

from random import randrange
import wx
import wx.lib.scrolledpanel

class GUI(wx.Frame):

    def __init__(self, parent, id, title):
        screenWidth = 800
        screenHeight = 450
        screenSize = (screenWidth, screenHeight)
        wx.Frame.__init__(self, None, id, title, size=screenSize)
        self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panel.SetupScrolling()
        panel.SetBackgroundColour('#FFFFFF')
        self.createPanels()
        panel.SetSizer(sizer)
        mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
        self.SetSizer(mainSizer)

    def createPanels(self):
        k = 0
        labelObj = labels()
        locations = labelObj.getLabel()
        print locations
        for i in locations:
            sPanels = 'sPanel'+str(k)
            sPanels = wx.Panel(self.panel)
            label = str(k+1)
            text = wx.StaticText(sPanels, -1, label)
            text.SetFont(self.locationFont)
            text.SetForegroundColour('#0101DF')
            self.sizer.Add(sPanels, 0, wx.ALL, 5)
            self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
            k += 1

################################################
class labels():
    def getLabel(self):
    mylist =[]
    i = randrange(10)
    for k in range(1,i+1):
        list.append(k)
    return mylist
###############################################

if __name__=='__main__':
app = wx.App()
frame = GUI(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()

如果执行代码后没有看到任何面板,请重新执行代码。

感谢您的宝贵时间。

【问题讨论】:

    标签: python multithreading python-2.7 user-interface wxpython


    【解决方案1】:

    希望这段代码能包含一些答案。基本上:将与服务器的接口放在一个线程中,并使用 wx.CallAfter 与 wx 中的组件对话。使用 pubsub 在 GUI 中的元素之间进行对话。该代码创建了一个每 5 秒执行一次操作的线程,它通过直接方法调用对您有句柄的 wx 元素进行通信,并通过 pubsub 对您没有句柄的元素进行通信。

    #!/usr/bin/env python
    
    from random import randrange
    import wx
    import wx.lib.scrolledpanel
    
    # imports added by GreenAsJade
    import time
    import threading
    from wx.lib.pubsub import setupkwargs
    from wx.lib.pubsub import pub
    
    class GUI(wx.Frame):
    
        def __init__(self, parent, id, title):
            screenWidth = 800
            screenHeight = 450
            screenSize = (screenWidth, screenHeight)
            wx.Frame.__init__(self, None, id, title, size=screenSize)
            self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
            mainSizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
            self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
            panel.SetupScrolling()
            panel.SetBackgroundColour('#FFFFFF')
            panel.SetSizer(sizer)
            mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
            self.SetSizer(mainSizer)
    
            pub.subscribe(self.OnNewLabels, "NEW_LABELS")
    
    
        def OnNewLabels(self, labels):
            k = 0
            locations = labels
            print locations
            for i in locations:
                sPanels = 'sPanel'+str(k)
                sPanels = wx.Panel(self.panel)
                label = str(k+1)
                print "doing", label
                text = wx.StaticText(sPanels, -1, label)
                text.SetFont(self.locationFont)
                text.SetForegroundColour('#0101DF')
                self.sizer.Add(sPanels, 0, wx.ALL, 5)
                self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
                k += 1
            self.sizer.Layout()
    
    
    ###############################
    #
    #
    
    def InterfaceThread(id, log):
        label_generator = Labels()
        while True:
            labels = label_generator.getLabel()   # get the info from the server
            # Tell the GUI about them
            wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = labels)
            # Tell the logger about them
            wx.CallAfter(log.AppendText, "Sent %s \n" % str(labels))
            time.sleep(5)
    
    
    class ServerInterface(wx.Frame):
    
        def __init__(self, parent, id, title):
            wx.Frame.__init__(self, None, id, title)
            self.log = wx.TextCtrl(self, style = wx.TE_MULTILINE)
            interface_thread = threading.Thread(target = InterfaceThread, args = (1, self.log)) 
            interface_thread.start()
    
    
    
    ################################################
    class Labels():
        def getLabel(self):
            mylist =[]
            i = 4 #randrange(10)
            for k in range(1,i+1):
                mylist.append(k)
            return mylist
    ###############################################
    
    if __name__=='__main__':
        app = wx.App()
        frame = GUI(parent=None, id=-1, title="Test")
        frame.Show()
        server_interface = ServerInterface(parent = None, id=-1, title="Server Log")
        server_interface.Show()
        app.MainLoop()
    

    【讨论】:

    • 注意:ServerInterface 不必是图形元素。它可能只是一个普通的类——我把它做成一个图形对象只是为了展示你如何实现它的图形界面,以及从线程(你有一个句柄)到它的通信。
    • 首先感谢您考虑我的问题。我执行了你的代码。大部分零件都按我的意愿工作。问题是现在当服务器发送值时,它正在主面板上创建新面板。我的问题是,当服务器发送值时,这些值应该被替换/更新旧值。它不应该添加具有新值的新面板。如果您只使用我的代码并尽可能简单地回答,那就太好了。您也可以删除服务器 GUI。
    • 我想我已经回答了你的问题,即“如何设置线程服务器接口”。如果您有其他问题,您应该接受我的回答,并提出一个单独的问题。听起来您想问“如何用新面板替换面板?”。或者您可能希望代码更新现有面板中的值?不管你想要什么,确定问题,然后问它:)
    • 请再次阅读我的问题。不幸的是,您还没有回答我的实际问题。我首先不需要服务器接口。这就是我通过 labels() 类模仿服务器行为的原因。我的问题是如何在不冻结我的 GUI 的情况下从服务器获取这些值后更新面板上的静态文本的值。如果您仍然认为我的问题令人困惑,那么我可以再次发布有关更新面板上的 staticTexts 值的问题。
    • 正如你所说 - 你的问题是如何在不冻结 GUI 的情况下更新面板。正如您所说:要做到这一点,您需要线程中的服务器接口。我已经向您展示了如何做到这一点:如何将休眠 5 秒的代码与操作 GUI 的代码放在一个单独的线程中。上面的代码就是这个问题的答案。您对文本控件执行的操作是一个单独的问题。如果您想更改现有 StaticText 控件中的文本,请先创建它们(或者当您发现需要更多时)并在 OnNewLabels 中对它们使用 SetLabel()
    【解决方案2】:

    后续:为清楚起见,这里是 SeverInterface 不是图形元素的版本。

    #!/usr/bin/env python
    
    from random import randrange
    import wx
    import wx.lib.scrolledpanel
    
    # imports added by GreenAsJade
    import time
    import threading
    from wx.lib.pubsub import setupkwargs
    from wx.lib.pubsub import pub
    
    class GUI(wx.Frame):
    
        def __init__(self, parent, id, title):
            screenWidth = 800
            screenHeight = 450
            screenSize = (screenWidth, screenHeight)
            wx.Frame.__init__(self, None, id, title, size=screenSize)
            self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
            mainSizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
            self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
            panel.SetupScrolling()
            panel.SetBackgroundColour('#FFFFFF')
            panel.SetSizer(sizer)
            mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
            self.SetSizer(mainSizer)
    
            pub.subscribe(self.OnNewLabels, "NEW_LABELS")
    
    
        def OnNewLabels(self, labels):
            k = 0
            locations = labels
            print locations
            for i in locations:
                sPanels = 'sPanel'+str(k)
                sPanels = wx.Panel(self.panel)
                label = str(k+1)
                print "doing", label
                text = wx.StaticText(sPanels, -1, label)
                text.SetFont(self.locationFont)
                text.SetForegroundColour('#0101DF')
                self.sizer.Add(sPanels, 0, wx.ALL, 5)
                self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
                k += 1
            self.sizer.Layout()
    
    
    ###############################
    #
    #
    
    def InterfaceThread():
        label_generator = Labels()
        while True:
            labels = label_generator.getLabel()   # get the info from the server
            # Tell the GUI about them
            wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = labels)
            time.sleep(5)
    
    
    class ServerInterface():
    
        def __init__(self):
            interface_thread = threading.Thread(target = InterfaceThread, args = ()) 
            interface_thread.start()
    
    
    
    ################################################
    class Labels():
        def getLabel(self):
            mylist =[]
            i = 4 #randrange(10)
            for k in range(1,i+1):
                mylist.append(k)
            return mylist
    ###############################################
    
    if __name__=='__main__':
        app = wx.App()
        frame = GUI(parent=None, id=-1, title="Test")
        frame.Show()
        server_interface = ServerInterface()
        app.MainLoop()
    

    正如我在另一个答案中提到的那样 - 如果您想更新 StaticTexts,则在创建它们时将它们放入一个数组中,并在 OnNewLabels 中迭代该数组并使用 SetLabel() 更新它们。

    【讨论】:

    • 感谢您的努力,我会在接受您的解决方案之前编辑我的问题,因为这不是我正在寻找的实际解决方案。我很快就会发布我的新问题,如果你也能帮助我,我将不胜感激。 :)
    猜你喜欢
    • 2014-04-06
    • 1970-01-01
    • 2018-07-03
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多