How to refresh the main frame when the child dialog do someting - python-3.x

I want the main frame refresh to update the label When I click the "done" button on MyDialog, But now it doesn't work.
Does there anything wrong? Thanks.
This is the code:
MyDialog: the child dialog where there is a button on it to update the label of the main frame
MainFrame: the main Frame, there is a button on it to start my dialog
# -*- coding: utf-8 -*-
import wx
#Dialog
class MyDialog(wx.Dialog):
"""setting MyDialog."""
def __init__(self):
self.dlg_main = wx.Dialog.__init__(self, None, -1, title="setting", size=(300, 300))
self.btn_ok = wx.Button(self, label="done", pos=(30, 30), size=(50, 26))
self.Bind(wx.EVT_BUTTON, self.__OnButtonClick_save, self.btn_ok,)
def __OnButtonClick_save(self, event):
self.Destroy()
main = MainFrame()
**main.set_label_name('test')**
main.Destroy()
def start_dialog():
my_dialog = MyDialog()
my_dialog.ShowModal()
my_dialog.Destroy()
#Main Frame
class MainFrame(wx.Frame):
def __init__(self):
self.main_frame = wx.Frame.__init__(self, None, -1, title='simple', size=(400, 400))
self.Centre()
self.label_name = wx.StaticText(self, label="Hello,everyone", pos=(30, 30))
self.btn_set = wx.Button(self, label="set", pos=(30, 60))
self.Bind(wx.EVT_BUTTON, self.on_button_click, self.btn_set)
def set_label_name(self, str):
print(str)
self.label_name.SetLabel('hello, Boys')
def on_button_click(self, event):
start_dialog()
def show_main():
main = wx.App()
main_win = MainFrame()
main_win.Show()
main.MainLoop()
if __name__ == '__main__':
show_main()

I have got the method, thanks all!
The point is how to call the parent method, so , use self.parent.xxx to fix the problem.
The code like this:
# -*- coding: utf-8 -*-
import wx
#Dialog
class MyDialog(wx.Dialog):
"""setting MyDialog."""
def __init__(self, parent, title):
super(MyDialog, self).__init__(parent, title=title, size=(300, 300))
self.parent = parent
panel = wx.Panel(self)
btn_ok = wx.Button(panel, label="done", pos=(30, 30), size=(50, 26))
btn_ok.Bind(wx.EVT_BUTTON, self.__OnButtonClick_save)
def __OnButtonClick_save(self, event):
#THis is the different
self.parent.set_label_name('test')
self.Destroy()
def start_dialog():
my_dialog = MyDialog()
my_dialog.ShowModal()
my_dialog.Destroy()
#Main Frame
class MainFrame(wx.Frame):
def __init__(self):
self.main_frame = wx.Frame.__init__(self, None, -1, title='simple', size=(400, 400))
self.init_ui()
def init_ui(self):
self.label_name = wx.StaticText(self, label="Hello,everyone", pos=(30, 30))
btn_set = wx.Button(self, label="set", pos=(30, 60))
btn_set.Bind(wx.EVT_BUTTON, self.on_button_click)
self.Centre()
def set_label_name(self, str):
self.label_name.SetLabel('hello, Boys')
def on_button_click(self, event):
my_dialog = MyDialog(self, "setting")
my_dialog.ShowModal()
my_dialog.Destroy()
def show_main():
main = wx.App()
main_win = MainFrame()
main_win.Show()
main.MainLoop()
if __name__ == '__main__':
show_main()
Thanks for the idea in this answer Wxpython show dialog on main frame startup

Related

How to Switch Between Panels whitch Buttons

