how to suspend execution of a function - python-3.x

I would like to play with 3 buttons. I have to pause the execution of my function and restart the execution where it stopped(unpause). I must also put stop the execution of my function and restart the execution from the start.
the cancel button must also stop the execution like stop. PyprogressDialog must disappear after a button (any button) set press.thank
import wx
import time
class PyProgressDemo(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
self.panel = wx.Panel(self, -1)
self.startbutton = wx.Button(self.panel, -1, "Start with PyProgress!")
self.pausebutton = wx.Button(self.panel, -1, "Pause/Unpause PyProgress!")
self.stopbutton = wx.Button(self.panel, -1, "stop all thing")
self.startbutton.Bind(wx.EVT_BUTTON, self.onButton)
self.pausebutton.Bind(wx.EVT_BUTTON, self.onPause)
self.stopbutton.Bind(wx.EVT_BUTTON, self.onStop)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.startbutton)
vbox.Add(self.pausebutton)
vbox.Add(self.stopbutton)
self.panel.SetSizer(vbox)
self.Show()
import threading
self.shutdown_event = threading.Event()
def activity(self):
while not self.shutdown_event.is_set():
for i in range(10):
print (i)
time.sleep(1)
if self.shutdown_event.is_set():
break
print("stop")
self.keepGoing = True
self.shutdown_event.set()
def onButton(self, event):
import threading
threading.Thread(target = self.activity).start()
self.dlg = wx.ProgressDialog('title', 'Some thing in progresse...',
style= wx.PD_ELAPSED_TIME
| wx.PD_CAN_ABORT)
self.keepGoing = False
while self.keepGoing == False:
wx.MilliSleep(30)
keepGoing = self.dlg.Pulse()
self.dlg.Destroy()
def onPause(self, event):
#pause/unpause
pass
def onStop(self, event):
#and wx.PD_CAN_ABORT
self.shutdown_event.set()
app = wx.App()
prog = PyProgressDemo(None)
app.MainLoop()

Here is a simple example that will open a progress dialog and start a simulated task when the button is clicked. When the cancel button is clicked the progress dialog will close and the long running task will stop. When the resume button is clicked it will re-open the progress dialog and re-start the long running task.
import wx, traceback
class Mainframe(wx.Frame):
def __init__(self):
super().__init__(None)
self.btn = wx.Button(self, label="Start")
self.btn.Bind(wx.EVT_BUTTON, self.on_btn)
# progress tracks the completion percent
self._progress = 0
self._task_running = False
self.dialog = None # type: wx.ProgressDialog
self.CenterOnScreen(wx.BOTH)
self.Show()
self.poll()
def on_btn(self, evt):
# is there a dialog already opened?
if not self.dialog:
# create a progress dialog with a cancel button
self.dialog = wx.ProgressDialog("title", "message", 100, self, wx.PD_CAN_ABORT)
self.dialog.Show()
self.btn.SetLabel("Running")
self.start_long_running_task()
def start_long_running_task(self):
if not self._task_running:
print("starting long running task")
self._task_running = True
self.long_running_task()
def long_running_task(self):
""" this method simulates a long-running task,
normally it would be run in a separate thread so as not to block the UI"""
if not self:
return # the frame was closed
if self.dialog:
# increment the progress percent
self._progress += 1
if self._progress > 100:
self._progress = 0
wx.CallLater(300, self.long_running_task)
else:
# the progress dialog was closed
print("task stopped")
self._task_running = False
def poll(self):
""" this method runs every 0.3 seconds to update the progress dialog, in an actual implementation
self._progress would be updated by the method doing the long-running task"""
if not self:
return # the frame was closed
if self.dialog:
# the cancel button was pressed, close the dialog and set the button label to 'Resume'
if self.dialog.WasCancelled():
self.btn.SetLabel("Resume")
self.dialog.Destroy()
else:
# update the progress dialog with the current percentage
self.dialog.Update(self._progress, "%s%% complete" % self._progress)
wx.CallLater(300, self.poll)
try:
app = wx.App()
frame = Mainframe()
app.MainLoop()
except:
input(traceback.format_exc())

