Skip to content

Conversation

@ceccopierangiolieugenio
Copy link
Owner

No description provided.

@slook
Copy link
Contributor

slook commented Oct 26, 2025

I think the easiest solution is to split the main loop in 2 methods, an initialisation and a loop,

I don't see why this should be required. It ought to be made such as in PR #469 so that it is enough to...

  1. Do all the main initialization in TTk.__init__() (i.e. when declaring root = TTk()), and then;
  2. Set root.mainloop as the target of a Thread() instance (i.e. app = Thread(target=root.mainloop));
  3. Invoke app.start().

Otherwise, the excessive choice of different method calls make the module too confusing.

@ceccopierangiolieugenio
Copy link
Owner Author

ceccopierangiolieugenio commented Oct 26, 2025

This is because I don't want to initialize the signals and the drivers in the init, I already told you this.
The root object should not interfere with the system status until the main loop is called (or I find a proper way to do it).
I thought that splitting the main loop would make life easier to address your threading issue.
I am not going to implement any hacky way to support a convoluted threading extension unless I have some reasonable use case.
The same gtk example you posted does not seems to to anything similar and still require the main thread.
The core routines are not perfect and careful planning is required to avoid regression or to break compatibility.

@slook
Copy link
Contributor

slook commented Oct 26, 2025

I agree that of course a solution cannot be done in a hacky way. There is no rush to implement this into the core, since I have a working patch for my use case in the time being, so there is no limit of time for you to consider the best way of going about this. As such, I think it is worth having a deeper consideration on the best approach to take before committing.

I don't want to initialize the signals and the drivers in the init, I already told you this.

I do recall you telling me this, but I fail to understand the reason why this would be a bad thing to do? Are you thinking that this could this lead to an unstable state or something like that?

The root object should not interfere with the system status until the main loop is called

Why not? And if it cannot be during TTk initializing, then can perhaps another opportunity to do so be scheduled to trigger at the appropriate time, i.e. without the caller having to use an auxiliary ttk_init() method?

@ceccopierangiolieugenio
Copy link
Owner Author

One example could be something like this,
(Someone asked me long ago to allow different root apps to run in the same script)

# Any TTk() could be a different app provided by an external library or series of plugins

apps = {
  'app' : TTk(),
  'test' : TTk(),
  'config' : TTk() }

mode = input()

apps.get(mode, TTk()).mainloop()

Or in general any tool that require the control of the stdin/out/err before the mainloop,
one example is pytest (in the ttkode prototype, I need to spawn it as a separate process because it require control over the stdin and signals)

Normally the base instance of any major library don't initialise the drivers until the main routine is executed.

@slook
Copy link
Contributor

slook commented Oct 26, 2025

I see. Thank you for taking the time for explaining this. It helps me to better understand use cases other than my own which are important to get right for correct operation in all runtime scenarios, and from thinking about your example it is clear that my proposal would cause such an implementation to be impossible.

May I suggest then that the signals and drivers initialization routine be set in the main (or only) thread as a pending callback method in something like a Queue() or SimpleQueue() to be triggered upon entering the mainloop().

@ceccopierangiolieugenio
Copy link
Owner Author

This sounds interesting but I am not sure about the implementation.
Do you have an example?

@slook
Copy link
Contributor

slook commented Oct 26, 2025

SimpleQueue() https://github.com/nicotine-plus/nicotine-plus/blob/master/pynicotine/events.py#L227

put_nowait() https://github.com/nicotine-plus/nicotine-plus/blob/master/pynicotine/events.py#L279

get_nowait() https://github.com/nicotine-plus/nicotine-plus/blob/master/pynicotine/events.py#L319

callback(*args, **kwargs) https://github.com/nicotine-plus/nicotine-plus/blob/master/pynicotine/events.py#L261-L265

In that example, a SimpleQueue() is used to drive the application's main event emitter (10 times per second), but here it needed only once to dispatch queued event callbacks at startup.

@ceccopierangiolieugenio
Copy link
Owner Author

I mean, how can I ensure that the queued callbacks run in the main thread if the mainloop is called on a separate thread?
Or should I expose the queued initialisation so the caller can run it on the main thread?

@slook
Copy link
Contributor

slook commented Oct 28, 2025

Put the end of the __init__() routine into an idle waiting sleep loop, waiting while until a flag is set by the mainloop() in order to know when release the queued initialization callbacks.

However, the start of the mainloop would also have to wait until the initialization has actually been completed (it cannot begin while certain signal and driver attributes are still None).

@ceccopierangiolieugenio
Copy link
Owner Author

you mean using asyncio?
a sleep loop will run on a separate thread.
do you have an example?

@slook
Copy link
Contributor

slook commented Oct 28, 2025

I have no example for you, and upon further consideration I fear that such a mechanism is overly complicated and would be prone to failures or incompatibilities. Afterall, it would be satisfactory enough to call an init method before run, as you propose herein.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants