Accessing the Gdk area after import Gtk and closing a window - python

I found myself in the situation where I needed to close a window which I had subclassed from Gtk.Window. I was connecting the window.destroy event to close the window initially to Gtk.main_quit for testing, but when I loaded the module into my main program, I couldn't do it that way, of course, without closing the main program. When I wrote a function, I stupidly called the window.destroy() method which, stupid of me, resulted in a max recursion error. So, I decided to connect a 'delete-event' and then call the window.destroy() which still recursed because (as I learned) a destroy event calls delete-event. So, I connected the destroy event to a function and tried to call:
window.emit('delete-event')
which worked, but resulted in an error wanting another parameter. Researching, I learned it needed to be something like this from gtk2:
window.emit("delete-event", gtk.gdk.Event(gtk.gdk.DELETE))
I can't find Gdk under Gtk like I can find gdk under gtk. I don't like this. I imported Gdk and Gdk.Event is found, but no equivalent to the gtk.gdk.DELETE variable. I did a:
l = dir(Gdk)
for i in l:
if 'DELETE' in i:
print(i)
and came up with no DELETE variable defined in Gdk.
How can we access Gdk from Gtk? If not, how do we translate these gtk.gdk... things when they don't exist under Gdk?
Ultimately, I just did:
del window
to do what I needed to do, but I still would like to know how to do the emit signal above in Gtk3 for learning purposes.
Thanks,
Narnie

There is no gtk.gdk in GTK 3, all of that functionality, or its equivalent, must be available using from gi.repository import Gdk. The previous gtk.gdk hierarchy was actually for convenience, but didn't make any sense because GDK is a completely independent package and is not part of GTK. The new organization makes more sense because the hierarchy and variable names are exactly the same as in the C, Javascript, etc. APIs.
In this case, gtk.gdk.DELETE is available as Gdk.EventType.DELETE, so you should be able to do
window.emit('delete-event', Gdk.Event(Gdk.EventType.DELETE))
However, you are not supposed to emit event signals yourself, so it would be better to do:
window.event(Gdk.Event(Gdk.EventType.DELETE))
Or even better, don't connect to the event at all in your module. Then you can use window.destroy() without a recursion error. Connect to the destroy signal in your main program, then you can simply call Gtk.main_quit.

Related

Is it possible to use PyQt5 QtSerialPort without GUI's or classes?

The point would be to have the functionality of reading only when there is something to be read, instead of using pyserial which doesn't have a special method for that. I guess this may go into a bigger question of whether signals and slots could be used without a GUI classes (that inherit from other objects). I could get the serial port to write, but not read, with
from PyQt5 import QtCore, QtSerialPort
serial_port = QtSerialPort.QSerialPort('COM3')
serial_port.open(QtCore.QIODevice.ReadWrite)
serial_port.write(bytes([255]))
def handle_ready_read():
while serial_port.canReadLine():
print(serial_port.readAll())
print('here')
serial_port.close()
serial_port.readyRead.connect(handle_ready_read)
Nothing prints out, even though something is read when using pyserial.
You do not need GUI to use Qt. There is a dedicated GUI module in Qt and what does not depend on it, doesn't need a GUI.
However, to use slots and signals you need to run Qt's event loop. The regular way would be to use a QCoreApplication.
app = QCoreApplication([])
# setup your serial port here
sys.exit(app.exec_())

Capturing stdout from an imported module in wxpython and sending it to a textctrl, without blocking the GUI