Related

Is there a way to send an event to wxApp?

I am using wxPython for the GUI, and I have a different thread that interacts with some I/O. Is there a way to update the GUI to show the I/O status when the thread receives an event from the I/O device?
To be clearer: every 3 seconds the IOthread asks to a parking bar if it is up or down, and I want the GUI to show a red led if it is down or a green led when it is up. So basically I need to communicate the information received by the IOthread to the GUI, which is running in the main thread
You could have a peek at pubsub but as you are using wxPython with a thread, it's easier to use a user defined event.
This adapted code, simply alternates between True and False, obviously you would change that and only post the event, if it changes.
from threading import Thread
import wx
import time
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class WorkThread(Thread):
def __init__(self,parent_target):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.target = parent_target
self.stop_work_thread = 0
self.start() # start the thread
def run(self):
curr_loop = True
while True:
if self.stop_work_thread == 1: # Exit
break
time.sleep(1)
if self.stop_work_thread == 2: # Paused
continue
curr_loop = not curr_loop
evt = progress_event(active=curr_loop,name=self.name)
#Send back current bar position up True, Down False
try:
wx.PostEvent(self.target, evt)
except: # The parent frame has probably been destroyed
self.stop()
return
def stop(self):
self.stop_work_thread = 1
def pause(self):
if self.stop_work_thread == 2:
self.stop_work_thread = 0
self.target.pause_btn.SetLabel('Pause')
else:
self.stop_work_thread = 2
self.target.pause_btn.SetLabel('Paused')
class Progress(wx.Frame):
def __init__(self, parent, title):
super(Progress, self).__init__(parent, title = title,size = (500,300))
left_sizer = wx.BoxSizer(wx.VERTICAL)
middle_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel = wx.Panel(self)
self.start_btn = wx.Button(self.panel, label="Start")
self.stop_btn =wx.Button(self.panel, label="Stop")
self.pause_btn =wx.Button(self.panel, label="Pause")
self.quit_btn =wx.Button(self.panel, label="Quit")
self.logger = wx.TextCtrl(self.panel, size=(200,200), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.stop_btn.Disable()
self.pause_btn.Disable()
left_sizer.Add(self.start_btn,0,wx.EXPAND)
left_sizer.Add(self.stop_btn,0,wx.EXPAND)
left_sizer.Add(self.pause_btn,0,wx.EXPAND)
left_sizer.Add(self.quit_btn,0,wx.EXPAND)
middle_sizer.Add(self.logger,0,wx.EXPAND)
self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainsizer.Add(left_sizer)
self.mainsizer.Add(middle_sizer)
self.panel.SetSizer(self.mainsizer)
self.Layout()
self.start_btn.Bind(wx.EVT_BUTTON, self.onStart)
self.stop_btn.Bind(wx.EVT_BUTTON, self.onCancel)
self.pause_btn.Bind(wx.EVT_BUTTON, self.onPause)
self.quit_btn.Bind(wx.EVT_BUTTON, self.onExit)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnActive)
self.Bind(wx.EVT_CLOSE, self.onExit)
def OnActive(self, event):
if not self.work.is_alive():
return
active = event.active
ident = event.name
if active:
self.logger.Clear()
self.logger.SetBackgroundColour("green")
self.logger.write('\n\n\n\n\n\t\tBar is Up')
else:
self.logger.Clear()
self.logger.SetBackgroundColour("red")
self.logger.write('\n\n\n\n\n\t\tBar is Down')
def onStart(self, event):
self.start_btn.Disable()
self.work = WorkThread(parent_target=self)
self.pause_btn.Enable()
self.stop_btn.Enable()
def onPause(self, event):
if self.work.is_alive():
self.work.pause() # Pause the thread
def onCancel(self, event):
"""Cancel thread process"""
try:
self.work.stop()
self.work.join()
except:
pass
self.onFinish()
def onFinish(self):
"""thread process finished - clean up"""
self.start_btn.Enable()
self.stop_btn.Disable()
self.pause_btn.Disable()
self.pause_btn.SetLabel("Pause")
self.logger.Clear()
self.logger.SetBackgroundColour("white")
def onExit(self, event):
self.onCancel(None)
self.onFinish()
self.Destroy()
app = wx.App()
frame = Progress(None,'Bar Up/Down Display')
frame.Show()
app.MainLoop()

