fix(TTk): register signal event callbacks on init to avoid exception#469
fix(TTk): register signal event callbacks on init to avoid exception#469slook wants to merge 4 commits intoceccopierangiolieugenio:mainfrom
Conversation
|
I think the easiest solution is to split the main loop in 2 methods, |
|
you would use the code like this: from threading import Thread
import TermTk as ttk
root = ttk.TTk()
win = ttk.TTkWindow(parent=root, size=(50,20), layout=ttk.TTkGridLayout())
ttk.TTkLogViewer(parent=win)
root.main_init()
def main_thread():
root.main_run()
app = Thread(target=main_thread)
app.start()
app.join()but an exception is raised when ctrl-c is captured |
Launch in a terminal with command line argument `nicotine --tui` Depends on ceccopierangiolieugenio/pyTermTk#469
Launch in a terminal with command line argument `nicotine --tui` Depends on ceccopierangiolieugenio/pyTermTk#469
|
Yes you have the right idea about having the capability to instantiate the toolkit in its own thread. However your example is misleading to name the threaded target " The exception upon pressing Ctrl-C is not so much of an issue because at least it doesn't prevent startup and that exception can be avoided by setting a |
|
Your above example is somewhat similar to what I had, which shows we are thinking along the same lines, but I've subclassed Ideally, the toolkit library would have a built-in method to handle creating a new thread for itself automatically, but that would be another matter for you consider entirely as to if that is really a good idea or not. This does get both the user interface and the program core started cleanly so that different modules can interact with eachother... Example mock-up derived from the As you can see, the client already has its own "main loop" and threaded event emitter so it seems like a good idea to use a terminology other than "main thread" to avoid ambiguity with core activities that need to run directly on the Python interpreter. If you are interested about my experimental implementation attempt, you can take a look at the git clone --branch ttk --depth 1 https://github.com/slook/nicotine-plus.git
cd nicotine-plus
./nicotine --tui # use the TTk TUI instead of the default Gtk GUIThe TermTk interface is in the "pynicotine/ttktui" folder of the project (its an interesting experiment to test your library). It could be helpful to bring you some ideas about how developers might choose to implement your library into larger programs that take a modular approach to separate the core from the UI(s), albeit perhaps in ways that maybe weren't originally intended! It could make sense to expose Ideally, it should be possible to properly register the relevant signal handlers upon initializing the |
|
I am interested in exploring the different ways to integrate my library with different projects. where I can call the initialise routine in the start. Anyway, the reason I am forcing to run it in the main thread is mainly because I didn't thought about a different implementation and also because QT and GTK seems to use the same approach. |
|
I added the start run and ttk_init methods and it works except the ctr-c issue (that I will investigate) This is something like you were thinking? import sys, os
from threading import Thread
import TermTk as ttk
root = ttk.TTk()
win = ttk.TTkWindow(parent=root, size=(50,20), layout=ttk.TTkGridLayout())
ttk.TTkLogViewer(parent=win)
root.ttk_init()
def ttk_thread():
root.run()
app = Thread(target=ttk_thread)
app.start()
app.join()Threaded reimplementation: import sys, os
from threading import Thread
from datetime import datetime
import TermTk as ttk
class ThreadedTTk(ttk.TTk, Thread):
pass
root = ThreadedTTk()
win = ttk.TTkWindow(parent=root, size=(50,20), layout=ttk.TTkGridLayout())
ttk.TTkLogViewer(parent=win)
root.start()
root.join()Or even better, I can introduce a ThreadedTTk class that would overcome a little hack I added to the main TTk. can you point me out the gtk reference (in the gtk doc or forums) that you were mentioning? |
Launch in a terminal with command line argument `nicotine --tui` Depends on ceccopierangiolieugenio/pyTermTk#469
|
Gtk.Application() https://docs.gtk.org/gtk4/class.Application.html ... https://gnome.pages.gitlab.gnome.org/pygobject/tutorials/gtk4/introduction.html#extended-example https://developer.gnome.org/documentation/tutorials/application.html https://gnome.pages.gitlab.gnome.org/pygobject/index.html
I'd suggest in future you might do a combination of other asynchronous methods depending on the platform, and so I advice against using the term "Threaded" in case that causes confusion about its purpose. It would be ideal if the working mechanics were abstracted away from the developer so they don't have to worry about managing threads or choosing from various methods invocation. In other words, the Since TTk seems to only be capable of making full-screen applications then I think calling it "
It might be helpful for you to know that some (but not all) Slots and Signals are also affected by the same crash such as connecting
You don't need a start() function becauuse Python handles calling your https://github.com/slook/nicotine-plus/blob/ttk/pynicotine/ttktui/widgets/screen.py https://github.com/slook/nicotine-plus/blob/ttk/pynicotine/ttktui/application.py#L48 That works nicely even though the
|
|
main_thread was just an example, ThreadedTTk same , it was just the idea of introducing a thread extension for this purpose I would have not used those names. Anyway, I am not sure what you mean about my library being only able to make full screen apps. About the start class TTk():
def mainloop(self) -> None:
self.ttk_init()
self.run()
def ttk_init(self) -> None:
# Signal and callbacks registration
def start(self) -> None:
self.ttk_init()
# if it is an istance of Thread
super().start()
def run(self) -> None:
# loop routine |
|
Your strategy seems sound, but I think implementing a class overloading approach would suit the rest of the library better for this use case. Essentially like you say, making a core widget to run threaded if it makes sense to do so and the system can allow it.
We can determine if the caller needs wants this case using this guard condition... ... or maybe https://docs.python.org/3/library/threading.html#threading.current_thread However, the only thing that is for certain is that we're operating well outside of "normal conditions" here :D
There isn't any examples or tutorials of simple command line programs in your repo so I made an assumption that you are focused on specializing mainly in full-screen programs, whereas another library such as python-prompt-toolkit, Rich or Textual perhaps seem more suited to partial non-alternate screen REPL interfaces like the progress bars used in
Indeed. The best I have ever been able to do is modifying the last printed line but I agree that going any further up the readback is asking for trouble. In any case the fact you have the TTkTerminal widget kind of makes it irrelevant for the TermTk library to support anything other than full screen applications, and that's totally fine to focus on that because what it does do it does really well and considering it is without
Heck, fair play to you... I didn't know it is all your own project from the very beginning and implementing a full blown API that follows modern programming conventions in that time is quite a large achievement to have made so you must really know your ANSI escape sequences and have done plenty of amazing hacks like you're some kind of mathematical genius or what?! |
|
See related upstream issue python/cpython#139391 and PR python/cpython#139858 and python/cpython#81269 and python/cpython#83223 about Update: Update: I pushed a related change to move the |
223fd4d to
96c2822
Compare
Allows mainloop() to be started from a different thread without encountering exception "signal only works in main thread of the main interpreter" on start. This makes sense because window resizing events come from the host.
96c2822 to
ea4aa70
Compare
Launch in a terminal with command line argument `nicotine --tui` Depends on ceccopierangiolieugenio/pyTermTk#469
Launch in a terminal with command line argument `nicotine --tui` Depends on ceccopierangiolieugenio/pyTermTk#469
… message Otherwise it is impossible to identify the real cause of any crash and the program ends badly without resetting the terminal back to default settings.
e00e5d4 to
4f6526a
Compare
@ceccopierangiolieugenio I have what I think is a good solution for that in this PR. Perhaps we can invoke other Update: Pushed latest changes from main. -- TTk 0.5.0 |
dc6bd59 to
8ef5c1a
Compare

TTk: fix exception when invoking mainloop() from a thread
It is necessary for me to invoke
mainloop()from a different thread because the core part of my application has to run in the main thread, and these two calls (toTTkSignalDriver.init()andTTkTerm.registerResizeCb()) were the only things which prevented that arrangement from being possible due to this error message on startup:Caught an exception: signal only works in main thread of the main interpreter(no stacktrace)If you require further clarification or explanation about this edge case then please do let me know. I think this should be a harmless change, and is a benefit to enable more flexible coroutine invocation methods for various purposes.
Thank you very muchly for your time and efforts :)
Update:
With later fixes found after this workaround enabled to get a stacktrace of what was happened underneath:
signalis actually nothing to do with the actual cause of the crash as you can see there was a bug in my program that the later errors in your library did hide but now they can be seen so I already fixed them so its okay only for future reference.Of course, I had no way of knowing that before because this crash wipes out any other traceback created by my program.