如何稳定我的 wx.Gauge 速度

how to stabilize the speed of my wx.Gauge

我的仪表速度根据线程的说明而变化,即 运行 它。当指令很简单时,我的仪表运行得非常快,但是当需要读取文件时,我的仪表非常慢。我想以同样的速度稳定它。 更糟糕的是,当您必须从图像文件中提取文本时,仪表会崩溃。并植入 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 的内容。如果您 运行 来自主线程的函数,它会阻塞事件循环,这意味着 none 的正常事件(如 EVT_TIMER)会被调用,直到函数完成。这是你的例子,在它自己的线程中有一个模拟的长 运行ning 任务。

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()

这是一个基于我在评论中推荐的代码的示例。
Start/Stop 按钮将开始或停止文件加载。
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()