wxPython threading - GUI freezes while doing an animation in GLCanvas

What is the proper way of refreshing a GLCanvas (with heavy 3D geometries) without freezing the GUI?
I'm trying to use a set of GUI elements (buttons, checkboxes) to control a GLCanvas which is refreshed to display a 3D animation. My problem is that the GUI elements are not responsive when the animation loop is on.
I've tried to launch the animation loop in three different ways:
Option 1: within a thread
Option 2: using wx.lib.delayedresult (most likely similar to thread)
Option 3: calling Refresh after the onDraw event
It seems I can get the Option 1 to work, but I need to introduce a sleep delay with time.sleep(xxx) in between the call to the Refresh of the canvas. Otherwise the GUI remains poorly responsive: resizing the window, will end up "graying" the GUI elements, clicking on the checkboxes will trigger events but not the "checking" of the box, the "mouseover" effect over buttons does not work, etc.
I enclose a small working example. In my actual application, I animate a 3D geometry using moderngl, and I noticed that the "sleep" time required varies depending on how heavy the geometry is (as opposed to this example which is extremely lightweight and works with a delay as small as 0.00001s). I'm wondering what I'm missing. Thanks!
import wx
from wx import glcanvas
import wx.lib.delayedresult as delayedresult
from OpenGL.GL import *
import OpenGL.GL.shaders
import time
import numpy as np
from threading import Thread
# --- Option 1: Thread
class TestThread(Thread):
def __init__(self, parent, canvas):
Thread.__init__(self)
self.parent=parent
self.canvas=canvas
self.start() # start the thread
def run(self):
print('Thread running... ')
while self.canvas.animate:
#time.sleep(0.01) # <<<<<<<<<<<< This line needed
self.canvas.Refresh()
print('Tread done ')
class OpenGLCanvas(glcanvas.GLCanvas):
def __init__(self, parent):
glcanvas.GLCanvas.__init__(self, parent, -1, size=(400,400))
self.context = glcanvas.GLContext(self)
self.SetCurrent(self.context)
self.init = False
self.animate = False
self.refreshAfter = False
self.t=0
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event):
wx.PaintDC(self)
if not self.init:
self.InitGL()
self.init = True
self.OnDraw()
def InitGL(self):
glEnable(GL_DEPTH_TEST)
def OnDraw(self):
""" Called at every frame"""
glClearColor(0.1, 0.0, np.mod(self.t,1), 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
if self.animate:
self.t+=0.0005 # increment time
# ---- Option 3
if self.refreshAfter:
self.Refresh() # Trigger next frame
self.SwapBuffers()
# --- Option 2: delayed results
def onAnimDelayedEnd(self, thread):
""" Consumer """
print('Delayed result done')
jobID = thread.getJobID()
result = thread.get()
def onAnimDelayedStart(self):
print('Delayed result running... ')
while self.animate:
self.Refresh()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.canvas = OpenGLCanvas(self)
# GUI
self.rot_btn = wx.Button(self, -1, label="Toggle animation", pos=(430, 10))
self.cbDo = wx.CheckBox (self, -1, label="Do something", pos=(430 ,100))
self.radDo = wx.RadioButton(self, -1, label="Do something", pos=(430,140))
self.radDo2= wx.RadioButton(self, -1, label="Do something", pos=(430,180))
# Binding
self.rot_btn.Bind(wx.EVT_BUTTON, self.toggleAnim)
self.radDo.Bind(wx.EVT_RADIOBUTTON, self.doSomething)
self.radDo2.Bind(wx.EVT_RADIOBUTTON, self.doSomething)
self.cbDo.Bind(wx.EVT_CHECKBOX , self.doSomething)
def toggleAnim(self, event):
if not self.canvas.animate:
self.canvas.animate = True
# --- Option 1: thread
TestThread(self, self.canvas)
# --- Option 2: delayed result
#delayedresult.startWorker(self.canvas.onAnimDelayedEnd, self.canvas.onAnimDelayedStart, jobID=1)
# --- Option 3: refreshloop
#self.canvas.refreshAfter=True
#self.canvas.Refresh() # set the canvas into an "infinite" refresh loop
else:
self.canvas.animate = False
def doSomething(self, event):
print('Do something')
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="My wx frame", size=(600,400))
self.Bind(wx.EVT_CLOSE, self.on_close)
self.panel = MyPanel(self)
def on_close(self, event):
self.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = MyFrame().Show()
app.MainLoop()