I can not navigate between the panels of my App through the buttons. I followed a lot of tutorials like this one but none solve my problem.
Thank for Help.
####################-----Principal Panel (first Panel)
import wx
from OtherPanel import OtherPanel
class MyPanel(wx.Panel):
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent, size=(600, 500))
self.SetBackgroundColour(wx.Colour(250, 250, 250))
parent.Center()
self.histoPanel = OtherPanel(self)
self.home()
self.panel = wx.Panel(self)
def home(self):
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour(wx.Colour(250, 250, 250))
bS =wx.Button(self, -1,"Go some where ",(200,30), size=(150,150))
self.Bind(wx.EVT_BUTTON, self.Sy, bS)
bOthe = wx.Button(self, -1,"Go to OtherPanel",(400,30),(150,150))
self.Bind(wx.EVT_BUTTON, self.onOpenFrame,bOthe)
def onOpenFrame(self, event):
"""
Opens secondary frame
"""
self.Hide()
self.histoPanel.Show()
def Sy(self, event):
"""
I have another panel (panel 3)
"""
self.Hide()
#self.panel3.Show()
--Second Panel (another Panel)
this panel is displayed when you click on the button of the first panel
import wx
class OtherPanel(wx.Panel):
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent)
self.panel = wx.Panel(self)
self.SetBackgroundColour(wx.Colour(250, 250, 250))
self.Center()
self.homeHist()
self.Show()
def homeHist(self):
bBack = wx.Button(self, -1, "", pos=(20,30), size=(50,50))
self.Bind(wx.EVT_BUTTON, self.onClose, bBack)
def onClose(self, event):
self.Close()
------My Frame---.
here is my Frame/Windows. he must wear all the panels in turn
import wx
from MyPanel import MyPanel
from OtherPanel import OtherPanel
class MyFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, size = (600,500))
self.myPanel = MyPanel(self)
self.otherpanel = OtherPanel(self)
self.otherpanel.Hide()
self.menu()
self.CreateStatusBar(style= wx.BORDER_NONE)
self.SetBackgroundColour("gray")
self.SetStatusText("\tthank")
def menu(self):
menubar = wx.MenuBar()
fileMenu = wx.Menu()
menubar.SetBackgroundColour(wx.Colour(1, 1, 6))
fileMenu.AppendSeparator()
live = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+Q', '\tlive my app')
fileMenu.Append(live)
self.Bind(wx.EVT_MENU, self.Onexit, live)
menubar.Append(fileMenu, '\t&Menu')
self.SetMenuBar(menubar)
def Onexit(self, event):
self.Close(True)
def Bouton(self, event):
if self.myPanel.IsShown():
self.SetTitle("Panel Two Showing")
self.myPanel.Hide()
self.otherpanel.Show()
else:
self.SetTitle("Panel One Showing")
self.myPanel.Show()
self.otherpanel.Hide()
self.Layout()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()
In your MyPanel.__init__ you are creating an instance of OtherPanel assigned to self.histoPanel as a child of the MyPanel, and then MyPanel's event handlers are hiding itself, which in turn hides its children including the self.histoPanel. You're also creating some panels assigned to self.panel which don't seem to be used for anything.
The way you are doing it in the MyFrame class is much better. It creates both panels and hides one. Then in MyFrame.Bouton it hides one and shows the other, depending on the current state. So what you need to do is get rid of all the extra panels in MyPanel (self.histoPanel and also the various self.panels that are never used for anything) and then you just need to have the button event ask the parent frame to swap the panels instead of trying to do it itself.
Here is an example of what Robin is suggesting in his answer:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 1", size=(250,75))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
def switch(self, event):
self.parent.Swap()
class MyOtherPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 2", size=(175,250))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
self.panel.Hide()
def switch(self, event):
self.parent.Swap()
class PanelSwitcher(wx.Frame):
def __init__(self):
super().__init__(None)
vbox = wx.BoxSizer(wx.VERTICAL)
self.panel1 = MyPanel(self)
self.panel2 = MyOtherPanel(self)
vbox.Add(self.panel1)
vbox.Add(self.panel2)
self.SetSizer(vbox)
self.Show()
def Swap(self):
if self.panel1.panel.IsShown():
self.panel1.panel.Hide()
self.panel2.panel.Show()
else:
self.panel2.panel.Hide()
self.panel1.panel.Show()
self.Layout()
if __name__ == "__main__":
app = wx.App()
PanelSwitcher()
app.MainLoop()

wxPython, making a window modal

