Skip to content

QObject based RunEngine#9

Open
ronpandolfi wants to merge 8 commits intoNSLS-II:masterfrom
ronpandolfi:master
Open

QObject based RunEngine#9
ronpandolfi wants to merge 8 commits intoNSLS-II:masterfrom
ronpandolfi:master

Conversation

@ronpandolfi
Copy link
Copy Markdown
Contributor

No description provided.

@teddyrendahl teddyrendahl mentioned this pull request May 31, 2019
@ronpandolfi ronpandolfi requested a review from teddyrendahl July 13, 2019 00:00
@ronpandolfi
Copy link
Copy Markdown
Contributor Author

@teddyrendahl How does this merge look?

Comment thread mily/runengine.py
@@ -0,0 +1,305 @@
import logging
from bluesky import RunEngine
from bluesky.utils import RunEngineInterrupted, install_qt_kicker
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread mily/runengine.py

class QRunEngine(QObject):
sigDocumentYield = Signal(str, dict)
sigAbort = Signal() # TODO: wireup me
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not yet wired up?

Comment thread mily/runengine.py
# Instantiate widget information and layout
super().__init__(parent=parent)

self.setStyleSheet('QLabel {qproperty-alignment: AlignCenter}')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I wonder if mily should make a consistent set of rules as to how QWidget subclasses handle style sheets.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not necessarily disagreeing but I think this is pretty harmless here? I believe this is to avoid running a for loop over all the labels to and using QLabel.setAlignment.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Harmless, sure - but not so easily extensible.

I doubt it's a controversial opinion, but I think everything in mily should be designed with extensibility in mind, where possible.

Copy link
Copy Markdown
Collaborator

@teddyrendahl teddyrendahl left a comment

Choose a reason for hiding this comment

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

Thanks for working on this @ronpandolfi!

Comment thread mily/runengine.py
# Instantiate widget information and layout
super().__init__(parent=parent)

self.setStyleSheet('QLabel {qproperty-alignment: AlignCenter}')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not necessarily disagreeing but I think this is pretty harmless here? I believe this is to avoid running a for loop over all the labels to and using QLabel.setAlignment.

Comment thread mily/runengine.py
self.engine = engine or QRunEngine()


@property
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On the first iteration @tacaswell suggested that we instead have a start QSlot that accepts a plan. That way you can easily pass in plans w/o dealing with attributes.

pcdshub/typhos#48 (comment)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Still stand by that suggestion, the RE widget should not hold any plans as state, but should be passed plans to be run by some other part of the GUI.

Not sure if passing (plan_fun, args, kwargs) and expecting this widget to call the plan or passing in an iterable makes more sense. In the case of passing in the callable + args it gives the widget a chance to do some nice introspcetion (like displaying the name of the plan it is running). On the other hand, the iterable scheme matches what the RE does more directly.

Comment thread mily/runengine.py
self.setLayout(lay)
self._plan = None

# Accept either RunEngine or QRunengine for engine
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this logic needs to go in the engine.setter if we truly want to accept both? For example:

# Will be QRunEngine
RE = RunEngine()
ew = EngineWidget(engine=RE)
# Won't be QRunEngine
RE = RunEngine()
ew = EngineWidget()
ew.engine = RE

Comment thread mily/runengine.py
return self._engine

@engine.setter
def engine(self, engine):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we have an existing _engine we need to disconnect the prior signals from their slots. Otherwise we risk having our widget being updated by multiple engines

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is not clear to me that allowing changing the runengine behind the widget is something we want to allow. If we really need to throw away a RunEngine instance might as well make a new Qt widget for it as well?

Comment thread mily/runengine.py
try:
func(self.engine)
# Pausing raises an exception
except RunEngineInterrupted as exc:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Might be worth catching generic Exception objects here. Nobody likes a GUI crash and this is where any bubbles from the execution of the plan will bubble up. There might also need to be some logic to make sure it is ready to take another plan afterwards (teardown and re-up of a failed RunEngine execution)? 🤔

Comment thread mily/tests/test_engine.py

from mily.runengine import EngineWidget

# Fixtures borrowed from pytest-qt
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just add pytest-qt as you discussed offline. Commenting here as a reminder.

@tacaswell
Copy link
Copy Markdown
Member

Can someone remind me what the plan for this and https://github.com/bluesky/bluesky-ui/tree/master/bluesky_ui is?

Comment thread mily/runengine.py
def __init__(self, runengine=None, **kwargs):
super(QRunEngine, self).__init__()

self.RE = runengine or RunEngine(context_managers=[], **kwargs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given that we need to create the RE with just the right settings (no context managers, probably a different during_task, and maybe using qthreads instead of python threads to push the asyncio event loop to the background, maybe with qumash to embed the asyncio event loop in the qt event loop (!!)) I don't think we should allow the user to pass in an existing RunEngine

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.

4 participants