There are alot of very similar questions to this but I can't find one that applies specifically to what I'm trying to do.
I have a simulation (written in SimPy) that I'm writing a GUI for, the main output of the simulation is text - to the console from 'print' statements. Now, I thought the simplest way would be to create a seperate module GUI.py, and import my simulation program into it:
import osi_model
I want all the print statements to be captured by the GUI and appear inside a Textctrl, which there's countless examples of on here, along these lines:
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
<general frame initialisation stuff..>
redir=RedirectText(self.txtCtrl_1)
sys.stdout=redir
class RedirectText:
def __init__(self,aWxTextCtrl):
self.out=aWxTextCtrl
def write(self,string):
self.out.WriteText(string)
I am also starting my simulation from a 'Go' button:
def go_btn_click(self, event):
print 'GO'
self.RT = threading.Thread(target=osi_model.RunThis())
self.RT.start()
This all works fine, and the output from the simulation module is captured by the TextCtrl, except the GUI locks up and becomes unresponsive - I still need it to be accessible (at the very minimum to have a 'Stop' button). I'm not sure if this is a botched attempt at creating a new thread that I've done here, but I assume a new thread will be needed at some stage in this process.
People suggest using wx.CallAfter, but I'm not sure how to go about this considering the imported module doesn't know about wx, and also I can't realistically go through the entire simulation architecture and change all the print statements to wx.CallAfter, and any attempt to capture the shell from inside the imported simulation program leads to the program crashing.
Does anybody have any ideas about how I can best achieve this? So all I really need is for all console text to be captured by a TextCtrl while the GUI remains responsive, and all text is solely coming from an imported module.
(Also, secondary question regarding a Stop button - is it bad form to just kill the simulation thread?).
Thanks,
Duncan
I would suggest looking into this WX wiki article about long running tasks.
It specifically addresses the situation you're dealing with using a "start" button to being a long running process. Several different examples are given using different techniques like threads and idle handlers.
I think you would have to redirect stdout to a log file (or simple SQLite database?) and then use your thread to check the log file for updates which it would then pass along to the GUI using wx.CallAfter or similar. You might be able to use something like a socket server built in Python: http://wiki.wxpython.org/AsynchronousSockets. I think the wxPython Cookbook mentioned something about using an RPC server too (probably this one: http://docs.python.org/library/simplexmlrpcserver.html), but I don't remember the details.
You should also try asking on the official wxPython mailing list. They're very friendly over there.

Threaded Tkinter script crashes when creating the second Toplevel widget