The following code makes a window to behave like a wx.Dialog but it works perfectly only in Windows. In macOS the buttons in the main window are not disabled and in Linux the main window can be maximized and minimized.
How can I have in macOS and Linux the same modal behavior I have in Windows? I know I could disable the buttons manually and I guess there should be a way to handle the minimize and maximize buttons, but perhaps there is an easier way.
import wx
class MyFrame(wx.Frame):
def __init__(self):
title='Multiple consecutive windows (ShowModal)'
super().__init__(None, title=title)
self.WinNum = 5
#### Widgets
self.panel = wx.Panel(self)
self.buttonShow = wx.Button(self.panel, pos=(50, 50), label='ShowModal')
self.buttonTest = wx.Button(self.panel, pos=(50, 100), label='Test')
#### Bind
self.buttonShow.Bind(wx.EVT_BUTTON, self.ShowWindows)
#### Position of the window
self.SetPosition(pt=(50, 50))
def ShowWindows(self, event):
i = 0
while i < self.WinNum:
a = WinModal(i)
a.ShowModal()
i += 1
class WinModal(wx.Frame):
def __init__(self, ThisWinNum):
title = 'This is window number: ' + str(ThisWinNum)
super().__init__(None, title=title)
#### Variables
self.ThisWinNum = ThisWinNum
#### Widgets
self.panel = wx.Panel(self)
self.buttonOk = wx.Button(self.panel, pos=(50, 50), label='Ok')
#### Positions
self.SetPosition(pt=(200, 200))
#### Bind
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.EVT_BUTTON, self.onOk)
def ShowModal(self):
"""
This function is the one giving the wx.FileDialog behavior
"""
self._disabler = wx.WindowDisabler(self)
self.Show()
self.eventLoop = wx.GUIEventLoop()
self.eventLoop.Run()
def OnClose(self, event):
"""
To handle closing the windows because you need to exit the eventLoop
of the modal window.
"""
del self._disabler
self.eventLoop.Exit()
self.Destroy()
def onOk(self, event):
print(self.ThisWinNum)
self.cancel = False
self.OnClose(event)
if __name__ == '__main__':
app = wx.App()
frame = MyFrame()
frame.Show()
app.MainLoop()
else:
pass
Probably my question was not correctly formulated. Now I realize that what I wanted to know was how to create a custom dialog window.
This can be done by deriving the custom class from wx.Dialog instead of wx.Frame. The class wx.Dialog gives an empty space that you can fill with widgets just like a normal wx.Frame and also has the ShowModal() method in it. Therefore, you can show your window as a modal window.
import wx
class MainWin(wx.Frame):
def __init__(self):
super().__init__(None, title='My modal window')
####---- Variables
self.frameFvar = None
####---- Widgets
self.panel = wx.Panel(self)
self.buttonF = wx.Button(self.panel, label='Show Normal')
self.buttonM = wx.Button(self.panel, label='Show Modal')
####---- Sizer
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.buttonF, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
self.sizer.Add(self.buttonM, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
self.sizerM = wx.BoxSizer(wx.HORIZONTAL)
self.sizerM.Add(self.sizer, border=20,
flag=wx.EXPAND|wx.ALIGN_CENTER|wx.ALL)
self.panel.SetSizer(self.sizerM)
self.sizerM.Fit(self)
####---- Position
self.SetPosition(pt=(50, 50))
####---- Bind
self.buttonF.Bind(wx.EVT_BUTTON, self.ShowAsNormal)
self.buttonM.Bind(wx.EVT_BUTTON, self.ShowAsModal)
#---
def ShowAsNormal(self, event):
if self.frameFvar == None:
self.frameF = AsFrame(self)
self.frameF.Show()
self.frameFvar = True
else:
self.frameF.Raise()
#---
def ShowAsModal(self, event):
self.frameM = AsDialog(self)
if self.frameM.ShowModal() == wx.ID_OK:
print("Exited by Ok button")
else:
print("Exited by X button")
self.frameM.Destroy()
#---
#---
class AsFrame(wx.Frame):
def __init__(self, parent):
super().__init__(parent=parent, title='Shown as a wx.Frame')
####---- Variables
self.parent = parent
####---- Widgets
self.a = MyPanel(self)
####---- Position
self.SetPosition(pt=(50, 200))
####---- Bind
self.Bind(wx.EVT_CLOSE, self.OnClose)
#---
def OnClose(self, event):
self.parent.frameFvar = None
self.Destroy()
#---
#---
class AsDialog(wx.Dialog):
def __init__(self, parent):
super().__init__(parent=parent, title='Shown as a wx.Dialog')
####---- Variables
self.SetEscapeId(12345)
####---- Widgets
self.a = MyPanel(self)
self.buttonOk = wx.Button(self, wx.ID_OK)
####---- Sizers
self.sizerB = wx.StdDialogButtonSizer()
self.sizerB.AddButton(self.buttonOk)
self.sizerB.Realize()
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.a, border=10, flag=wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
self.sizer.Add(self.sizerB, border=10,
flag=wx.EXPAND|wx.ALIGN_RIGHT|wx.ALL)
self.SetSizer(self.sizer)
####---- Position
self.SetPosition(pt=(550, 200))
#---
#---
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent=parent)
####---- Variables
self.parent = parent
####---- Widgets
label = ("The same window shown as a wx.Frame or a wx.Dialog")
self.text = wx.StaticText(self, label=label, pos=(10, 10))
#---
#---
if __name__ == '__main__':
app = wx.App()
frameM = MainWin()
frameM.Show()
app.MainLoop()

