如何使用 wxPython Phoenix 正确处理对 wx.App() 的多次调用
How to Properly Handle Multiple Calls to wx.App() using wxPython Phoenix
我正在尝试 troubleshoot/de-bug 我在使用 wxPython 4.0.7 的应用程序中遇到的问题。
我在 Windows 7 32 位系统上重写了我的整个程序,该程序在 Python 2.7 和 wxPython 2.8 上运行,现在可以在 64 位系统上运行 Python 3.7.4 和 wxPython 4.0.7 在 64 位 Windows 10 系统上。
我遇到的问题是我的程序要求它根据用户指定的循环次数迭代多次,并且它从两个不同的 python 调用 wx.App()
的实例使用的脚本。
我读到调用 wx.App()
的多个实例是 "no-no"(参见 )
很明显,这是这个版本的 wxPython 的一个问题,因为我的应用程序现在在第一次迭代后崩溃,而之前它运行良好。
好的,所以我现在明白了,但是我不确定 "fix" 是针对我的特定问题的。
我的应用程序的基本轮廓是这样的:
启动了一个 "runner.py" 脚本,其中包含主要的 wx.frame()
gui 并且以下代码附加到脚本的末尾:
app = wx.App()
frame = Runner(parent=None, foo=Foo)
frame.Show()
app.MainLoop()
当用户单击 wxPython GUI 中的 "execute" 按钮时,我有一个使用以下代码启动的进度对话框:
pd = wx.ProgressDialog(title = "Runner.py", message= "Starting Model", parent=self, style=wx.PD_AUTO_HIDE | wx.PD_SMOOTH | wx.PD_CAN_ABORT )
pd.Update(15)
runner.py 脚本执行一个 "for loop" 做一堆事情(实际上是从 R 脚本读取一些输入)然后一旦完成,它会打开第二个 python 脚本 ("looping.py") 并根据用户在从 runner.py 启动的 GUI 中指定的循环数迭代一组进程。
由于用户需要直观地查看模型 运行 正在经历的循环过程,我在第二个 "looping.py" 脚本中有另一个 wx.App()
调用的实例另一个 wx.ProgressDialog()
,脚本如下所示:
#Progress Bar to user to start model
app = wx.App()
pd = wx.ProgressDialog("looping.py", "Setup Iteration", parent=None, style=wx.PD_AUTO_HIDE | wx.PD_SMOOTH | wx.PD_CAN_ABORT )
pd.Update(15)
我的具体问题是:如何在 "looping.py" 脚本中成功启动 wx.ProgressDialog()
而不会在第一次迭代后使我的应用程序崩溃?
你可能要分class wx.ProgressDialog
,按理说自己写进度条显示可能更容易
像这样的东西,可能会给你一些想法。
我已经包括 运行 多个线程做不同事情的能力,使用 pause
和 stop
按钮。主框架有一个按钮来测试 Gui 是否仍然处于活动状态,同时 运行 正在运行线程。
来自线程的更新由 event
驱动
您可能希望减少或增加其选项。
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(400,400))
panel = MyPanel(self)
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.parent=parent
self.btn_start = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(180,30), pos=(10,10))
btn_test = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(180,30), pos=(10,50))
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(300,100))
self.btn_start.Bind(wx.EVT_BUTTON, self.Start_Process)
btn_test.Bind(wx.EVT_BUTTON, self.ActiveText)
def Start_Process(self, event):
process1 = ProcessingFrame(title='Threaded Task 1', parent=self, job_no=1)
process2 = ProcessingFrame(title='Threaded Task 2', parent=self, job_no=2)
self.btn_start.Enable(False)
def ActiveText(self,event):
self.text_count += 1
txt = "Gui is still active " + str(self.text_count)+"\n"
self.txt.write(txt)
class ProcessingFrame(wx.Frame):
def __init__(self, title, parent=None,job_no=1):
wx.Frame.__init__(self, parent=parent, title=title, size=(400,400))
panel = wx.Panel(self)
self.parent = parent
self.job_no = job_no
self.btn = wx.Button(panel,label='Stop processing', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.btn_pause = wx.Button(panel,label='Pause processing', size=(200,30), pos=(10,50))
self.btn_pause.Bind(wx.EVT_BUTTON, self.OnPause)
self.progress = wx.Gauge(panel,size=(200,10), pos=(10,90), range=60)
self.process = wx.TextCtrl(panel,size = (200,250), pos=(10,120), style = wx.TE_MULTILINE)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self)
def OnProgress(self, event):
self.progress.SetValue(event.count)
self.process.write(event.process+"\n")
self.Refresh()
if event.count >= 60:
self.OnExit(None)
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.parent.btn_start.Enable(True)
self.Destroy()
def OnPause(self, event):
if self.mythread.isAlive():
self.mythread.pause() # Pause the thread
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.target = parent_target
self.stopthread = False
self.process = 1 # Testing only - mock process id
self.start() # start the thread
def run(self):
# A selectable test loop that will run for 60 loops then terminate
if self.target.job_no == 1:
self.run1()
else:
self.run2()
def run1(self):
curr_loop = 0
while self.stopthread != True:
if self.stopthread == "Pause":
time.sleep(1)
continue
curr_loop += 1
self.process += 10 # Testing only - mock process id
if curr_loop <= 60: # Update progress bar
time.sleep(1.0)
evt = progress_event(count=curr_loop,process="Envoking process "+str(self.process))
#Send back current count for the progress bar
try:
wx.PostEvent(self.target, evt)
except: # The parent frame has probably been destroyed
self.terminate()
self.terminate()
def run2(self):
curr_loop = 0
while self.stopthread != True:
if self.stopthread == "Pause":
time.sleep(1)
continue
curr_loop += 1
self.process += 100 # Testing only - mock process id
if curr_loop <= 60: # Update progress bar
time.sleep(1.0)
evt = progress_event(count=curr_loop,process="Checking process"+str(self.process))
#Send back current count for the progress bar
try:
wx.PostEvent(self.target, evt)
except: # The parent frame has probably been destroyed
self.terminate()
self.terminate()
def terminate(self):
self.stopthread = True
def pause(self):
if self.stopthread == "Pause":
self.stopthread = False
self.target.btn_pause.SetLabel('Pause processing')
else:
self.stopthread = "Pause"
self.target.btn_pause.SetLabel('Continue processing')
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
我正在尝试 troubleshoot/de-bug 我在使用 wxPython 4.0.7 的应用程序中遇到的问题。
我在 Windows 7 32 位系统上重写了我的整个程序,该程序在 Python 2.7 和 wxPython 2.8 上运行,现在可以在 64 位系统上运行 Python 3.7.4 和 wxPython 4.0.7 在 64 位 Windows 10 系统上。
我遇到的问题是我的程序要求它根据用户指定的循环次数迭代多次,并且它从两个不同的 python 调用 wx.App()
的实例使用的脚本。
我读到调用 wx.App()
的多个实例是 "no-no"(参见
很明显,这是这个版本的 wxPython 的一个问题,因为我的应用程序现在在第一次迭代后崩溃,而之前它运行良好。
好的,所以我现在明白了,但是我不确定 "fix" 是针对我的特定问题的。
我的应用程序的基本轮廓是这样的:
启动了一个 "runner.py" 脚本,其中包含主要的 wx.frame()
gui 并且以下代码附加到脚本的末尾:
app = wx.App()
frame = Runner(parent=None, foo=Foo)
frame.Show()
app.MainLoop()
当用户单击 wxPython GUI 中的 "execute" 按钮时,我有一个使用以下代码启动的进度对话框:
pd = wx.ProgressDialog(title = "Runner.py", message= "Starting Model", parent=self, style=wx.PD_AUTO_HIDE | wx.PD_SMOOTH | wx.PD_CAN_ABORT )
pd.Update(15)
runner.py 脚本执行一个 "for loop" 做一堆事情(实际上是从 R 脚本读取一些输入)然后一旦完成,它会打开第二个 python 脚本 ("looping.py") 并根据用户在从 runner.py 启动的 GUI 中指定的循环数迭代一组进程。
由于用户需要直观地查看模型 运行 正在经历的循环过程,我在第二个 "looping.py" 脚本中有另一个 wx.App()
调用的实例另一个 wx.ProgressDialog()
,脚本如下所示:
#Progress Bar to user to start model
app = wx.App()
pd = wx.ProgressDialog("looping.py", "Setup Iteration", parent=None, style=wx.PD_AUTO_HIDE | wx.PD_SMOOTH | wx.PD_CAN_ABORT )
pd.Update(15)
我的具体问题是:如何在 "looping.py" 脚本中成功启动 wx.ProgressDialog()
而不会在第一次迭代后使我的应用程序崩溃?
你可能要分class wx.ProgressDialog
,按理说自己写进度条显示可能更容易
像这样的东西,可能会给你一些想法。
我已经包括 运行 多个线程做不同事情的能力,使用 pause
和 stop
按钮。主框架有一个按钮来测试 Gui 是否仍然处于活动状态,同时 运行 正在运行线程。
来自线程的更新由 event
驱动
您可能希望减少或增加其选项。
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(400,400))
panel = MyPanel(self)
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.parent=parent
self.btn_start = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(180,30), pos=(10,10))
btn_test = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(180,30), pos=(10,50))
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(300,100))
self.btn_start.Bind(wx.EVT_BUTTON, self.Start_Process)
btn_test.Bind(wx.EVT_BUTTON, self.ActiveText)
def Start_Process(self, event):
process1 = ProcessingFrame(title='Threaded Task 1', parent=self, job_no=1)
process2 = ProcessingFrame(title='Threaded Task 2', parent=self, job_no=2)
self.btn_start.Enable(False)
def ActiveText(self,event):
self.text_count += 1
txt = "Gui is still active " + str(self.text_count)+"\n"
self.txt.write(txt)
class ProcessingFrame(wx.Frame):
def __init__(self, title, parent=None,job_no=1):
wx.Frame.__init__(self, parent=parent, title=title, size=(400,400))
panel = wx.Panel(self)
self.parent = parent
self.job_no = job_no
self.btn = wx.Button(panel,label='Stop processing', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.btn_pause = wx.Button(panel,label='Pause processing', size=(200,30), pos=(10,50))
self.btn_pause.Bind(wx.EVT_BUTTON, self.OnPause)
self.progress = wx.Gauge(panel,size=(200,10), pos=(10,90), range=60)
self.process = wx.TextCtrl(panel,size = (200,250), pos=(10,120), style = wx.TE_MULTILINE)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self)
def OnProgress(self, event):
self.progress.SetValue(event.count)
self.process.write(event.process+"\n")
self.Refresh()
if event.count >= 60:
self.OnExit(None)
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.parent.btn_start.Enable(True)
self.Destroy()
def OnPause(self, event):
if self.mythread.isAlive():
self.mythread.pause() # Pause the thread
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.target = parent_target
self.stopthread = False
self.process = 1 # Testing only - mock process id
self.start() # start the thread
def run(self):
# A selectable test loop that will run for 60 loops then terminate
if self.target.job_no == 1:
self.run1()
else:
self.run2()
def run1(self):
curr_loop = 0
while self.stopthread != True:
if self.stopthread == "Pause":
time.sleep(1)
continue
curr_loop += 1
self.process += 10 # Testing only - mock process id
if curr_loop <= 60: # Update progress bar
time.sleep(1.0)
evt = progress_event(count=curr_loop,process="Envoking process "+str(self.process))
#Send back current count for the progress bar
try:
wx.PostEvent(self.target, evt)
except: # The parent frame has probably been destroyed
self.terminate()
self.terminate()
def run2(self):
curr_loop = 0
while self.stopthread != True:
if self.stopthread == "Pause":
time.sleep(1)
continue
curr_loop += 1
self.process += 100 # Testing only - mock process id
if curr_loop <= 60: # Update progress bar
time.sleep(1.0)
evt = progress_event(count=curr_loop,process="Checking process"+str(self.process))
#Send back current count for the progress bar
try:
wx.PostEvent(self.target, evt)
except: # The parent frame has probably been destroyed
self.terminate()
self.terminate()
def terminate(self):
self.stopthread = True
def pause(self):
if self.stopthread == "Pause":
self.stopthread = False
self.target.btn_pause.SetLabel('Pause processing')
else:
self.stopthread = "Pause"
self.target.btn_pause.SetLabel('Continue processing')
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()