def __init__(self, sequencelistmodel, parent=None, *args): super().__init__(parent=parent, *args) self.seqlist = sequencelistmodel self._hwsoc = HWSOC() self.mark = 0 self._steps = 0 self._sequence_cache = None # thread locks self.lock_running = QMutex() self.lock_waiting = QMutex() self.lock_waitcond = QWaitCondition() self.lock_steps = QMutex() # state variables self.state_paused = False self.state_running = False self.state_waitinghwok = False self.startTreatment.connect(self._startTreatment) self.stopTreatment.connect(self._stopTreatment) self.restartTreatment.connect(self._restartTreatment) self.abortTreatment.connect(self._abortTreatment) self.setHWOK.connect(self._sethwok) # create QThread and move this object to it self.thread = QThread() self.moveToThread(self.thread) self.thread.start()
class LeafletAssembly(QQuickItem): """Base class for all leaflet assembly types""" # Q_CLASSINFO('DefaultProperty', 'leaflets') def __init__(self, parent=None): QQuickItem.__init__(self, parent) self._leaflets = [] self._hwsoc = HWSOC() def componentComplete(self): QQuickItem.componentComplete(self) self.enableHWLink() leafletsChanged = pyqtSignal([QQmlListProperty], arguments=['leaflets']) @pyqtProperty(QQmlListProperty, notify=leafletsChanged) def leaflets(self): return QQmlListProperty(Leaflet, self, self._leaflets) def publishToHW(self, index=None): poslist = [lf.extension for lf in self._leaflets] logger.debug('publishing all to HW - [{}]'.format(', '.join(str(x) for x in poslist))) self._hwsoc.set_all_positions(poslist) @pyqtSlot() def enableHWLink(self): self.onLeafletReleased.connect(self.publishToHW) @pyqtSlot() def disableHWLink(self): self.onLeafletReleased.disconnect(self.publishToHW)
class LeafletAssembly(QQuickItem): """Base class for all leaflet assembly types""" # Q_CLASSINFO('DefaultProperty', 'leaflets') def __init__(self, parent=None): QQuickItem.__init__(self, parent) self._leaflets = [] self._hwsoc = HWSOC() self.hw_linked = False def componentComplete(self): QQuickItem.componentComplete(self) self.enableHWLink() leafletsChanged = pyqtSignal([QQmlListProperty], arguments=['leaflets']) @pyqtProperty(QQmlListProperty, notify=leafletsChanged) def leaflets(self): return QQmlListProperty(Leaflet, self, self._leaflets) @pyqtSlot() @pyqtSlot(int) def publishToHW(self, index=None): poslist = [lf.extension for lf in self._leaflets] logger.debug('publishing all to HW - [{}]'.format(', '.join( str(x) for x in poslist))) self._hwsoc.set_all_positions(poslist) @pyqtSlot() def setCalibration(self): self._hwsoc.set_calibration() @pyqtSlot() def enableHWLink(self): if not self.hw_linked: self.onLeafletReleased.connect(self.publishToHW) self.hw_linked = True @pyqtSlot() def disableHWLink(self): if self.hw_linked: self.onLeafletReleased.disconnect(self.publishToHW) self.hw_linked = False # pre-defined leaflet configurations @pyqtSlot() def setClosed(self): """Move leaflets to 'closed' position""" for ii, leaf in enumerate(self._leaflets): leaf.extension = leaf.max_extension if ii < 4 else 0 self.publishToHW() @pyqtSlot() def setOpened(self): """Move leaflets to 'closed' position""" for ii, leaf in enumerate(self._leaflets): leaf.extension = 0 self.publishToHW()
def __init__(self, *args, **kwargs): QQuickItem.__init__(self, **kwargs) self._hwsoc = HWSOC() self._extension = 0 self._index = Leaflet.next_available self._limit_travel = True Leaflet.next_available += 1
def __init__(self, parent=None): QQuickItem.__init__(self, parent) self._leaflets = [] self._hwsoc = HWSOC() self.hw_linked = False
def start_gui(): parser = argparse.ArgumentParser( description= 'SmallSOCC v{!s} - Frontend for interfacing with SOC hardware'.format( VERSION_FULL), formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-L', '--loglevel', type=str, choices=[*logging._nameToLevel.keys()], default='WARNING', help='set the loglevel') parser.add_argument('--logconf', type=str, default=os.path.join(LIB_DIR, 'logging.conf.json'), help='path to log configuration') args = parser.parse_args() # initialize logger soclog.init_logging(level=logging._nameToLevel.get(args.loglevel, None), config_path=args.logconf) listmodel = sequence.SequenceListModel() if args.loglevel is not 'NOTSET' and logging._nameToLevel[ args.loglevel] <= logging.DEBUG: # load example sequence, for rapid debugging try: listmodel = sequence.SequenceListModel.fromJson( os.path.join(TEST_FILES, 'test_output.json')) except Exception as e: logger.warning('FAILED TO READ DEBUG JSON FILE : {!s}'.format(e)) # SAMPLE ITEMS FOR DEBUG from sequence import SequenceItem, SequenceItemType sample_sequenceitems = [ SequenceItem(rot_couch_deg=5, rot_gantry_deg=0, timecode_ms=0, datecreatedstr="2016 Oct 31 12:00:00", type='Manual'), SequenceItem(rot_couch_deg=12, rot_gantry_deg=120, timecode_ms=1500, description="descriptive text2", type=SequenceItemType.Auto), SequenceItem(rot_couch_deg=24, rot_gantry_deg=25, timecode_ms=3000, description="descriptive text3"), SequenceItem(rot_couch_deg=0, rot_gantry_deg=45, timecode_ms=4500, description="descriptive text4"), ] listmodel = sequence.SequenceListModel( elements=sample_sequenceitems) HWSOC(8, HID=None) # init singleton instance for controlling hardware # integrate qml logging with python logging QtCore.qInstallMessageHandler(qt_message_handler) # prevent qml caching os.environ['QML_DISABLE_DISK_CACHE'] = '1' # set QML visual style app = QGuiApplication(sys.argv + ['-style', 'default']) # app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) # register pre-exit hook app.aboutToQuit.connect(preExit) # perform cleanup ### the order here matters greatly - must init context properties before loading main.qml engine = QQmlApplicationEngine() rootContext = engine.rootContext() ## compute scaling ratios to make views dpi-independent if logger.getEffectiveLevel() <= logging.DEBUG: screens = app.screens() for sidx, screen in enumerate(screens): geometry = screen.geometry() dpi = screen.logicalDotsPerInch() logger.debug( 'Screen #{} ({}) - size: (w:{}, h:{}); DPI: {}'.format( sidx, screen.name(), geometry.width(), geometry.height(), dpi)) screen = app.primaryScreen() geometry = screen.geometry() dpi = screen.logicalDotsPerInch() refDpi = 96 refHeight = 1440 refWidth = 2560 _h = min(geometry.width(), geometry.height()) _w = max(geometry.width(), geometry.height()) sratio = min(_h / refHeight, _w / refWidth) # element size scaling fratio = min(_h * refDpi / (dpi * refHeight), _w * refDpi / (dpi * refWidth)) # font pointSize scaling logger.debug('Setting scaling ratios - general: {}; font: {}'.format( sratio, fratio)) ## Set accessible properties/objects in QML Root Context rootContext.setContextProperty( "mainwindow_title", 'SOC Controller - {!s}'.format(VERSION_FULL)) # make seq. list model accessible to qml-listview rootContext.setContextProperty("SequenceListModel", listmodel) pathhandler_instance = pathhandler.PathHandler() rootContext.setContextProperty("PathHandler", pathhandler_instance) rootContext.setContextProperty("sratio", sratio) rootContext.setContextProperty("fratio", fratio) # load layout engine.load(QtCore.QUrl(os.path.join(dirname(__file__), 'main.qml'))) ## connect signals to slots - unnecessary, example of grabbing qml objects from py-code # listview buttons # rootObject = engine.rootObjects()[0] # btns = rootObject.findChildren(QQuickItem, "list_buttons", QtCore.Qt.FindChildrenRecursively)[0] # btns.findChild(QQuickItem, 'btn_moveup').clicked.connect(lambda: print('moveup clicked')) return app.exec_()
class TreatmentManager(QObject): def __init__(self, sequencelistmodel, parent=None, *args): super().__init__(parent=parent, *args) self.seqlist = sequencelistmodel self._hwsoc = HWSOC() self.mark = 0 self._steps = 0 self._sequence_cache = None # thread locks self.lock_running = QMutex() self.lock_waiting = QMutex() self.lock_waitcond = QWaitCondition() self.lock_steps = QMutex() # state variables self.state_paused = False self.state_running = False self.state_waitinghwok = False self.startTreatment.connect(self._startTreatment) self.stopTreatment.connect(self._stopTreatment) self.restartTreatment.connect(self._restartTreatment) self.abortTreatment.connect(self._abortTreatment) self.setHWOK.connect(self._sethwok) # create QThread and move this object to it self.thread = QThread() self.moveToThread(self.thread) self.thread.start() onTreatmentStarted = pyqtSignal() onTreatmentStopped = pyqtSignal(int) onTreatmentAborted = pyqtSignal(int) onTreatmentCompleted = pyqtSignal(int) onTreatmentAdvance = pyqtSignal(int) onTreatmentSkip = pyqtSignal(int, float) # cross-thread control via signals startTreatment = pyqtSignal([int]) stopTreatment = pyqtSignal() restartTreatment = pyqtSignal() abortTreatment = pyqtSignal() setHWOK = pyqtSignal() onStepsChanged = pyqtSignal([int]) @pyqtProperty(int, notify=onStepsChanged) def steps(self): self.lock_steps.lock() v = self._steps self.lock_steps.unlock() return v @steps.setter def steps(self, v): self.lock_steps.lock() self._steps = v self.lock_steps.unlock() onWaitingChanged = pyqtSignal([bool]) @pyqtProperty(bool, notify=onWaitingChanged) def waitinghwok(self): self.lock_waiting.lock() v = self.state_waitinghwok self.lock_waiting.unlock() return v @waitinghwok.setter def waitinghwok(self, v): self.lock_waiting.lock() self.state_waitinghwok = v self.lock_waiting.unlock() onRunningChanged = pyqtSignal([bool]) @pyqtProperty(bool, notify=onRunningChanged) def running(self): self.lock_running.lock() v = self.state_running self.lock_running.unlock() return v @running.setter def running(self, v): self.lock_running.lock() self.state_running = v self.lock_running.unlock() def deliverAll(self): while self.mark < len(self._sequence_cache) and self.running: duration = self.deliverOne() # advance to next segment? if self.running: if self.mark < len(self.seqlist) - 1: self.mark += 1 self.steps += 1 # if duration >= 1000: # self.onTreatmentAdvance.emit(self.mark) # only updates UI self.onTreatmentAdvance.emit(self.mark) # only updates UI else: self._stopTreatment() self.state_paused = False self.onTreatmentCompleted.emit(self.mark) #update ui def deliverOne(self): """Run timer for a single beam""" seg = self._sequence_cache[self.mark] extension_list = seg._members['extension_list'].value duration = float(seg._members['timecode_ms'].value) if duration <= 0: self.waitinghwok = True self.onTreatmentSkip.emit(self.mark, duration) else: self.waitinghwok = True self._hwsoc.set_all_positions(extension_list) while self.waitinghwok: # spin event loop until hwok signal is recieved after delivery of prev. segment QCoreApplication.processEvents() t1 = time.perf_counter() while (time.perf_counter() - t1) < duration * 0.001: # catch signals every 250ms while delivering if (time.perf_counter() - t1) % 0.25: QCoreApplication.processEvents() if not self.state_running: # early exit from UI break return duration def _sethwok(self): self.waitinghwok = False @pyqtSlot(int) def _startTreatment(self, index): """Start the treatment at specified index""" self.mark = index self._sequence_cache = self.seqlist._items.copy() if not self.state_paused: self.steps = 1 self.running = True self.onTreatmentStarted.emit() logger.debug("Treatment started") self.deliverAll() def _stopTreatment(self): self.state_paused = True self.running = False self.onTreatmentStopped.emit(self.mark) logger.debug("Treatment stopped") def _restartTreatment(self): self.steps = 0 self.state_paused = False self._startTreatment(0) logger.debug("Treatment restarted") def _abortTreatment(self): self.running = False self.state_paused = False self.onTreatmentAborted.emit(self.mark) logger.debug("Treatment aborted")