PyQt5 switch between windows

I want to create menu based on tiles. Now I need PyQt5 concept how to switch MainWindow to Window1/Window2/... with back option to MainWindow. The only thing I've achieved is opening a new window on top. I'd rather have separate windows, where I could define different functions.
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from PyQt5.QtCore import pyqtSlot
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.title = "App"
self.top = 100
self.left = 100
self.width = 680
self.height = 500
self.InitUI()
def InitUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
buttonWindow1 = QPushButton('Window1', self)
buttonWindow1.move(100, 100)
buttonWindow1.clicked.connect(self.buttonWindow1_onClick)
buttonWindow2 = QPushButton('Window2', self)
buttonWindow2.move(100, 200)
buttonWindow2.clicked.connect(self.buttonWindow2_onClick)
self.show()
#pyqtSlot()
def buttonWindow1_onClick(self):
self.statusBar().showMessage("Switched to window 1")
#pyqtSlot()
def buttonWindow2_onClick(self):
self.statusBar().showMessage("Switched to window 2")
if __name__ == '__main__':
app=QApplication(sys.argv)
ex=Window()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.title = "App"
self.top = 100
self.left = 100
self.width = 680
self.height = 500
self.InitUI()
def InitUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
buttonWindow1 = QPushButton('Window1', self)
buttonWindow1.move(100, 100)
buttonWindow1.clicked.connect(self.buttonWindow1_onClick)
self.lineEdit1 = QLineEdit("Type here what you want to transfer for [Window1].", self)
self.lineEdit1.setGeometry(250, 100, 400, 30)
buttonWindow2 = QPushButton('Window2', self)
buttonWindow2.move(100, 200)
buttonWindow2.clicked.connect(self.buttonWindow2_onClick)
self.lineEdit2 = QLineEdit("Type here what you want to transfer for [Window2].", self)
self.lineEdit2.setGeometry(250, 200, 400, 30)
self.show()
#pyqtSlot()
def buttonWindow1_onClick(self):
self.statusBar().showMessage("Switched to window 1")
self.cams = Window1(self.lineEdit1.text())
self.cams.show()
self.close()
#pyqtSlot()
def buttonWindow2_onClick(self):
self.statusBar().showMessage("Switched to window 2")
self.cams = Window2(self.lineEdit2.text())
self.cams.show()
self.close()
class Window1(QDialog):
def __init__(self, value, parent=None):
super().__init__(parent)
self.setWindowTitle('Window1')
self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogInfoView))
label1 = QLabel(value)
self.button = QPushButton()
self.button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
self.button.setIcon(self.style().standardIcon(QStyle.SP_ArrowLeft))
self.button.setIconSize(QSize(200, 200))
layoutV = QVBoxLayout()
self.pushButton = QPushButton(self)
self.pushButton.setStyleSheet('background-color: rgb(0,0,255); color: #fff')
self.pushButton.setText('Click me!')
self.pushButton.clicked.connect(self.goMainWindow)
layoutV.addWidget(self.pushButton)
layoutH = QHBoxLayout()
layoutH.addWidget(label1)
layoutH.addWidget(self.button)
layoutV.addLayout(layoutH)
self.setLayout(layoutV)
def goMainWindow(self):
self.cams = Window()
self.cams.show()
self.close()
class Window2(QDialog):
def __init__(self, value, parent=None):
super().__init__(parent)
self.setWindowTitle('Window2')
self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogInfoView))
label1 = QLabel(value)
self.button = QPushButton()
self.button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
self.button.setIcon(self.style().standardIcon(QStyle.SP_ArrowLeft))
self.button.setIconSize(QSize(200, 200))
layoutV = QVBoxLayout()
self.pushButton = QPushButton(self)
self.pushButton.setStyleSheet('background-color: rgb(0,0,255); color: #fff')
self.pushButton.setText('Click me!')
self.pushButton.clicked.connect(self.goMainWindow)
layoutV.addWidget(self.pushButton)
layoutH = QHBoxLayout()
layoutH.addWidget(label1)
layoutH.addWidget(self.button)
layoutV.addLayout(layoutH)
self.setLayout(layoutV)
def goMainWindow(self):
self.cams = Window()
self.cams.show()
self.close()
if __name__ == '__main__':
app=QApplication(sys.argv)
ex=Window()
sys.exit(app.exec_())
Try this:
from file2 import Ui_Dialog2 #------>import the class for next window
def buttonWindow1_onClick(self):
self.window=QtWidgets.QMainWindow()
self.ui=Ui_Dialog2() #------------->creating an object
self.ui.setupUi(self.window)
self.window.show()
Here the Ui_Dialog2() represents that your are creating an object for new window (in your case creating object for Window1 for switch MainWindow to Window1 ).So when you click the next button this function is called so an object is created for the next window and the window will be opened.