wxPython class doesnt show in window

So i created a class that runs a big calculation in a seperet thread.
This is what i expected to see:
And this is what i do see:
This is the frame class
class ThreadFrame(wx.Frame):
def __init__(self,parent=None):
wx.Frame.__init__(self,parent=parent)
self.frame = wx.Frame(None, title='Bitte Warten',style=wx.FRAME_NO_TASKBAR)
self.frame.SetSize(500,100)
self.panel=wx.Panel(self.frame)
self.parent=parent
self.WaitLbl=wx.StaticText(self.panel,-1)
self.WaitLbl.SetLabel('Geotags werden ausgelesen und abgerufen.')
self.progress = wx.Gauge(self.panel,size=(500,30), range=self.parent.list_ctrl.GetItemCount())
self.btn = wx.Button(self.panel,label='Abbrechen')
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.Sizer=wx.BoxSizer(wx.VERTICAL)
#Add Widgets LeftSizer
self.Sizer.Add(self.WaitLbl,0,wx.ALL|wx.CENTER,5)
self.Sizer.Add(self.progress,0,wx.ALL,5)
self.Sizer.Add(self.btn,0,wx.ALL|wx.CENTER,5)
self.panel.SetSizer(self.Sizer)
self.Sizer.Fit(self.panel)
self.panel.Layout()
self.Centre()
#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.frame, self.parent)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
print('Thread lebt noch')
self.mythread.terminate() # Shutdown the thread
print('Thread wird beendet')
self.mythread.join() # Wait for it to finish
self.Close()
And this is the thread where the calculation is running
class TestThread(Thread):
def __init__(self,parent_target,toplevel):
Thread.__init__(self)
self.parent = toplevel
self.ownparent=parent_target
self.stopthread = False
self.start() # start the thread
def run(self):
print('Thread gestartet')
i=0
while self.stopthread == False:
#if calculation is not finished:
#do calculation and count i one up
evt = progress_event(count=i)
#Send back current count for the progress bar
try:
wx.PostEvent(self.ownparent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
i=i+1
else:
print('Thread Terminated')
self.terminate()
def terminate(self):
self.stopthread = True
And this is how i call the class from my main programm:
frame=ThreadFrame(self)
The main program also has a frame open. So this is a frame which is opend and then starts a thread which does calculation and then stops.
I think thats all that is to know. I replaced the caluclation with speudo code because my brain hurts and i cant come up with a plachold right now. But i feel like between all the fiting and sizers and panels and frames i went somewhere wrong. And i totaly dont look through all of this stuff at the moment.
The code you show doesn't seem to correspond to the screenshots. Whatever problem with the layout you might have, you should still have "Bitte warten" in the frame title, but your screenshot doesn't even show this. Either you're not executing the code you show at all, or you create some other frame elsewhere in your code which you see here. Or, of course, you've uploaded a wrong screenshot or code version. But something just doesn't fit here.
So im not sure what caused the problem. I started from scratch and now it works. This time i didnt use a new frame because the class itself is allready a frame.
class GeoThreadFrame(wx.Frame):
def __init__(self, radsteuer):
wx.Frame.__init__(self,parent=radsteuer.frame,style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP|wx.FRAME_NO_TASKBAR)
panel = wx.Panel(self)
self.SetWindowStyle(wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP)
self.progress = wx.Gauge(panel,size=(300,30), pos=(10,50), range=radsteuer.list_ctrl.GetItemCount())
self.btn = wx.Button(panel,label='Abbrechen', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
panel.Center()
#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.Center()
self.Show()
self.mythread = GeoLocationThread(self, radsteuer)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
#time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
#self.mythread.join(3) # Wait for it to finish
self.Destroy()
class GeoLocationThread(Thread):
def __init__(self,parent_target,mainparent):
Thread.__init__(self)
self.parent = parent_target
self.mainparent=mainparent
self.stopthread = False
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
i=0
while self.stopthread == False:
if i < self.mainparent.list_ctrl.GetItemCount():
self.calculation(i)
evt = progress_event(count=i)
i=i+1
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
def calculate(self,i):
#your calculation here

Open another wx.python frame cause the main UI to be blocked

I am trying to open another wx frame from a main frame UI. I followed the example shown here: https://wiki.wxpython.org/CallAfter but my main UI is still blocked.
Here is the event listener on the main UI:
def testShowGUI(self):
# This process is a long one
# It uses the vtk to read point cloud file and reconstruct the surface
file = "cache/d9c5e8ef-7b7f-485e-8fc8-23098c32afcb.ply"
reader = vtk.vtkPLYReader()
reader.SetFileName(file)
reader.Update()
delaunay = vtk.vtkDelaunay2D()
delaunay.SetAlpha(0.1)
delaunay.SetTolerance(0.0001)
delaunay.SetOffset(1.25)
delaunay.BoundingTriangulationOff()
delaunay.SetInputData(reader.GetOutput())
delaunay.Update()
#Once finish reading and processing the point cloud, pass to the next function for rendering
wx.CallAfter(self.AfterProcess, delaunay)
def AfterProcess(self, data):
meshVisGui = MesVisGUI.MeshVisGui(data)
meshVisGui.Show()
def OnEnter(self, event):
#Event listener when user click on Enter button
my_thread = threading.Thread(target=self.testShowGUI)
my_thread.start()
The code for the separate frame is as below:
class MeshVisGui(wx.Frame):
SPACING = 4
def __init__(self, delaunay, parent=None):
self.delaunayData = delaunay
self.title = "Mesh Visualization"
wx.Frame.__init__(self, None, -1, self.title)
self.Initialize()
def Initialize(self):
self.panel = wx.Panel(self, -1, size=(600, 400), style=wx.BORDER_RAISED)
self.widget3d = wxVTKRenderWindowInteractor(self.panel, -1)
self.widget3d.Enable()
self.render = vtk.vtkRenderer()
self.render.SetBackground(params.BackgroundColor)
self.widget3d.GetRenderWindow().AddRenderer(self.render)
self.interactor = self.widget3d.GetRenderWindow().GetInteractor()
self.interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
self.axesWidget = utils.createAxes(self.interactor)
self.meshActor = utils.build_actor(self.delaunayData)
self.render.AddActor(self.meshActor)
self.render.ResetCamera()
box = wx.BoxSizer(wx.VERTICAL)
box.Add(self.widget3d, 1, wx.EXPAND, self.SPACING)
self.panel.SetSizer(box)
self.Layout()
However, on my main UI, it is still showing a spinning icon and block the UI while it is trying to process the point cloud data. Can someone help me spot what I have done wrong?
Wxpython version: 4.0.1
Python version: 3.6.5
Following on from Mike Driscoll's answer and comment, here is a sample of a threaded task running from another panel.
The thread reports back to the 2nd panel (it's parent) using an event. This allows a progress bar to be updated.
The 2nd panel includes a "Cancel" option for the threaded task, whilst the main frame has a button to test if it is not frozen.
The use of wx.GetApp().Yield() may be slightly old fashioned but I have always found it to be reliable.
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class ThreadFrame(wx.Frame):
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
self.btn = wx.Button(panel,label='Stop Long running process', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.progress = wx.Gauge(panel,size=(300,10), pos=(10,50), range=300)
#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)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.Destroy()
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.parent = parent_target
self.stopthread = False
self.time = time.time()
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
while self.stopthread == False:
curr_loop = int(time.time() - self.time)
if curr_loop < 300:
time.sleep(1)
evt = progress_event(count=curr_loop)
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.parent=parent
btn = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(200,30), pos=(10,10))
btn.Bind(wx.EVT_BUTTON, self.Thread_Frame)
btn2 = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(200,30), pos=(10,50))
btn2.Bind(wx.EVT_BUTTON, self.AddText)
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(400,100))
def Thread_Frame(self, event):
frame = ThreadFrame(title='Threaded Task', parent=self.parent)
def AddText(self,event):
self.text_count += 1
txt = self.txt.GetValue()
txt += "More text " + str(self.text_count)+"\n"
self.txt.SetValue(txt)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(600,400))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
You do not need threads to open new frames / windows in your wxPython application. You just need to create a sub-class of wx.Frame to hold the code of your other frame. Then from your main application's frame, you can instantiate the other frame and show it. You use the same concept when you create a wx.Dialog or a wx.MessageDialog.
Here is a simple example:
import wx
class OtherFrame(wx.Frame):
"""
Class used for creating frames other than the main one
"""
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label='Create New Frame')
btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
def on_new_frame(self, event):
frame = OtherFrame(title='SubFrame',
parent=wx.GetTopLevelParent(self))
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
In this example, I set the other frame's parent to the MainFrame instance by using wx.GetTopLevelParent(self). The benefit of setting a parent for the sub-frames is that if I close the main frame, it will cause the other frames to also be closed.

