Skip to content

Conversation

@AbbyGi
Copy link
Collaborator

@AbbyGi AbbyGi commented Oct 29, 2025

References and relevant issues

Description

def run(self):
catalog_sub = self.client.subscribe()
catalog_sub.child_created.add_callback(on_new_child)
catalog_sub.start()
Copy link
Contributor

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.

Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

Exactly.

Copy link
Contributor

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())).

Copy link
Contributor

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.

Copy link
Collaborator

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?

Copy link
Contributor

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.

Copy link
Contributor

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?
Copy link
Collaborator

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:

  1. TILED_PROFILE
  2. TILED_DEFAULT_URL
  3. tiled.profiles.get_default_profile_name()

Comment on lines 39 to 41
# Ask the server to replay from the very first update, if we already
# missed some.
sub.start_in_thread(1)
Copy link
Collaborator

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()
Copy link
Collaborator

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()
Copy link
Collaborator

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.

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.

5 participants