How to terminate thread using condition returned by wx.CallAfter()?

I am very new to wxPython and also not familiar with thread concept. I would appreciate a lot if anyone could provide info sources or suggestion to my question.
I had created a GUI using wxpython to allow users run my script with inputs. Since one step takes 20 min to run so I plan to create a Progress Dialog to show users progress and allow them to abort it. I had tested this using the sample code below.
However, I couldn't stop WorkThread even though I clicked the Stop Button in Progress Dialog. I tried
1. make if statement using return value from pb.sendMessage()
2. create the ProgressDialog object when WorkThread starts and call ProgressDialog.abort
but none of them work. I wonder if there's conceptual mistakes implementing code like this to achieve what I want to do? Or if this can work with correction? Any hint would be appreciated!!
class WorkThread(Thread):
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.start() # start the thread
def run(self):
for i in range(10):
time.sleep(1)
val = 100 / 10
wx.CallAfter(pub.sendMessage, "update", step=val)
print 'Finish Run'
class ProgressDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None)
self.abort = False
self.progress = 0
bSizer2 = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
self.gauge.SetValue(0)
self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer2.Add(self.gauge, 0, 0, 5)
bSizer2.Add(self.m_button1, 0, 0, 5)
self.SetSizer(bSizer2)
self.Layout()
self.Centre(wx.BOTH)
## Connect Events
self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel)
pub.subscribe(self.updateProgress, "update")
def updateProgress(self, step):
self.progress += step
if self.abort:
self.Update()
self.Close()
elif self.progress >= 100:
self.gauge.SetValue(self.progress)
self.Update()
self.Close()
else:
self.gauge.SetValue(self.progress)
def on_cancel(self, event):
"""Cancels the conversion process"""
self.abort = True
print 'Click'
# pub.unsubscribe(self.if_abort, 'cancel')
def __del__(self):
pass
########################################################################################
class MainFrame(wx.Frame):
# ----------------------------------------------------------------------
def __init__(self,parent):
wx.Frame.__init__(self,parent)
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.btn = btn = wx.Button(panel, label="Start Thread")
btn.Bind(wx.EVT_BUTTON, self.onButton)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(sizer)
# ----------------------------------------------------------------------
def onButton(self, event):
btn = event.GetEventObject()
btn.Disable()
WorkThread()
self.dlg = ProgressDialog()
self.dlg.ShowModal()
btn.Enable()
app = wx.App()
frame = MainFrame(None)
frame.Show(True)
# start the applications
app.MainLoop()
You need a method within the thread to stop it.
You should also wait for it to stop.
Here is an option using your code:
from threading import Thread
import wx
from wx.lib.pubsub import pub
import time
class WorkThread(Thread):
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.stop_work_thread = 0
self.start() # start the thread
def run(self):
for i in range(10):
if self.stop_work_thread == 1:
break
time.sleep(1)
val = 100 / 10
wx.CallAfter(pub.sendMessage, "update", step=val)
wx.CallAfter(pub.sendMessage, "finish")
return
def stop(self):
self.stop_work_thread = 1
class ProgressDialog(wx.Dialog):
def __init__(self,parent):
wx.Dialog.__init__(self, parent)
self.parent = parent
self.abort = False
self.progress = 0
bSizer2 = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
self.gauge.SetValue(0)
self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer2.Add(self.gauge, 0, 0, 5)
bSizer2.Add(self.m_button1, 0, 0, 5)
self.SetSizer(bSizer2)
self.Layout()
self.Centre(wx.BOTH)
## Connect Events
self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel)
pub.subscribe(self.updateProgress, "update")
pub.subscribe(self.on_finish, "finish")
def updateProgress(self, step):
self.progress += step
self.gauge.SetValue(self.progress)
def on_cancel(self, event):
"""Cancels the conversion process"""
self.parent.work.stop()
self.parent.work.join()
pub.unsubscribe(self.updateProgress, "update")
pub.unsubscribe(self.on_finish, "finish")
self.Destroy()
# pub.unsubscribe(self.if_abort, 'cancel')
def on_finish(self):
"""conversion process finished"""
pub.unsubscribe(self.updateProgress, "update")
pub.unsubscribe(self.on_finish, "finish")
self.Close()
def __del__(self):
pass
########################################################################################
class MainFrame(wx.Frame):
# ----------------------------------------------------------------------
def __init__(self,parent):
wx.Frame.__init__(self,parent)
# Add a panel so it looks the correct on all platforms
self.panel = wx.Panel(self, wx.ID_ANY)
self.btn = btn = wx.Button(self.panel, label="Start Thread")
btn.Bind(wx.EVT_BUTTON, self.onButton)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
self.panel.SetSizer(sizer)
# ----------------------------------------------------------------------
def onButton(self, event):
btn = event.GetEventObject()
btn.Disable()
self.panel.work = WorkThread()
self.dlg = ProgressDialog(self.panel)
self.dlg.ShowModal()
btn.Enable()
app = wx.App()
frame = MainFrame(None)
frame.Show(True)
# start the applications
app.MainLoop()

