我的量规的速度根据与它一起运行的线程的指示而变化。当说明很简单时,我的仪表速度很快,但是当需要读取文件时,我的仪表速度很慢。我想以同样的速度稳定下来。更糟的是,当您必须从图像文件中提取文本时,仪表会崩溃。并植入GUI
wx.Gauge(self, -1, 20, pos=(150, 300), size=(250, 25), style = wx.GA_HORIZONTAL)
我已经用更大的值(Range
)更改了now Range=20
,但什么也没有
此仪表与读取文件的线程一起运行。当您必须读取Txt文件时,仪表会快速运行,但是当您必须读取Docx时,仪表的速度会变慢,当您必须读取图像文本时仪表会导致GUI崩溃。我想避免GUI崩溃。如果可能,请稳定仪表的速度。
import time
import datetime
class Example(wx.Frame):
def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.gspeed = 20
self.timer = wx.Timer(self)
self.timer.Start(self.gspeed)
self.star = True
self.start_time = time.time()
self.InitUI()
def InitUI(self):
pnl = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
self.btn2 = wx.Button(pnl, wx.ID_STOP)
self.text = wx.StaticText(pnl)
self.count = wx.StaticText(pnl)
self.Bind(wx.EVT_BUTTON, self.OnStop, self.btn2)
self.gauge = wx.Gauge(pnl,20, size=(250, -1), style = wx.GA_HORIZONTAL)
hbox1.Add(self.gauge, proportion=1, flag=wx.ALIGN_CENTRE)
hbox2.Add(self.btn2, proportion=1)
hbox3.Add(self.text, proportion=1, flag=wx.RIGHT, border=50)
hbox3.Add(self.count, proportion=1)
vbox.Add((0, 30))
vbox.Add(hbox1, flag=wx.ALIGN_CENTRE)
vbox.Add((0, 20))
vbox.Add(hbox2, proportion=1, flag=wx.ALIGN_CENTRE)
vbox.Add(hbox3, proportion=1, flag=wx.ALIGN_CENTRE)
pnl.SetSizer(vbox)
self.SetTitle('Gauge')
self.Centre()
def OnStop(self, e):
self.timer.Stop()
self.text.SetLabel('Task Interrupted')
def OnTimer(self, e):
self.gauge.Pulse()
self.SetTimeLabel()
def get_elapsed_time(self):
val = round(time.time() - self.start_time, 1)
hours = val/3600
minutes = (val%3600)/60
seconds = val%60
strs = ("%lu:%02lu:%02lu")%(hours, minutes, seconds)
return strs
def SetTimeLabel(self):
self.text.SetLabel("{elapsed} seconds elapsed".format(elapsed=self.get_elapsed_time()))
def main():
app = wx.App()
ex = Example(None)
ex.Show()
app.MainLoop()
if __name__ == '__main__':
main()
您的示例没有包含任何显示您如何读取文件的内容,但是我将基于对问题的描述,假定它们是从主线程调用的。关于如何以及为什么需要从另一个线程运行阻止功能以保持UI响应的很多examples。如果从主线程运行一个函数,它将阻止事件循环,这意味着直到该函数完成之前,不会调用任何常规事件(例如EVT_TIMER)。这是您的示例,该示例在其自己的线程中模拟了长期运行的任务。
import time
import datetime
import wx
import threading
class Example(wx.Frame):
def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.gspeed = 20
self.timer = wx.Timer(self)
self.timer.Start(self.gspeed)
self.star = True
self.start_time = time.time()
self.thread = None
self.InitUI()
self.start_long_running_task()
def start_long_running_task(self):
"""
:return: starts the long running task in its own thread to keep the UI responsive
:rtype:
"""
self.thread = threading.Thread(target=self.long_running_task)
self.thread.start()
def long_running_task(self):
"""
:return: simulated long running task
:rtype:
"""
print("long running task has started")
# checks if the window has been closed and if the timer is still running
while bool(self) and self.timer.IsRunning():
# do something
time.sleep(1)
# the timer was stopped or the window was closed
print("long running task is exiting")
def InitUI(self):
pnl = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
self.btn2 = wx.Button(pnl, wx.ID_STOP)
self.text = wx.StaticText(pnl)
self.count = wx.StaticText(pnl)
self.Bind(wx.EVT_BUTTON, self.OnStop, self.btn2)
self.gauge = wx.Gauge(pnl, 20, size=(250, -1), style=wx.GA_HORIZONTAL)
hbox1.Add(self.gauge, proportion=1, flag=wx.ALIGN_CENTRE)
hbox2.Add(self.btn2, proportion=1)
hbox3.Add(self.text, proportion=1, flag=wx.RIGHT, border=50)
hbox3.Add(self.count, proportion=1)
vbox.Add((0, 30))
vbox.Add(hbox1, flag=wx.ALIGN_CENTRE)
vbox.Add((0, 20))
vbox.Add(hbox2, proportion=1, flag=wx.ALIGN_CENTRE)
vbox.Add(hbox3, proportion=1, flag=wx.ALIGN_CENTRE)
pnl.SetSizer(vbox)
self.SetTitle('Gauge')
self.Centre()
def OnStop(self, e):
self.timer.Stop()
self.text.SetLabel('Task Interrupted')
def OnTimer(self, e):
self.gauge.Pulse()
self.SetTimeLabel()
def get_elapsed_time(self):
val = round(time.time() - self.start_time, 1)
hours = val / 3600
minutes = (val % 3600) / 60
seconds = val % 60
strs = ("%lu:%02lu:%02lu") % (hours, minutes, seconds)
return strs
def SetTimeLabel(self):
self.text.SetLabel("{elapsed} seconds elapsed".format(elapsed=self.get_elapsed_time()))
def main():
app = wx.App()
ex = Example(None)
ex.Show()
app.MainLoop()
if __name__ == '__main__':
main()
这是一个基于我在评论中建议的代码的示例。“开始/停止”按钮将开始或停止文件加载。Other task
按钮只是打印textctrl框的内容,以证明程序没有被文件加载线程锁定。
import wx
import time
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
load_status=["File Loading","File Loaded","Cancelled"]
class Model(Thread):
def __init__(self,parent):
Thread.__init__(self)
self.stopthread = 0
self.target = parent
#self.start()
def run(self):
line_counter = 0
with open('../xxx.html', 'r') as f:
while not self.stopthread:
line = f.readline()
if not line:
break
line_counter += 1
print(line_counter)
if self.stopthread:
break
time.sleep(0.05)
evt = progress_event(count=line_counter, status=self.stopthread)
wx.PostEvent(self.target, evt)
if self.stopthread == 0:
self.stopthread = 1
evt = progress_event(count=line_counter, status=self.stopthread)
wx.PostEvent(self.target, evt)
def terminate(self):
self.stopthread = 2
class View(wx.Frame):
def __init__(self, parent, title):
super(View, self).__init__(parent, title=title, size=(400, 400))
self.InitUI()
def InitUI(self):
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.fgs = wx.FlexGridSizer(6, 2, 10, 25)
id = wx.StaticText(self, label="ID:")
firstName = wx.StaticText(self, label="First name:")
lastName = wx.StaticText(self, label="Last name:")
self.id = wx.TextCtrl(self)
self.firstName = wx.TextCtrl(self)
self.lastName = wx.TextCtrl(self)
self.stop = wx.Button(self, -1, "Start")
self.other = wx.Button(self, -1, "Other task")
self.fgs.AddMany([id, (self.id, 1, wx.EXPAND),
firstName, (self.firstName, 1, wx.EXPAND),
lastName, (self.lastName, 1, wx.EXPAND),
(self.stop,1,wx.EXPAND),
(self.other,1,wx.EXPAND)])
self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL | wx.EXPAND,border=15)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Stop button
self.stop.Bind(wx.EVT_BUTTON, self.OnStartStop)
#Bind to Other task button
self.other.Bind(wx.EVT_BUTTON, self.OnOther)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.SetSizer(self.vbox)
self.Layout()
self.statusbar = self.CreateStatusBar(2)
self.text = wx.StaticText(self.statusbar,-1,("No File loaded"))
self.progress = wx.Gauge(self.statusbar, range=20)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.text, 0, wx.ALIGN_TOP|wx.ALL, 5)
sizer.Add(self.progress, 1, wx.ALIGN_TOP|wx.ALL, 5)
self.statusbar.SetSizer(sizer)
#wx.BeginBusyCursor()
self.loadthread = Model(self)
def OnProgress(self, event):
self.text.SetLabel(load_status[event.status])
#self.progress.SetValue(event.count)
#or for indeterminate progress
self.progress.Pulse()
if event.status != 0:
wx.EndBusyCursor()
self.Update()
time.sleep(1)
#self.statusbar.Hide()
#Re-set thread in case it needs to be restarted
self.loadthread = Model(self)
self.stop.SetLabel("Start")
self.progress.SetValue(0)
self.text.SetLabel("")
def OnStartStop(self, event):
if self.loadthread.isAlive():
self.loadthread.terminate() # Shutdown the thread
self.loadthread.join() # Wait for it to finish
#Re-set thread in case it needs to be restarted
self.loadthread = Model(self)
self.stop.SetLabel("Start")
self.progress.SetValue(0)
self.text.SetLabel("")
else:
wx.BeginBusyCursor()
self.loadthread.start()
self.stop.SetLabel("Stop")
def OnExit(self, event):
if self.loadthread.isAlive():
self.loadthread.terminate() # Shutdown the thread
self.loadthread.join() # Wait for it to finish
self.Destroy()
def OnOther(self, event):
print("Other Task")
print(self.id.GetValue())
print(self.firstName.GetValue())
print(self.lastName.GetValue())
class Controller:
def __init__(self):
self.view = View(None, title='Test')
self.view.Show()
def main():
app = wx.App()
controller = Controller()
app.MainLoop()
if __name__ == '__main__':
main()