【问题标题】:Updating the labels of the panels in a GUI using threading in wxPython使用 wxPython 中的线程更新 GUI 中面板的标签
【发布时间】:2014-04-06 06:40:09
【问题描述】:

级别:初学者

我使用的是 python v2.7 和 wxPython v3.0,操作系统是 windows 7。

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

问题:我想每 5 秒检查一次服务器以获取值并相应地更新我的 GUI,即。我必须更新面板上的staticText 以显示更新后的值。我不想添加新面板我只想更新旧面板中的值。

例如: 如果我检查服务器并且服务器返回 123 为值然后我想创建 3 面板分别显示 123 值。然后 5 秒后,当我再次检查服务器时,如果服务器给出 456 作为值,那么我只想更新这些值在旧面板上。这意味着现在面板将显示 456 而不是 12强>、3。我在 SC 上阅读了一些关于使用线程的教程和帖子,我也了解了一些基本事实。不幸的是,我不明白如何将这个概念应用于我的问题。 如果我能为我的这个特定问题找到一个工作示例,这样我就可以将其应用于我的其余应用程序,那将是非常棒的。

代码:我为这个特定问题创建了一个简短的示例代码。 class labelsA 中的 getLabels()labelsB 类中的 getLabels() 通过生成一些随机值并将它们返回到列表中来模拟服务器。然后class labelAgetLabels() 返回的值列表和class labelsBgetLabels() 返回的值列表分别被createPanels()AcreatePanelsB() 使用来创建面板并显示这些值。白色背景的面板是panelA,黄色背景的面板是panelB。如果有人能教我如何使用线程来更新这两个面板的值而不冻结/阻塞我的 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.HORIZONTAL)
        panelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.sizerA = sizerA = wx.BoxSizer(wx.VERTICAL)
        self.panelA = panelA = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panelA.SetupScrolling()
        panelA.SetBackgroundColour('#FFFFFF')
        self.sizerB = sizerB = wx.BoxSizer(wx.VERTICAL)
        self.panelB = panelB = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panelB.SetupScrolling()
        panelB.SetBackgroundColour('#FFF000')
        panelA.SetSizer(sizerA)
        panelB.SetSizer(sizerB)
        mainSizer.Add(panelA, 15, wx.EXPAND|wx.ALL)
        mainSizer.Add(panelB, 15, wx.EXPAND|wx.ALL)
        self.SetSizer(mainSizer)
        self.createPanelsA()
        self.createPanelsB()

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

    def createPanelsB(self):
        k = 0
        labelObj = labelsB()
        locations = labelObj.getLabel()
        print locations
        for i in locations:
            sPanels = 'sPanel'+str(k)
            sPanels = wx.Panel(self.panelB)
            label = str(k+1)
            text = wx.StaticText(sPanels, -1, label)
            text.SetFont(self.locationFont)
            text.SetForegroundColour('#0101DF')
            self.sizerB.Add(sPanels, 0, wx.ALL, 5)
            self.sizerB.Add(wx.StaticLine(self.panelB), 0, wx.ALL|wx.EXPAND, 0)
            k += 1
################################################
class labelsA():
    def getLabel(self):
        mylist =[]
        i = randrange(10)
        for k in range(1,i+1):
            mylist.append(k)
        return mylist
###############################################
class labelsB():
    def getLabel(self):
        mylist =[]
        i = 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()
    app.MainLoop()