wxPython threading wxGauge

I'm trying to learn about threading with wxPython, so I tried to make this simple example work, but it seems that the progress bar updates happen after the "long process" is done. Could anyone tell me why this is not working ?
#!/usr/bin/env python
import time
import wx
from wx.lib.pubsub import Publisher as pub
class Window(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="wxGauge")
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, range=10)
self.btn = wx.Button(self, label="Start process")
self.sizer.Add(self.gauge)
self.sizer.Add(self.btn)
self.SetSizerAndFit(self.sizer)
pub.subscribe(self.update, "update")
self.btn.Bind(wx.EVT_BUTTON, self.OnClick)
self.Show()
def update(self, msg):
self.gauge.SetValue(msg.data)
def OnClick(self, evt):
for i in range(10):
wx.CallAfter(pub.sendMessage, "update", i + 1)
time.sleep(1)
if __name__ == '__main__':
app = wx.App()
Window()
app.MainLoop()
It is because of the usage of "CallAfter". If call "sendMessage" in this way, it is actually called after the event handler finished.
You can try it in this way:
for i in range(10):
pub.sendMessage("update", i+1)
time.sleep(1)
EDIT:
put this "sendMessage" out of the event handler.
import time
import wx
from wx.lib.pubsub import Publisher as pub
class Window(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="wxGauge")
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, range=10)
self.btn = wx.Button(self, label="Start process")
self.sizer.Add(self.gauge)
self.sizer.Add(self.btn)
self.SetSizerAndFit(self.sizer)
pub.subscribe(self.update, "update")
self.btn.Bind(wx.EVT_BUTTON, self.OnClick)
self.Bind(wx.EVT_TIMER, self.TimerHandler)
self.timer = wx.Timer(self)
self.gaugeIndex = 0
self.Show()
def TimerHandler(self, event):
self.gaugeIndex += 1
pub.sendMessage("update", self.gaugeIndex)
if self.gaugeIndex == 10:
self.timer.Stop()
def update(self, msg):
self.gauge.SetValue(msg.data)
def OnClick(self, evt):
self.timer.Start(1000)
if __name__ == '__main__':
app = wx.App(redirect=False)
Window()
app.MainLoop()

Resources