Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nonblocking open support to Dialog #552

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

yajaru
Copy link

@yajaru yajaru commented May 22, 2024

Add a non-blocking 'open' to Dialog so that applications may continue to run their existing event loops while the dialog is open.
Applications that wish to receive results from the Dialog can do so via the existing 'finished' event.

Copy link

codecov bot commented May 22, 2024

Codecov Report

Attention: Patch coverage is 55.55556% with 4 lines in your changes missing coverage. Please review.

Project coverage is 70.74%. Comparing base (ca6cf4a) to head (58412b0).

Current head 58412b0 differs from pull request most recent head ec2c616

Please upload reports for the commit ec2c616 to get more accurate results.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #552      +/-   ##
==========================================
- Coverage   70.75%   70.74%   -0.01%     
==========================================
  Files         287      287              
  Lines       25638    25637       -1     
  Branches     4678     4680       +2     
==========================================
- Hits        18139    18136       -3     
  Misses       6386     6386              
- Partials     1113     1115       +2     

@MatthieuDartiailh
Copy link
Member

@sccolbert I am interested in the historical reasons you had not to provide such a mode initially.

@yajaru
Copy link
Author

yajaru commented May 22, 2024

@sccolbert I am interested in the historical reasons you had not to provide such a mode initially.

My guess is that open wasn't available when enaml on QT was being made but, this is pure speculation.
image

@sccolbert
Copy link
Member

sccolbert commented May 22, 2024 via email

@yajaru
Copy link
Author

yajaru commented May 22, 2024

I'm pretty sure you can call .show() on a dialog and it will open and return immediately. However, it won't be modal.

show does exist on Dialog by virtue of it existing for all enaml widgets (which I am leery on, but that is a different conversation). However, losing the modal behavior is a significant loss given that this is a dialog and exec_ has the modal behavior we are trying to separate from the blocking.

Mostly I'm just exposing the api that QT recommends instead of exec (see https://doc.qt.io/qt-6/qdialog.html#exec, specifically the note) as it has the closest semantics to exec.

@sccolbert
Copy link
Member

sccolbert commented May 22, 2024 via email

@yajaru
Copy link
Author

yajaru commented May 22, 2024

For what it's worth, I took a quick perusal of some other toolkits to see how they handle dialogs.

GTK3 has a similar blocking/non-blocking split to QT (with the default falling on the non-blocking side)
GTK4 drops the blocking API and only leaves the non-blocking version.

Win32 supports both blocking and non-blocking.
WinUI 3 only supports non-blocking

AppKit supports both blocking and non-blocking
SwiftUI only supports non-blocking

Note that I'm not a developer in any of these so, it is very possible that I have gotten something wrong or misunderstood the docs. That being said, the world looks to be moving towards non-blocking dialogs exclusively for newer toolkits, which lends credence to this change or something in the same vein.

@MatthieuDartiailh
Copy link
Member

Thanks for the detailed explanation. Given it means aligning with most toolkit, this addition looks fine to me.

Could you add some tests and an entry to the changelog ?

@yajaru
Copy link
Author

yajaru commented May 30, 2024

I've written a test but no clue if it works though. Unfortunately, I have not been able to get enaml building on my machine due to dependency issues so, I'm unable to run the test to check if it works.
If there are any dumb errors, then squash will have to do for it.

@yajaru
Copy link
Author

yajaru commented Jun 19, 2024

Ok, this should be ready to go. I would personally squash this down b/c there are intermediate commits due to debugging difficulties.

Some notes:

  • I only wrote a test over the new async Dialog path. This is b/c I'm not sure how best to test the sync Dialog given that it blocks on the show. This should be fine as the sync path is untouched by my changes.
  • There should also changes made to the stdlib dialogs to expose the async interface for them as well. I'll leave that as an exercise for later.
  • I encountered some weirdness that I'm not sure if I can class as a bug or not. Basically, when we close the Dialog, it sticks around in the windows set and does not remove itself. I'm not sure if this is intended but, once we not longer have a live window for the dialog and all the references to it are gone, I would expect it to remove itself from the set.

@sccolbert
Copy link
Member

sccolbert commented Jun 19, 2024 via email

@yajaru
Copy link
Author

yajaru commented Jun 20, 2024

The addition and removal to and from the windows set is done in the initialize and destroy methods. So I guess the issue is that we are not fully executing the lifecycle of the Dialog with the way I have done it here (which is creating a dialog object outside of the tree).
https://github.com/nucleic/enaml/blob/ca6cf4a6d80eab455cbe8201f9251b46bd185fa7/enaml/widgets/window.py#L152C5-L168C37

This raises the question of what is the proper way to create and show a Dialog given that it is often done in a callback from some user initiated action like a button.

Copy link
Member

@MatthieuDartiailh MatthieuDartiailh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you did not touch the closing logic I fail to see why the dialog is not removed from the set. What may happen is that Qt does not process all the required events for the closing to complete. They are some testing utilities to process events till a window is closed, could you try using them ?

@@ -68,6 +71,16 @@ def exec_(self):
self.activate_proxy()
return self.proxy.exec_()

def open(self):
""" Launch the dialog as a modal window.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could specify that this call is non blocking to make the difference with exec_

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@yajaru
Copy link
Author

yajaru commented Jun 21, 2024

That was the ticket it seems; I needed wait the QT loop for a bit since the callback to do the destroy was deferred on it.

@MatthieuDartiailh
Copy link
Member

Tests failures are only Codecov failing to retrieve the upload_token. So all good. Could you add an entry into the changelog and I will merge.

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