Python 3, using tkinter, trying to create a game where the user has to click a button as many times as they can until the time runs out

Basically, I'm trying to make a small game of sorts where the user has to click a button as many times as they can before the time runs out (5 seconds).
After 5 seconds have passed, the button is greyed/disabled. However, my code has trouble working, because the 'timer' works only when the user clicks the button. I want the timer to run regardless of whether the user has clicked the button.
So basically, when the program is run, even if the user hasn't clicked the button and 5 seconds have passed, the button should be greyed/disabled.
Here is my code:
from tkinter import *
import time
class GUI(Frame):
def __init__(self, master):
Frame.__init__(self,master)
self.result = 0
self.grid()
self.buttonClicks = 0
self.create_widgets()
def countTime(self):
self.end = time.time()
self.result =self.end - self.start
return (self.result)
def create_widgets(self):
self.start = time.time()
self.button1 = Button(self)
self.label = Label(self, text=str(round(self.countTime(),1)))
self.label.grid()
self.button1["text"] = "Total clicks: 0"
self.button1["command"] = self.update_count
self.button1.grid()
def update_count(self):
if(self.countTime() >=5):
self.button1.configure(state=DISABLED, background='cadetblue')
else:
self.buttonClicks+=1
self.button1["text"] = "Total clicks: " + str(self.buttonClicks)
root = Tk()
root.title("Something")
root.geometry("300x300")
app = GUI(root)
root.mainloop()
You must create a timer using the after()
from tkinter import *
import time
class GUI(Frame):
def __init__(self, master):
Frame.__init__(self,master)
self.result = 0
self.grid()
self.buttonClicks = 0
self.create_widgets()
self.isRunning = True
self.update_clock()
self.master = master
def countTime(self):
self.end = time.time()
self.result =self.end - self.start
return self.result
def create_widgets(self):
self.start = time.time()
self.button1 = Button(self)
self.label = Label(self, text=str(round(self.countTime(),1)))
self.label.grid()
self.button1["text"] = "Total clicks: 0"
self.button1["command"] = self.update_count
self.button1.grid()
def update_count(self):
if self.isRunning:
self.buttonClicks+=1
self.button1["text"] = "Total clicks: " + str(self.buttonClicks)
def update_clock(self):
t = round(self.countTime(), 1)
self.label.configure(text=str(t))
if t < 5:
self.master.after(100, self.update_clock)
else:
self.isRunning = False
root = Tk()
root.title("Something")
root.geometry("300x300")
app = GUI(root)
root.mainloop()
You should be running a different thread (As seen here: Tkinter: How to use threads to preventing main event loop from "freezing") to be running the timer.
Check this other question to get an idea on how your other thread should be like How to create a timer using tkinter?

Resources