非常感谢您的宝贵时间。任何帮助将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    这是一个创建线程以每 5 秒从某处获取信息的演示,还展示了如何根据需要创建静态文本,以及如何更新它们。

    #!/usr/bin/env python
    
    from random import randrange
    import wx
    import wx.lib.scrolledpanel
    
    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)
    
            self.text_labels = []  # Stores the labels where server data is displayed
    
            pub.subscribe(self.OnNewLabels, "NEW_LABELS")
    
    
        def OnNewLabels(self, labels):
            locations = labels
            print locations
            if len(self.text_labels) < len(labels):
                new_labels_needed = len(labels) - len(self.text_labels) 
                label = "(no data)"
                for i in range(new_labels_needed):
                    sPanels = wx.Panel(self.panel)
                    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)
                    self.text_labels.append(text)
                self.sizer.Layout()            
            k = 0
            for label in locations:
                self.text_labels[k].SetLabel(str(label))
                k=k+1
    
    
    ###############################
    #
    #
    
    def InterfaceThread():
        while True:
            # get the info from the server
            mylist =[]
            i = randrange(10)
            for k in range(1,i+1):
                mylist.append(randrange(10))
    
            # Tell the GUI about them
            wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = mylist)
            time.sleep(5)
    
    
    class ServerInterface():
    
        def __init__(self):
            interface_thread = threading.Thread(target = InterfaceThread, args = ()) 
            interface_thread.start()
    
    
    
    #############
    #
    
    if __name__=='__main__':
        app = wx.App()
        frame = GUI(parent=None, id=-1, title="Test")
        frame.Show()
        server_interface = ServerInterface()
        app.MainLoop()
    

    【讨论】:

    • 再次感谢您的帮助。现在只有一个问题。例如:如果服务器发送 [1,2,3] 则创建 3 个面板显示 1、2 和 3。然后如果下次服务器发送 [3,4] 则前 2 个面板显示 3,4 但现在有旧面板也显示 3。它没有被删除。
    • 这是您在 OnNewLabels 中做出的简单选择。当数据少于上次时,我无法猜测您想对旧数据做什么。我的示例显示了如何检查是否需要更多面板。很明显,相同类型的检查将显示如何检查是否太少。在执行for label in locations 循环后,检查剩余面板是否多于位置如果有,那么也许您应该简单地将剩余面板设置为“无数据”或仅“”。 for k in range(len(locations), len(self.text_labels)): self.text_labels[k].SetLabel("") 之类的。
    • 是的,我也有同样的想法,但如果它不是一个好的实现,我会感到困惑。谢谢。在我的问题中还有一件事要更新两个不同的面板,因此您的代码演示了如何更新一个面板。那么对于第二个面板,我是否需要创建单独的 serverInterface() 和 InterfaceThread() 类等等?
    • 服务器接口/接口线程的数量将取决于您收集数据所需的不同频率的数量。如果您每 5 秒从两台服务器收集一次,则可以在一个线程中同时执行这两项操作,这与需要更新多少面板是分开的。来自每个服务器的消息将以不同的“消息类型”发送。在我的代码中,我有一个消息类型“NEW_LABELS”,此消息导致 OnNewLabels 重绘一个面板。您可以发送不同的消息类型 -​​ 比如“SERVER1_LABELS”和“SERVER2_LABELS”,并为每个调用不同的绘图例程。
    • 好的,非常感谢。我将尝试使用代码。如果我有任何问题,我会问一个新问题。我期待着您的合作。 :)
    【解决方案2】:

    作为记录,我知道这个问题很久以前就已经回答过了,用一个简单的 wx.Timer 就可以达到同样的结果。
    使用上面提供的代码:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import wx
    import wx.lib.scrolledpanel
    from random import randrange
    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)
            self.text_labels = []  # Stores the labels where server data is displayed
            self.timer = wx.Timer(self)
            self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
            self.timer.Start(5000)
    
        def OnNewLabels(self, labels):
            locations = labels
            print locations
            if len(self.text_labels) < len(labels):
                new_labels_needed = len(labels) - len(self.text_labels) 
                label = "(no data)"
                for i in range(new_labels_needed):
                    sPanels = wx.Panel(self.panel)
                    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)
                    self.text_labels.append(text)
                self.sizer.Layout()
            k = 0
            for label in locations:
                self.text_labels[k].SetLabel(str(label))
                k=k+1
            if len(self.text_labels) > len(labels):
                labels_not_needed = len(self.text_labels) - len(labels)
                for i in range(labels_not_needed):
                    self.text_labels[k].SetLabel("-")
                    k+=1
    
        def OnTimer(self, evt):
            # get the info from the server
            mylist =[]
            i = randrange(10)
            for k in range(1,i+1):
                mylist.append(randrange(10))
            self.OnNewLabels(mylist)
    
    if __name__=='__main__':
        app = wx.App()
        frame = GUI(parent=None, id=-1, title="Test")
        frame.Show()
        app.MainLoop()
    

    【讨论】:

    • 感谢您的努力! :)
    猜你喜欢
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 2021-11-10
    • 2018-07-03
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 2023-04-04
    相关资源
    最近更新 更多