How to synchronize wxpython Gauge widget with threads in python? - python-2.7

My python project has multiple threads running. I want to add a gauge widget from the wxpython library to show the progress. I want the gauge to fill until my first thread completes. How do I achieve this? I am using Python 2.7 on Windows.

Use wx.CallAfter
def add_to_the_gauge(value):
your_gauge.Value += value
...
#in some thread
def some_thread_running():
wx.CallAfter(add_to_the_gauge, 2)

You need to post events from your worker thread to the main thread asking it to update the gauge, see this overview.

You can use some simple things
remember to import the module:
import os
and then put this on the frame
def __init__(self, *a, **k):
filename = 'game.exe'
self.timer = wx.Timer()
self.Bind(wx.EVT_TIMER, self.OnUpdateGauge, self.timer)
self.timer.Start()
self.proc = os.popen(filename) # start the process
def OnUpdateGauge(self, event):
if self.proc.poll() == None: # return None if still running and 0 if stopped
self.timer.Stop()
return
else:
'''do something with the gauge'''
hope those help

Related

Terminate All QThreads on GUI Close

I have a PyQT gui that has one gui thread, and 24 "tester threads." They work fine, but still seem to stay up when I close the gui. How can I gracefully close the threads to avoid python crashing?
#!/usr/bin/python
# Standard Lib
from datetime import datetime
import logging
import os
import random
import sys
import time
# Third Party
from PyQt4 import QtGui, QtCore
# Local Kung Fu
stuff
class SSITesterThread(QtCore.QThread):
# vars for updating gui using signals
updateText = QtCore.pyqtSignal(str)
updateColor = QtCore.pyqtSignal(str)
updateSN = QtCore.pyqtSignal(str)
def __init__(self, thread_number, port, path, parent=None):
super(SSITesterThread, self).__init__(parent)
self.delay = random.random()
def run(self):
self.ssitester()
def ssitester(self):
# stuff
class SSITestSuiteGUI(QtGui.QMainWindow):
def __init__(self, parent=None):
self._threads = []
QtGui.QWidget.__init__(self, parent)
# Init class from template and paths
self.launch_tester_threads()
def init_gui_nodes(self, com_ports_list):
for num, port, in zip(range(1, 25), range(0, 24)):
label = getattr(self.ui, 'com_{}'.format(num))
label.setText("COM Port: {}".format(com_ports_list[port]["COM"]))
def launch_tester_threads(self):
logging.info("Spinning up threads...")
for num, com_port_chunk in zip(range(1, 25), self.com_ports_list):
tester_thread = SSITesterThread(thread_number=num, port=com_port_chunk["COM"], path=self.vc_test_path)
# get a reference to the associated textbox somehow...
status_box = getattr(self.ui, 'status_{}'.format(num))
tester_thread.updateText.connect(status_box.setText)
status_box = getattr(self.ui, 'status_{}'.format(num))
tester_thread.updateColor.connect(status_box.setStyleSheet)
sn_label = getattr(self.ui, 'sn_{}'.format(num))
tester_thread.updateSN.connect(sn_label.setText)
sn_label.setText("S/N: None")
tester_thread.start()
self._threads.append(tester_thread)
logging.info("Ready for tests.")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
test_suite = SSITestSuiteGUI()
test_suite.show()
# Close app only when window is closed.
sys.exit(app.exec_())
I tried implementing this solution: https://gist.githubusercontent.com/metalman/10721983/raw/15c6f115f9918fee7c1b88d0a549d4cc59a5b346/qapplication_about_to_quit_signal.py
But got an error:
attributeerror: 'function' object has no attribute '__pyqtSignature__'
Thanks for your time.
UPDATE:
Added the suggestion below as:
#QtCore.pyqtSlot()
def stop(self):
return
in my SSITesterThread Class, and it errors out when various "emits" I've used as singals in the thread sudden try to access NoneType objects:
File in gui.py, line 75 in tester,
self.updateColor.emit("{}".format(thread_colors.green_alert)
AttributeError: "NoneType" object has no attribute 'green alert'
Did the fix work, and this is a new problem? Because it seems like things still aren't shutting down gracefully.
It looks like you're importing your GUI from a form generated by QTDesigner. Try this:
self.ui.closeEvent = self.closeEvent
I believe your problem is that you're editing the wrong QMainWindow instance in the wrong place.
You probably just need to decorate the stop method as a slot.
#QtCore.pyqtSlot()
def stop(self):
# do thread cleanup, stop thread

Threading in wxpython

Hi I'm new to python and have started developing a GUI using wxpython. I just want to know the basic threading operation in wxpython. I have a main process which has OK & Cancel Buttons and a child process derived from main with the OK and Cancel Buttons. I want to implement threading such a way that clicking OK button on main process must send a message to child process and child process must look for if any of it's process is running, if so, it has to wait for that process to finish and then receive the message from main process.Similarly the same with the Cancel button in the main process.
Basically i want to see how child process receives message from main process and both work parallel.
I'm trying with wx.CallAfter and wx.PostEvent and i'm confused with the threading concept here. Please someone help me.
Thanks in advance
Multithreading in wxpython is not different than in python. wx.CallAfter and threading example shows how you use both. wx.CallAfter waits for the event to be completed and run the handler in the main thread. Additionally, you can use timers (wx.Timer) to check child processes and send/receive data.
Here is a link for wx.PostEvent showing how to use it. In this example, you create custom event, bind it to a handler in main thread. After that you post the event in a worker thread and attach some data as well. Your event handler receives the event and do some stuff in the main thread.
So, there are important questions arising when you use multithreading
When to start the child threads,
Which communication mechanism is used between threads,
How to stop the threads in a safe way when necessary,
etc.
I would prefer wx.Timer + Queue module. I can regularly check the queue with timers or with user events and send something (i.e. None) through queue to stop threads.
Note: Long running jobs in main thread can make GUI unresponsive.
I got it right. THanks to #ozy
import threading
import wx
from threading import Thread
ID_RUN = 101
ID_RUN2 = 102
class ChildThread(Thread):
def __init__(self, myframe):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.myframe = myframe
def run(self):
wx.CallAfter(self.myframe.AfterRun, 'Ok button pressed')
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title)
panel = wx.Panel(self, -1)
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
mainSizer.Add(wx.Button(panel, ID_RUN, "OK"))
mainSizer.Add(wx.Button(panel, ID_RUN2, "Cancel"))
panel.SetSizer(mainSizer)
mainSizer.Fit(self)
wx.EVT_BUTTON(self, ID_RUN, self.onRun)
wx.EVT_BUTTON(self, ID_RUN2, self.onRun2)
def onRun(self, event):
self.child = ChildThread(myframe=self)
self.child.daemon = True
self.child.start()
def onRun2(self, event):
self.child2 = threading.Thread(None, self.__run)
self.child2.daemon = True
self.child2.start()
def __run(self):
wx.CallAfter(self.AfterRun, "Cancel button pressed")
def AfterRun(self, msg):
dlg = wx.MessageDialog(self, msg, "Message", wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "My GUI")
frame.Show(True)
frame.Centre()
return True
app = MyApp(0)
app.MainLoop()