I have a Python script which uses Tkinter for the GUI. My little script should create a Toplevel widget every X seconds. When I run my code, the first Toplevel widget is created successfully, but when it tries to create a second one the program crashes.
What I am doing is using the after method to call the function startCounting every 5 seconds alongside root's mainloop. Every time this function is called, I append a Toplevel widget object into a list and start a new thread which hopefully will be running the new mainloop.
I would be very grateful if someone could figure this problem out. By the way, this is just a little script that I am currently using to solve my problem, which is preventing me from going on with my real school project.
The code:
import threading,thread
from Tkinter import *
def startCounting():
global root
global topLevelList
global classInstance
topLevelList.append (Toplevel())
topLevelList[len(topLevelList)-1].title("Child")
classInstance.append(mainLoopThread(topLevelList[len(topLevelList)-1]))
root.after(5000,startCounting)
class mainLoopThread(threading.Thread):
def __init__(self,toplevelW):
self.toplevelW = toplevelW
threading.Thread.__init__(self)
self.start()
def run(self):
self.toplevelW.mainloop()
global classInstance
classInstance = []
global topLevelList
topLevelList = []
global root
root = Tk()
root.title("Main")
startCounting()
root.mainloop()
Tkinter is designed to run from the main thread, only. See the docs:
Just run all UI code in the main
thread, and let the writers write to a
Queue object; e.g.
...and a substantial example follows, showing secondary threads writing requests to a queue, and the main loop being exclusively responsible for all direct interactions with Tk.
Many objects and subsystems don't like receiving requests from multiple various threads, and in the case of GUI toolkit it's not rare to need specfically to use the main thread only.
The right Python architecture for this issue is always to devote a thread (the main one, if one must) to serving the finicky object or subsystem; every other thread requiring interaction with said subsystem or object must them obtain it by queueing requests to the dedicated thread (and possibly waiting on a "return queue" for results, if results are required as a consequence of some request). This is also a very sound Python architecture for general-purpose threading (and I expound on it at length in "Python in a Nutshell", but that's another subject;-).
Tkinter has issues dealing with input from multiple threads, I use mtTkinter instead, you won't need to change any code and everything will work fine. Just import mtTkinter instead of Tkinter.
You can get it here:
http://tkinter.unpythonic.net/wiki/mtTkinter
Is there a reason you want (or think you need) one event loop per toplevel window? A single event loop is able to handle dozens (if not hundreds or thousands) of toplevel windows. And, as has been pointed out in another answer, you can't run this event loop in a separate thread.
So, to fix your code you need to only use a single event loop, and have that run in the main thread.

GUI not updated from another thread when using PyGtk

I am using PyGTK to build a GUI application. I want to update the textview widget from another thread but the widget is not getting updated everytime i try an update. What should i do to get a reliable GUI updating?
GTK+ is not thread-safe, so you should not simply call GUI update methods from other threads. glib.idle_add (or gobject.idle_add in older PyGTK versions) can be used for this purpose.
Instead of writing:
label.set_text("foo")
you would write:
glib.idle_add(label.set_text, "foo")
which causes the function call to be queued in GTK+.
If you need to run multiple statements, it's often easier to wrap them in a function:
def idle():
label1.set_text("foo")
label2.set_text("bar")
glib.idle_add(idle)
Make sure that the function passed to idle_add does not return True; otherwise it will be queued again.
Edit: As Daniel pointed out, you need to call gtk.gdk.threads_init() anywhere in your program first.
As stated in the previous answers, GTK is not "thread safe," but it is "thread-aware" - see this page on Threads: https://developer.gnome.org/gdk2/stable/gdk2-Threads.html
In order to modify GTK widgets from another thread you have to use GTK's locking. Call gtk.threads_init() immediately after importing the gtk module, and then you can update like so:
gtk.threads_enter()
# make changes...
gtk.threads_leave()
Note that the above will not work on Windows (see the link above). On Windows you must use gobject.idle_add() as explained above, though don't forget to put gobject.threads_init() directly after importing gobject in your code! The idle_add() function will execute the update itself in the main thread (the thread running gtk.main()).
the same may be achieved using gobject.idle_add method whose syntax is same as above,you have to import the module gobject
What Johannes said is correct, however since GTK is a wrapper for the glib and gobject things, you would actually want to use gtk.idle_add(). No need for the unnecessary imports.

Crash reporting in Python

Is there a crash reporting framework that can be used for pure Python Tkinter applications? Ideally, it should work cross-platform.
Practically speaking, this is more of 'exception reporting' since the Python interpreter itself hardly crashes.
Here's a sample crash reporter:
Rather than polluting your code with try..except everywhere, you should just implement your own except hook by setting sys.excepthook. Here is an example:
import sys
import traceback
def install_excepthook():
def my_excepthook(exctype, value, tb):
s = ''.join(traceback.format_exception(exctype, value, tb))
dialog = ErrorReportDialog(None, s)
dialog.exec_()
sys.excepthook = my_excepthook
Call install_exception() when your application starts.
ErrorReportDialog is a Qt dialog I've made. traceback.format_exception() will format argument passed to the except hook in the same way it does in Python's interpreter.
EDIT: I forgot to mention a little gotcha with that. It doesn't work with threads (well, at least it didn't last time I checked). For code running in another thread, you will need to wrap it in a try..except block.
Stick try excepts everywhere your application can crash (I/O, networking etc.). Whenever an except is called, call a function that will kill the old window, spawn a new tkinter notification window, or a custom one with your error message.
Do a root.after to the new window and send your error report (urllib).
Put a restart button if you wish.
There is no crash reporting framework - as tkinter is not that type of GUI. It's pretty much a wrapper for simple command line apps.
Go pyqt/gtk or wxpython if you want the features seen in the screen-shot above. But I'm pretty sure that where ever you go, you'll have to write your own reporter.

Categories