-
Notifications
You must be signed in to change notification settings - Fork 4
Enable multi-dimensional plotting #20
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
base: main
Are you sure you want to change the base?
Conversation
| def run(self): | ||
| catalog_sub = self.client.subscribe() | ||
| catalog_sub.child_created.add_callback(on_new_child) | ||
| catalog_sub.start() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works.
Long-term question; QRunnable is generally intended for fire-and-forget short-term tasks. Is it appropriate for this task, which will run ~forever waiting for new scans to appear? This might be a use case for QThreadPool`.
Again, what you have works. Just something keep in the back of your minds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question probably comes down to how much interaction is needed between the worker thread and the main thread.
That does seem to imply that QThread would be more appropriate that QThreadPool + QRunnble for the catalog subscriber here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The child data subscribers however might be better off as individual QRunnables in a QThreadPool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
- Every sub should be started a QThread because they could be long-running (like a long scan or a never-completed scan).
- The callbacks should be run using a custom executor:
class QtExecutor:
"Wrap QtThreadPool in concurrent.futures.Executor-compatible API"
…
The above is easier than it might sound: you just need the methods submit(f, *args) and shutdown(wait: bool).
Then inject your custom executor, overwriting Tiled’s default ThreadPoolExecutor like x.subscribe(executor=QtExecutor(QtThreadPool())).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thus, the threads listening to websockets for seconds/minutes/hours are QThread and the threads doing the work of the callbacks (which is always a short job) are QRunmable in a QThreadPool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Qthread largely has the same code signature as Qrunnable, anything else I need to add?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above comment about QtExector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, that is not a thing that exists—it is a thing you'll need to define, to wrap the QtThreadPool interface in a class that present the Executor interface expected by Tiled.
| self.viewer = napari_viewer | ||
|
|
||
| self.model = TiledSelector(url="https://tiled.nsls2.bnl.gov/api") | ||
| # TODO: try using TILED_DEFAULT_PROFILE here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a sensible order of preference here could be:
- TILED_PROFILE
- TILED_DEFAULT_URL
- tiled.profiles.get_default_profile_name()
| # Ask the server to replay from the very first update, if we already | ||
| # missed some. | ||
| sub.start_in_thread(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we need handle the case where replaying from message #1 overlaps with data we already have.
Is there a unique identifier in the update payload to know whether we are receiving data that is already in the table / image stack?
| def run(self): | ||
| catalog_sub = self.client.subscribe() | ||
| catalog_sub.child_created.add_callback(on_new_child) | ||
| catalog_sub.start() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question probably comes down to how much interaction is needed between the worker thread and the main thread.
That does seem to imply that QThread would be more appropriate that QThreadPool + QRunnble for the catalog subscriber here.
| def run(self): | ||
| catalog_sub = self.client.subscribe() | ||
| catalog_sub.child_created.add_callback(on_new_child) | ||
| catalog_sub.start() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The child data subscribers however might be better off as individual QRunnables in a QThreadPool.
References and relevant issues
Description