Why won't my wxFrame close properly?

I am trying to build a basic messaging system, but I have hit a major roadblock in the process. I can't get the window to close without it not responding and me having to close it in the Task Manager. From what I've read online, it sounds like I need to close when a sys.exit(0) to exit all the threads and connections. I have been stuck on this problem for days so I would really appreciate an answer and explanation to why it doesn't work! The meat of the problem is in the close_window() function. It is run able provided you have a basic server that accepts a connection. Thanks!
import wx
import socket
import threading
import sys
class oranges(wx.Frame):
def __init__(self,parent,id):
##Unimportant stuff
wx.Frame.__init__(self,parent,id," Retro Message",size=(500,500))
self.frame=wx.Panel(self)
self.input_box=wx.TextCtrl(self.frame, -1,pos=(15,350),size=(455,120),style=wx.NO_BORDER| wx.TE_MULTILINE)
self.messaging_box=wx.TextCtrl(self.frame, -1,pos=(15,15),size=(455,285),style=wx.NO_BORDER | wx.TE_MULTILINE|wx.TE_READONLY)
send_button=wx.Button(self.frame,label="Send",pos=(350,315),size=(75,40))
self.Bind(wx.EVT_BUTTON, self.sender,send_button)
self.Bind(wx.EVT_CLOSE, self.close_window)
self.counter = 1
self.socket_connect = socket.socket()
self.setup()
def sender(self,event):
self.socket_connect.send(self.input_box.GetValue())
self.input_box.Clear()
self.Refresh()
##Important stuff
def close_window(self,event): #This is the function in question#
self.counter = 0
self.socket_connect.shutdown(socket.SHUT_RDWR)
sys.exit(0)
def setup(self):
self.ip_enter = wx.TextEntryDialog(None, "Enter in the IP:", "Setup", "192.168.1.1")
if self.ip_enter.ShowModal() ==wx.ID_OK:
self.offical_ip = self.ip_enter.GetValue()
try:
self.socket_connect.connect((self.offical_ip,5003))
self.username = "false" #Tells the server just to give the user a IP username
self.Thread1 = threading.Thread(target = self.listening_connect)
self.Thread1.start()
except socket.error:
self.error_connect()
else:
sys.exit(0)
def listening_connect(self):
self.socket_connect.send(self.username)
while self.counter != 0:
data = self.socket_connect.recv(1024)
self.messaging_box.AppendText(data)
self.Refresh()
if not data:
break
self.socket_connect.close()
def error_connect(self):
pop_ups = wx.MessageDialog(None, "Failed to Connect to Server!", 'Error', wx.OK)
pop_ups.ShowModal()
self.setup()
if __name__=="__main__":
app=wx.App(False)
window=oranges(parent=None,id=-1)
window.Show()
app.MainLoop()
Here is a basic server program that should work with it(I am unable to test it but it is very similar to the real one)
import socket
HOST = '192.168.1.1'
PORT=5003
s = socket.socket()
s.bind((HOST, PORT))
s.listen(1)
c,addr = s.accept()
while True:
data = c.recv(1024)
if not data:
break
c.close()
You need to wait for the thread to end. Otherwise it's probably going to make the script hang. Why? Well the thread is separate from the GUI thread, so it doesn't get killed just because you closed down your wxPython application. Here is what I would recommend:
def close_window(self, event):
self.Thread1.join()
self.Destroy()
This makes the script wait for the thread to finish before closing the application. If you want the frame to disappear, then you should call self.Hide() before the join. Another method would be to put some logic in your thread where you can send it a message that tells it the application is shutting down, so the thread needs to abort.
You should probably check out the following Stack answer:
Is there any way to kill a Thread in Python?

