在wxPython中使用线程更新GUI面板标签

1 投票
2 回答
941 浏览
提问于 2025-04-17 20:48

级别:初学者

我正在使用 Python 2.7 和 wxPython 3.0,操作系统是 Windows 7。

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

问题:我想每隔 5 秒检查一次服务器,以获取最新的值,并相应地更新我的 GUI,也就是说,我需要更新面板上的 staticText,以显示更新后的值。我不想添加新的面板,只想更新旧面板中的值。

例如:如果我检查服务器,服务器返回 123 这些值,那么我想创建 3 个面板,分别显示 123。然后在 5 秒后再次检查服务器,如果服务器给出 456 这些值,我只想在旧面板上更新这些值。这意味着现在面板将显示 456,而不是 123。我看了一些关于使用线程的教程和帖子,也理解了一些基本概念。不幸的是,我不太明白如何将这个概念应用到我的问题上。如果能给我一个适用于我这个特定问题的示例,那就太好了,这样我可以将其应用到我的其他部分。

代码:我为这个特定问题创建了一个简短的示例代码。在 class labelsA 中的 getLabels()class labelsB 中的 getLabels() 模拟了服务器,只是生成一些随机值并将它们以列表的形式返回。然后,class labelA 中的 getLabels() 返回的值列表和 class labelsB 中的 getLabels() 返回的值列表分别被 createPanels()AcreatePanelsB() 用来创建面板并显示这些值。白色背景的面板是 panelA,黄色背景的面板是 panelB。如果有人能教我如何使用线程来更新这两个面板的值,而不让我的 GUI 冻结或阻塞,那就太好了。

下载:下面提供了示例代码,也可以通过 这里下载,以避免缩进问题。

#!/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()

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

2 个回答

1

为了说明,我知道这个问题早就有人回答过了,其实用一个简单的 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()
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()

撰写回答