I'm new at phyton programming and developing gui interface has gtk framework and serial port. It has a treeview whose liststore model. I could the insert new rows easily.
I'm using the serialport recive callback in different thread from main gui thread avoid to not miss any data. After the received a new data, it should update the treeview. However, since the serialport is in different thread, I don't know how to update the list. Please help me to do this.
the gui class:
class MainGUI():
def __init__(self):
self.builder = Gtk.Builder()
self.builder.add_from_file("main.glade")
self.builder.connect_signals(MainGUI)
self.window = self.builder.get_object("window1")
self.mycombobox = self.builder.get_object('comboboxtext1')
self.toggle = self.builder.get_object('togglebutton1')
self.table = self.builder.get_object('treeview2')
self.list = self.builder.get_object('liststore1')
self.scroll_window = self.builder.get_object('scrolledwindow1')
def show(self):
print("App main thread number", format(threading.get_ident()))
self.window.show()
Gtk.main()
#staticmethod
def connect_toggled(_self):
if main.toggle.get_active():
main.toggle.set_label("Disconnect")
serial_port.connect(main.mycombobox.get_active_text())
t3 = threading.Thread(target=serial_port.read_from_port)
t3.start()
serial_port.disconnect()
def row_inserted_event(self, path, iter):
"""The actual scrolling method"""
adj = main.scroll_window.get_vadjustment()
adj.set_value(adj.get_upper() - adj.get_page_size())
def update_table(self):
# for i in range(256):
# main.list.append(['aaa', 'ddds', i])
# if len(main.list) > 50:
# main.list.remove(main.list.get_iter(0))
main.list.append(['aaa', 'ddds', 0])
if len(main.list) > 50:
main.list.remove(main.list.get_iter(0))
print(len(main.list))
if __name__ == "__main__":
serial_port = SerialPort()
ports = SerialPort().list_ports()
main = MainGUI()
for port in ports:
main.mycombobox.append_text(port)
main.mycombobox.set_active(0)
main.toggle.set_label("Connect")
main.update_table()
main.show()
the serial port class:
class SerialPort:
def __init__(self):
self.ser = serial.Serial()
self.baud_rate = 115200
def write(self, data):
self.ser.write(bytes(data))
print(data)
def connect(self, port):
print("serial port thread number = %d" % (threading.get_ident()))
print("connected the port = %s" % (port))
self.ser.port = port
self.ser.baudrate = self.baud_rate
self.ser.timeout = 0
if self.ser.isOpen():
print("already connected this port = %s" % (port))
else:
self.ser.open()
def disconnect(self):
if self.ser.isOpen():
self.ser.close()
print("disconnected port")
def read_from_port(self):
while True:
if self.ser.isOpen():
reading = self.ser.readline()
if len(reading) > 0:
self.received_callback(reading)
time.sleep(0.1)
def received_callback(self, data):
print(data)
def list_ports(self):
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
# ports = glob.glob('/dev/tty[A-Za-z]*')
ports = ['/dev/pts/%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
I believe that your problem is more related to threading + GUI, than GTK.
As far as I know, when you modify the liststore that is the model for the treeview, the latter should be updated instantly. So, there should be no problem there.
A fundamental principle when working with threads and a GUI, is that you should only update the GUI from within its own thread (main loop). So what you need to do, is have your worker thread (serial port connection thread) send the update to the main GUI thread and let it update the treeview. The update can be scheduled with the GLib.idle_add function to let GTK do it when most convenient.
Now, to communicate between threads, you could use the queue module.
I don't quite understand your code. So I'll write a simple example (using gtk3 PyGObject, since you didn't specify).
import threading
import queue
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GLib', '2.0')
from gi.repository import Gtk, GLib
def do_work(com_queue):
# do some work
com_queue.put("update for your treeview")
# continue
class MainGUI(object):
def __init__(self):
self.com_queue = queue.Queue()
self.worker_thread = None
self.liststore = None
# more gui initialization...
def launch_worker_thread(self):
self.worker_thread = threading.Thread(target=do_work, args=(self.com_queue,))
self.worker_thread.start()
Glib.timeout_add(1000, self.check_queue) # run check_queue every 1 second
def check_queue(self):
if self.worker_thread.is_alive():
try:
update = self.com_queue.get()
GLib.idle_add(self.update_treeview, (update,)) # send tuple
except queue.Empty:
pass
return True # to keep timeout running
else:
return False # to end timeout
def update_treeview(self, update):
self.liststore.append(update) # here update the treeview model with tuple
if __name__ == "__main__":
gui = MainGUI()
Gtk.main()
I hope this helps.
Related
I am building a local Flask web interface (not intended for more than one user) to control a Motor Driver from a Raspberry Pi. I found a library called Thorpy (https://github.com/UniNE-CHYN/thorpy) to control the Motor Driver and its working well with a test script.
Here is the Port class (cleaned from the irrelevant parts) to create the serial connection with the Motor Driver.
Thorpy : port.py
class Port:
#List to make "quasi-singletons"
static_port_list = weakref.WeakValueDictionary()
static_port_list_lock = threading.RLock()
def __init__(self, port, sn):
super().__init__()
self._lock = threading.RLock()
self._lock.acquire()
self._buffer = b''
self._unhandled_messages = queue.Queue()
print("Port : serial")
self._serial = serial.Serial(port,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
#write_timeout = 0.1,#rajout
rtscts=True)
self._port = port
self._debug = True
self._lock.release()
self.daemon = False
print("Constructed: {0!r}".format(self))
self._thread_main = threading.current_thread()
self._thread_worker_initialized = threading.Event()
self._thread_worker = threading.Thread(target = Port.run, args = (weakref.proxy(self), ))
self._thread_worker.daemon = True
self._thread_worker.start()
self._thread_worker_initialized.wait()
print("Port : fin init")
print("")
time.sleep(5)
#staticmethod
def run(self):
try:
self._continue = True
timeout = 1
self._thread_worker_initialized.set()
while self._thread_main.is_alive():
print("run run")
#Trick to avoid holding lock
r, w, e = select.select([self._serial], [], [], timeout)
msg = self._recv_message(False)
if msg is not None:
# print("RUN :", msg)
message_handled = self._handle_message(msg)
if not message_handled:
print("Unhandled message", msg)
self._unhandled_messages.put(msg)
self._serial.close()
except ReferenceError:
print("EXCEPT : STOP RUN")
pass #Object deleted
And now here the Flask server (relevant parts only) :
webapp.py
from flask import *
from flask_socketio import SocketIO
from thorpy.comm.port import Port
app = Flask(__name__)
regul = Regul()
socketio = SocketIO(app)
#app.route('/pompe')
def page_pompe():
return render_template('pompe.html')
#socketio.on('drv_connect')
def on_connect(state):
serial_ports = [(x[0], x[1], dict(y.split('=', 1) for y in x[2].split(' ') if '=' in y)) for x in comports()]
for dev in usb.core.find(find_all=True, custom_match= lambda x: x.bDeviceClass != 9):
try:
port_candidates = [x[0] for x in serial_ports if x[2].get('SER', None) == dev.serial_number]
assert len(port_candidates) == 1
p = Port.create(port, dev.serial_number)
except :
pass
if __name__ == '__main__':
socketio.run(app, debug=True, host='0.0.0.0', port=5000)
When a button on the web page call the on_connect() function, the Port object is created and is stuck in the while loop of its "run" function (printing "run run" every couple of seconds), while the flask server hangs.
I suspect it has something to do with the part creating the thread inside the init of the Port class, but I have always used threading very basically and I don't fully understand it...
What I tried
I tried to create a thread (from the Thread library) inside the Flask app to call the creation of the Port object (and encapsulate its inner "run" thread) but it makes no difference.
If I create a Process instead of a thread, the Flask app stays alive, but I cannot have access to the port object from other functions (and cannot send orders the the Motor Driver).
currently I'm working on a Server-Client Application with python.
The Server-Client is working. My problem is the GUI.
I'm trying to do this with GTK.
Since 2 weeks I'm testing around but the login-screen wont work.
So here are my questions:
How can I create and connect multiple windows in GTK? e.g first login-screen then main_window
How can a Text_Entry-Dialog be created (all in PyGTK)?
[Edit-03.01.2016] Code:
#!/usr/bin/python3
# coding=utf8
import socket
from gi.repository import GObject, Gio, Gtk
class MyApplication(Gtk.Application):
# Main initialization routine
def __init__(self, application_id, flags):
Gtk.Application.__init__(self, application_id=application_id, flags=flags)
self.connect("activate", self.new_window)
#main_Window(self)
def new_window(self, *args):
self. snc =start_Connection_Window(self)
print("going on")
def main_Window(self):
print "Here I'm "
self.connect("activate", self.new_MainWindow)
self.snc.close()
def new_MainWindow(self):
main_Window(self)
print "main_Window started"
class start_Connection_Window(Gtk.Window):
def __init__(self, application):
self.Application = application
# Read GUI from file and retrieve objects from Gtk.Builder
try:
GtkBuilder = Gtk.Builder.new_from_file()#Some Glade file with two windows
GtkBuilder.connect_signals(self)
except GObject.GError:
print("Error reading GUI file")
raise
# Fire up the main window
self.start_Connection_Window = GtkBuilder.get_object("start_Connection")
self.start_Connection_Window.set_application(application)
self.ServerIP_Input = GtkBuilder.get_object("ServerIP-Input")
self.Username_Input = GtkBuilder.get_object("Username_Input")
self.Password_Input = GtkBuilder.get_object("Password_Input")
self.start_Connection_Window.show()
def on_btn_Connect_clicked(self, button):
button.set_sensitive(False)
try:
self.host = str(self.ServerIP_Input.get_text())
self.username = str(self.Username_Input)
self.password = str(self.Password_Input)
self.port=1317
self.clientsocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientsocket.connect((self.host, self.port))
self.LENGTH_SIZE = 4
data='ping'
lenght = str(len(data))
self.clientsocket.send(lenght.zfill(4))
print data
self.clientsocket.send(data)
except:
print "Error!"
button.set_sensitive(True)
self.Application.main_Window()
try:
lenght = self.clientsocket.recv(self.LENGTH_SIZE)
data = self.clientsocket.recv(int(lenght))
print data
if(data == str("ok")):
try:
self.Application.main_Window( )
self.close()
except :
print "Here is the mistake"
else:
print "Failed!"
button.set_sensitive(True)
except:
print "Fail!"
#print "Would Start Conn"
def close(self, *args):
self.start_Connection_Window.destroy()
def on_MainWindow_destroy(self, window):
#self.Application.main_Window()
print "Bye"
def on_Window_destroy(self):
print("Bye aus dem destroyten!")
class main_Window(Gtk.Window):
def __init__(self, application):
self.Application = application
# Read GUI from file and retrieve objects from Gtk.Builder
try:
GtkBuilder = Gtk.Builder.new_from_file()#someGladeFile with two Windows
GtkBuilder.connect_signals(self)
except GObject.GError:
print("Error reading GUI file")
raise
# Fire up the main window
self.MainWindow = GtkBuilder.get_object("main_Window")
self.MainWindow.set_application(application)
self.MainWindow.show()
def on_btn_Connect_clicked(self, button):
print "Would Start Conn"
def close(self, *args):
self.MainWindow.destroy()
def on_MainWindow_destroy(self, window):
#self.Application.new_window()
print "Bye"
def on_Window_destroy(self, window):
#self.Application.new_window()
print "Bye"
def start(self, socket, host, username, password):
self.SClient = SC(host, username, password, self)
self.MainWindow.show()
# Starter
def main():
# Initialize GTK Application
Application = MyApplication("App", Gio.ApplicationFlags.FLAGS_NONE)
print "starting"
# Start GUI
Application.run()
if __name__ == "__main__": main()
I am thinking about the proper way to open and read a file whose data needs to be used in a thread. It is a GUI-based application for reading and writing to a serial port in a thread. This works perfectly right now.
Between reading, I also want to read from the file and write it to the port at a specific time. So far the structure when starting the thread is:
Open port
run method
Inside the run loop:
read time
read from file
write to port
if something in the buffer, read it
Finally, if stopped by the button:
close port
terminate thread
Now the question is that I am not sure how to structure and implement this file handling. Should I make a separate class for the file handler with methods I need, or just simply open the file and read it in the run() loop in the thread? If so, it does not seems to me very Pythonic or OOP, and I really want to code it in the right way to be able to maintain it later.
I am appending a quite simplified piece of code for better understanding. I removed some unimportant stuff, but there is probably still some left. You are very welcome to criticize it, since I am still a beginner.
class COMThread(QtCore.QThread):
def __init__(self, parent, *args, **kwargs):
QtCore.QThread.__init__(self, parent)
self.myInit (*args, **kwargs)
self._stop = False # flag to stop thread
self.parent = parent
try:
self.baudrate = int(self.baud)
self.port = str(self.port)
self.ser = serial.Serial(self.port, self.baudrate, timeout=0, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, xonxoff=False, rtscts=False, dsrdtr=False)
except serial.SerialException:
logger.error('Cannot open port', exc_info=True)
self._stop = True
else:
self.parent.comPortLabel.setText(u'Connected to port ' + str(self.port))
self.parent.comConnectButton.setText("Disconnect")
logger.info('Com Connected to port '+ str(self.port))
self.connect(self.parent.comPollButton, QtCore.SIGNAL("clicked()"), self.toggleComCustomLine)
self.connect(self.parent.comFreqSetButton, QtCore.SIGNAL("clicked()"), self.toggleComPollFreq)
self.connect(self.parent.comCustomCheckBox, QtCore.SIGNAL("stateChanged(int)"), self.toggleComCustomCheck)
self.connect(self, QtCore.SIGNAL('comSpeed(int)'), self.comWSlcd, QtCore.SLOT('display(int)'))
self.connect(self, QtCore.SIGNAL('comAngle(int)'), self.comWAlcd, QtCore.SLOT('display(int)'))
def myInit(self, port, baud):
self.port = port
self.baud = baud
def run (self): # class which is automatically called after __init__
self.txQueue = collections.deque(maxlen=10)
self.readyToSend = True
self.messageData = ''
self.txData = ''
self.newData = ''
self.time_start = 0
self.time_delta_s = 0
self.time_delta_ms = 0
self.speed = 0
self.angle = 0
self.poll_msg = collections.deque()
self.poll_msg_t = collections.deque()
# ------------------ THREAD STARTED ---------------------------------
logger.info('COM port com thread started...')
self.time_start = time.time() # initial time
while self._stop == False:
self.time_delta_ms, self.time_delta_s = math.modf(time.time() - self.time_start)
self.time_delta_ms = int(self.time_delta_ms*1000)
# prepare data based on timing
# READ FILE
# put data to queue
self.txQueue.append(self.messageData)
self.poll_msg.rotate(-1)
self.poll_msg_t.rotate(-1)
self.response_time_start = time.time()
# flush out queue and send everything
if self.readyToSend and len(self.txQueue):
while len(self.txQueue):
self.txData = self.txQueue.popleft()
try:
n = self.ser.write(self.txData)
self.ser.flush()
except serial.SerialException:
logger.error("Com Could not write to serial port")
#-------------------------------------------
time.sleep(0.001)
n = self.ser.inWaiting() # check if something in serial buffer
if n:
self.readyToSend = False
try:
self.newData = self.newData + self.ser.read(n)
logger.debug("S: "+str(self.time_delta_s)+ " ms: "+ str(self.time_delta_ms))
except serial.SerialException:
logger.error('Com Worker cannot read Serial Port !!!')
else:
n = 0
# Process received data
else:
self.readyToSend = True
self.ser.close()
logger.info('Com COM port thread stopped...jump out of loop')
self.parent.comConnectButton.setText("Connect")
self.parent.comPortLabel.setText("Disconnected")
def toggleStop(self):
self._stop = True
logger.info("Com Data worker stopped by button")
I can't find a way to make my threads persistent between the first and second call of my script.
So far, when I run python script_1.py A the script runs the if option == 'A' block and starts the thread. Then, the script exits and the thread is cleaned up. So, when I run python script_1.py B the isAlive attribute can't be used.
is there any way to keep persistence?
The code for script_1.py is:
from script_2 import imp
script_2 = imp()
if option == 'A':
script_2.start()
elif option == 'B':
script_2.stop()
and for script_2.py is:
from threading import Thread
class workerThread(Thread):
def __init__(self, _parent):
Thread.__init__(self)
self.parent = _parent
self.active = False
def run(self):
while(self.active == False):
print 'I am here'
print 'and now I am here'
class imp():
def __init__(self):
self.threadObj = None
def start(self):
self.threadObj = workerThread(self)
self.threadObj.start()
def stop(self):
if self.threadObj.isAlive() == True:
print 'it is alive'
A solution would be:
from threading import Thread
from socket import *
from time import sleep
class workerThread(Thread):
def __init__(self):
Thread.__init__(self)
self.sock = socket()
self.sock.bind(('', 9866))
self.sock.listen(4)
self.start()
def run(self):
while 1:
ns, na = self.sock.accept()
if ns.recv(8192) in (b'quit', 'quit'):
ns.close()
break
self.sock.close()
print('Worker died')
imp = workerThread()
And the first script:
if option == 'A':
from time import sleep
from script_2 import imp
while 1:
sleep(0.1)
elif option == 'B':
from socket import *
s = socket()
s.connect(('127.0.0.1', 9866))
s.send('quit') # b'quit' if you're using Python3
s.close()
It's not even close to elegant, but it's a 5min mockup of what you could do.
To make this closer to useable code, I'd go with:
self.sock = fromfd('/path/to/socket', AF_UNIX, SOCK_DGRAM)
and register it with an ePoll object within the worker thread.
import select
self.watch = select.epoll()
self.watch.register(self.sock.fileno(), select.EPOLLIN)
while 1:
for fd, event in self.watch.poll(0.1):
if fd == self.sock.fileno() and event == select.EPOLLIN:
ns, na = self.sock.accept()
# store socket and register it
elif event == select.EPOLLIN:
data = storedSockets[fd].recv(8192)
# and do work on it
Anyway, but you will need to keep the first instance of your execution running and create some form of communication method for the second instance you start up, i used sockets as an example which i think is rather good, especially in conjunction with unix sockets and epoll because the speed is fantastisc. You can also use memcache
I am doing simple gui for continuous reading data stream from serial COM port and display the results. I could not get up and running queue to push the data from reading thread to main (GUI) part. I think I am missing some part but cannot figure out what.
I can easily start thread and read data(printing them after) from COM port but it seems nothing is going to queue becase when I am printing output in GUI part is empty. Beside that reading and main thread is running ok.
Do you have an idea how to check if is something in the queue?
Here is part of the code if you could check it.
And also sorry for messed up indention :-P
Thank you
class MyGUI(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.queueData = Queue.Queue() # create queue
self.initialize()
def initialize(self):
"""Create the GUI"""
self.grid()
...gui code here...
def processQueueData(self):
try:
rawData = self.queueData.get()
except Queue.Empty:
pass
else:
print 'GUI:'+ rawData
#show result of the task
finally:
self.queueDataID = self.after(50, self.processQueueData)
And reading thread is here:
class MyProcess(threading.Thread):
def __init__(self, parent, queueData): #def __init__(self, startValue)
threading.Thread.__init__(self)
self._stop = False
self.parent = parent
self.queueData = queueData
try:
port = app.boxPort.get()
self.ser = serial.Serial(port, baudrate=19200, timeout=0, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE)
except serial.SerialException:
print ('Cannot open port')
self.stop()
else:
app.labelConStatus.set(u'Connected to ' + port)
def stop(self):
self._stop = True
try:
self.ser.close()
except serial.SerialException:
print ('Cannot close port')
else:
app.labelConStatus.set(u'Disconnected')
def run (self):
while self._stop == False:
data = self.ser.read(1)
n = self.ser.inWaiting()
if n:
data = data + self.ser.read(n)
self.queueData.put(data)
# print 'PORT:'+data