wxPython: How do you start a wxPython application from a wxPython application?

I am attempting to start a wxPython application but I want a banner to be displayed before it is started.
One way to do this is to start a wxPython application which in turn starts another wxPython application, the reason for doing it this way is since the App part of the second wxPython application needs to do some processing before starting and may take some time.
The issue is how do I start the other application and know that it has started?
Currently I do this which blocks for the entire GUI session:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
I have attempted to do the following but the frame of the first application does not seem to close:
loadCompleted, EVT_LOAD_COMPLETED = wx.lib.newevent.NewEvent()
class MyRegion(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="My Region")
self.label = wx.StaticText(self, label="Hello, World!")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.label, 0, wx.ALL, 5)
self.SetSizer(sizer)
self.startThread = Thread(target=self.Start)
self.startThread.start()
self.Bind(EVT_LOAD_COMPLETED, self.OnClose)
def OnClose(self, result):
self.Close()
def Start(self):
try:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
except:
pass
wx.PostEvent(self, loadCompleted(result=(None)))
if __name__ == "__main__":
app = wx.App()
frame = MyRegion(None)
frame.Show()
app.MainLoop()
I don't think starting a second wxPython application is the way to go. Instead, I would just load the banner inside your frame's __init__ method, then do your processing. When the processing finishes, you can destroy the banner and show your main app. Here's some psuedo-code:
#
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
banner = MyBanner()
# do a bunch of stuff
banner.Destroy() # or banner.Close()
self.Show()
Now if the processing takes a really long time, you can put that into a thread and have the thread send a message back to the UI that tells it that the thread is finished. When the app receives the message, it can close the banner in the handler and show the App at that point. Please note that you need to use a thread-safe method, such as wx.CallAfter or wx.PostEvent.
Check out the following articles for ideas:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/

gobject.io_add_watch continuous callback from pyalsaaudio

I'm trying to create a small custom mixer that suits my needs, using python 2.7 and pyalsaaudio 0.7, but I'm stuck with events got from alsamixer when another program changes the volume values. I tried to understand how other mixers work (for example volti) and, as far as I understand, it should work as expected, but even if the method is similar, I still get a continuous loop of event response from io_add_watch. So, I suppose that I don't understand how io_add_watch works yet.
This is a small version of the code:
class MyMixer(gtk.Window):
def __init__(self):
super(MyMixer, self).__init__()
self.m = alsaaudio.Mixer(control='Headphone', id=1, cardindex=0)
""" here go the mixer widgets """
self.show_all()
fd, event = self.m.polldescriptors()[0]
self.watch = gobject.io_add_watch(fd, event, self.update)
def update(self, *args):
print 'changed'
""" here I update mixer widgets """
return True
mixer = MyMixer()
gtk.main()
What am I getting wrong?
When you get an event from the poll descriptors, you must call snd_mixer_handle_events().
pyalsaaudio has no mechanism for that.