在wxPython中使用线程向GUI添加面板
级别:初学者
我已经用wxPython工作了一个月。我的图形界面应用几乎完成了,但在处理线程时遇到了困难。我使用的是python v2.7和wxPython v3.0,操作系统是Windows 7。
我的图形界面应用:在我的图形界面应用中,我从服务器读取一些值,然后根据这些值的数量在我的界面中创建面板。每个面板会以staticText
的形式显示这些值。例如:如果我从服务器收到1,2,3这几个值,那么我会创建3个面板,分别显示1、2和3。到这里一切都正常。
问题:我想每5秒检查一次服务器,以获取新的值,并相应地更新我的图形界面,也就是说,我需要添加新的面板来显示新的值。我看了一些关于使用线程的教程和帖子,了解了一些基本知识。但不幸的是,我不知道如何将这个概念应用到我的问题上。我觉得我需要把检查服务器值的代码放在一个线程循环中。但我不明白还需要做些什么。我想我可能需要改变我的代码逻辑(创建面板的部分)来使用线程。如果能给我一个适用于我这个特定问题的示例,那就太好了,这样我可以把它应用到我的其他部分应用中。
代码:我为这个特定问题创建了一个简短的示例代码。在class labels
中的getLabels()
模拟了服务器,只是生成一些随机值并以列表的形式返回。然后这些值被createPanels()
用来创建面板并显示这些值。如果有人能教我如何使用线程来添加新的面板到我的图形界面,而不让我的界面卡住,那就太好了。
下载:完整的代码可以在下面找到,也可以通过这里下载,以避免任何缩进问题。
#!/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()
如果在执行代码后没有看到任何面板,请重新执行代码。
感谢您的时间。
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() 来更新它们。
希望这段代码能给你一些启发。基本上:把和服务器的接口放在一个线程里,然后用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()