class SpectrumViewer(QtGui.QMainWindow): '''The application main window.''' def __init__(self, parent=None): # We're not calling the super method so we can span an new instance in # the new function. # However, when the first created window is closed, all windoes close # super(SpectrumViewer, self).__init__() QtGui.QMainWindow.__init__(self, parent) self.spectra = [] self.plots = [] self.initUI() def initToolBar(self): '''The main toolbar.''' # File actions self.newAction = QtGui.QAction(QtGui.QIcon('../icons/new.png'), '&New', self) self.newAction.setStatusTip('New molecule viewer') self.newAction.setShortcut('Ctrl+N') self.newAction.triggered.connect(self.new) self.openAction = QtGui.QAction(QtGui.QIcon('../icons/open.png'), '&Open', self) self.openAction.setStatusTip('Read molecule from file') self.openAction.setShortcut('Ctrl+O') self.openAction.triggered.connect(self.open) self.saveAction = QtGui.QAction(QtGui.QIcon('../icons/save.png'), '&Save', self) self.saveAction.setStatusTip('Write molecule to file') self.saveAction.setShortcut('Ctrl+S') self.saveAction.triggered.connect(self.save) self.saveimageAction = QtGui.QAction(QtGui.QIcon('../icons/picture-edit.png'), 'Save &image as PNG', self) self.saveimageAction.setStatusTip('Save image as PNG') self.saveimageAction.setShortcut('Ctrl+I') self.saveimageAction.triggered.connect(self.saveimage) self.exitAction = QtGui.QAction(QtGui.QIcon('../icons/exit.png'), '&Exit', self) self.exitAction.setShortcut('Ctrl+Q') self.exitAction.setStatusTip('Exit application') self.exitAction.triggered.connect(QtGui.qApp.quit) # View actions self.fullScreenAction = QtGui.QAction(QtGui.QIcon('../icons/fullscreen.png'), '&Full Screen', self) self.fullScreenAction.setStatusTip('Toggle full screen') self.fullScreenAction.setShortcut('F11') self.fullScreenAction.triggered.connect(self.fullscreen) self.toggleSidebarAction = QtGui.QAction('&Toggle sidebar', self) self.toggleSidebarAction.setStatusTip('Toggle sidebar') self.toggleSidebarAction.triggered.connect(self.togglesidebar) # Spectrum actions self.addspectrumAction = QtGui.QAction(QtGui.QIcon('../icons/addspectrum.png'), '&Add spectrum', self) self.addspectrumAction.setStatusTip('Add a new spectrum') self.addspectrumAction.setShortcut('Ctrl + A') self.addspectrumAction.triggered.connect(self.open) self.removespectrumAction = QtGui.QAction(QtGui.QIcon('../icons/removespectrum.png'), '&Remove spectrum', self) self.removespectrumAction.setStatusTip('Remove a spectrum') self.removespectrumAction.setShortcut('Ctrl + R') self.removespectrumAction.triggered.connect(self.removespectrum) # Toolbars self.filetoolbar = self.addToolBar('File') self.filetoolbar.addAction(self.newAction) self.filetoolbar.addAction(self.openAction) self.filetoolbar.addAction(self.saveAction) self.filetoolbar.addAction(self.saveimageAction) self.viewtoolbar = self.addToolBar('View') self.viewtoolbar.addAction(self.fullScreenAction) self.spectrumtoolbar = self.addToolBar('Spectrum') self.spectrumtoolbar.addAction(self.addspectrumAction) self.spectrumtoolbar.addAction(self.removespectrumAction) def initMenuBar(self): '''The main menubar.''' menubar = self.menuBar() menubar.setNativeMenuBar(True) # Create menus file = menubar.addMenu('&File') edit = menubar.addMenu('&Edit') view = menubar.addMenu('&View') spectrum = menubar.addMenu('&Spectrum') about = menubar.addMenu('&About') # File menu file.addAction(self.newAction) file.addAction(self.openAction) file.addAction(self.saveAction) file.addAction(self.saveimageAction) file.addSeparator() file.addAction(self.exitAction) # View menu view.addAction(self.fullScreenAction) view.addAction(self.toggleSidebarAction) # Spectrum menu spectrum.addAction(self.addspectrumAction) spectrum.addAction(self.removespectrumAction) def initParameterTree(self): '''The sidebar.''' self.parametertree = ParameterTree() self.params = [] def initUI(self): '''Initializes the main window widgets.''' self.initToolBar() self.initMenuBar() self.initParameterTree() self.plotwidget = pg.PlotWidget() self.plot = self.plotwidget.plotItem self.plot.setMenuEnabled(False) #cross hair self.vLine = pg.InfiniteLine(angle=90, movable=False) self.hLine = pg.InfiniteLine(angle=0, movable=False) self.plotwidget.addItem(self.vLine, ignoreBounds=True) self.plotwidget.addItem(self.hLine, ignoreBounds=True) self.vb = self.plot.vb self.plot.scene().sigMouseMoved.connect(self.mouseMoved) widget = QtGui.QWidget() hbox = QtGui.QHBoxLayout(widget) hbox.addWidget(self.parametertree) hbox.addWidget(self.plotwidget, 1) self.setCentralWidget(widget) self.resize(1200, 600) self.setWindowTitle('Molviewer :: Spectrum main window') self.show() def mouseMoved(self, evt): '''Detect mouse movement.''' pos = evt if self.plot.sceneBoundingRect().contains(pos): mousePoint = self.vb.mapSceneToView(pos) self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) def new(self): '''Spawn new main window.''' spawn = SpectrumViewer(self) spawn.setWindowTitle('Molviewer :: Spectrum main window (child)') spawn.show() def open(self): '''Open a new spectrum using the file dialog.''' filenames = QtGui.QFileDialog.getOpenFileNames(self, 'Open Files', '', '*.dx;; *.jdx') if filenames: for f in filenames: spectrum = Spectrum(f) self.addspectrum(spectrum) def save(self): pass def saveimage(self): pass def draw(self, spectrum): '''Draw a new spectrum. :param spectrum: The spectrum to draw. :type spectrum: spectrum.Spectrum :param color: The color in which the spectrum is drawn. ''' p = self.plotwidget.plot(spectrum.x, spectrum.y, pen=spectrum.color) if p not in self.plots: self.plots.append(p) if spectrum.datatype.lower() == 'infrared spectrum': # Plot higher wavenumbers to the left hand side self.plotwidget.invertX(True) self.plotwidget.setLabel('bottom', '1/cm') else: self.plotwidget.setLabel('bottom', spectrum.xunits) def fullscreen(self): '''Toggle full screen window.''' if self.windowState() & QtCore.Qt.WindowFullScreen: self.showNormal() else: self.showFullScreen() def togglesidebar(self): if self.parametertree.isVisible(): self.parametertree.hide() else: self.parametertree.show() def addspectrum(self, spectrum): '''Add a new spectrum. :param spectrum: The new spectrum. :type spectrum: spectrum.Spectrum ''' self.spectra.append(spectrum) ind = self.spectra.index(spectrum) spectrum.color = spectrumcolors[ind] params = [ {'name': spectrum.title, 'type': 'group', 'children': [ {'name': 'Number', 'type': 'int', 'value': self.spectra.index(spectrum)}, {'name': 'Color', 'type': 'color', 'value': spectrum.color}, {'name': 'Visible', 'type': 'bool', 'value': True}, {'name': 'Action Parameter', 'type': 'action'}, {'name': 'Spectrum', 'type': 'str', 'value': spectrum, 'visible': False}] }] p = Parameter.create(name='Files', type='group', children=params) p.sigTreeStateChanged.connect(self.change) self.parametertree.addParameters(p, showTop=False) self.params.append(p) self.draw(spectrum) def removespectrum(self): '''Remove a spectrum from display.''' num, ok = QtGui.QInputDialog.getText(self, 'Remove a spectrum', 'Enter spectrum number:') if ok and num: num = int(num) self.plotwidget.removeItem(self.plots[num]) self.plots.pop(num) self.spectra.pop(num) self.params.pop(num) self.parametertree.clear() for p in self.params: self.parametertree.addParameters(p, showTop=False) def change(self, param, changes): for param, change, data in changes: spec = param.parent().child('Spectrum').value() index = self.spectra.index(spec) if isinstance(param.value(), QtGui.QColor): self.plots[index].setPen(param.value()) self.spectra[index].color = param.value() if param.name() == 'Visible': if data is False: self.plots[index].clear() elif data is True: self.plots[index].setData(self.spectra[index].x, self.spectra[index].y)
class ObjectTree(QWidget, ComponentMixin): name = 'Object Tree' _stash = [] preferences = Parameter.create(name='Preferences', children=[{ 'name': 'Preserve properties on reload', 'type': 'bool', 'value': False }, { 'name': 'Clear all before each run', 'type': 'bool', 'value': True }, { 'name': 'STL precision', 'type': 'float', 'value': .1 }]) sigObjectsAdded = pyqtSignal([list], [list, bool]) sigObjectsRemoved = pyqtSignal(list) sigCQObjectSelected = pyqtSignal(object) sigAISObjectsSelected = pyqtSignal(list) sigItemChanged = pyqtSignal(QTreeWidgetItem, int) sigObjectPropertiesChanged = pyqtSignal() def __init__(self, parent): super(ObjectTree, self).__init__(parent) self.tree = tree = QTreeWidget( self, selectionMode=QAbstractItemView.ExtendedSelection) self.properties_editor = ParameterTree(self) tree.setHeaderHidden(True) tree.setItemsExpandable(False) tree.setRootIsDecorated(False) tree.setContextMenuPolicy(Qt.ActionsContextMenu) #forward itemChanged singal tree.itemChanged.connect(\ lambda item,col: self.sigItemChanged.emit(item,col)) #handle visibility changes form tree tree.itemChanged.connect(self.handleChecked) self.CQ = CQRootItem() self.Imports = ImportRootItem() self.Helpers = HelpersRootItem() root = tree.invisibleRootItem() root.addChild(self.CQ) root.addChild(self.Imports) root.addChild(self.Helpers) tree.expandToDepth(1) self._export_STL_action = \ QAction('Export as STL', self, enabled=False, triggered=lambda: \ self.export('stl', self.preferences['STL precision'])) self._export_STEP_action = \ QAction('Export as STEP', self, enabled=False, triggered=lambda: \ self.export('step')) self._clear_current_action = QAction(icon('delete'), 'Clear current', self, enabled=False, triggered=self.removeSelected) self._toolbar_actions = \ [QAction(icon('delete-many'),'Clear all',self,triggered=self.removeObjects), self._clear_current_action,] self.prepareMenu() tree.itemSelectionChanged.connect(self.handleSelection) tree.customContextMenuRequested.connect(self.showMenu) self.prepareLayout() def prepareMenu(self): self.tree.setContextMenuPolicy(Qt.CustomContextMenu) self._context_menu = QMenu(self) self._context_menu.addActions(self._toolbar_actions) self._context_menu.addActions( (self._export_STL_action, self._export_STEP_action)) def prepareLayout(self): self._splitter = splitter((self.tree, self.properties_editor), stretch_factors=(2, 1), orientation=Qt.Vertical) layout(self, (self._splitter, ), top_widget=self) self._splitter.show() def showMenu(self, position): self._context_menu.exec_(self.tree.viewport().mapToGlobal(position)) def menuActions(self): return {'Tools': [self._export_STL_action, self._export_STEP_action]} def toolbarActions(self): return self._toolbar_actions def addLines(self): origin = (0, 0, 0) ais_list = [] for name, color, direction in zip(('X', 'Y', 'Z'), ('red', 'lawngreen', 'blue'), ((1, 0, 0), (0, 1, 0), (0, 0, 1))): line_placement = Geom_Line( gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction))) line = AIS_Line(line_placement) line.SetColor(to_occ_color(color)) self.Helpers.addChild(ObjectTreeItem(name, ais=line)) ais_list.append(line) self.sigObjectsAdded.emit(ais_list) def _current_properties(self): current_params = {} for i in range(self.CQ.childCount()): child = self.CQ.child(i) current_params[child.properties['Name']] = child.properties return current_params def _restore_properties(self, obj, properties): for p in properties[obj.properties['Name']]: obj.properties[p.name()] = p.value() @pyqtSlot(dict, bool) @pyqtSlot(dict) def addObjects(self, objects, clean=False, root=None): if root is None: root = self.CQ request_fit_view = True if root.childCount() == 0 else False preserve_props = self.preferences['Preserve properties on reload'] if preserve_props: current_props = self._current_properties() if clean or self.preferences['Clear all before each run']: self.removeObjects() ais_list = [] #remove empty objects objects_f = { k: v for k, v in objects.items() if not is_obj_empty(v.shape) } for name, obj in objects_f.items(): ais, shape_display = make_AIS(obj.shape, obj.options) child = ObjectTreeItem(name, shape=obj.shape, shape_display=shape_display, ais=ais, sig=self.sigObjectPropertiesChanged) if preserve_props and name in current_props: self._restore_properties(child, current_props) if child.properties['Visible']: ais_list.append(ais) root.addChild(child) if request_fit_view: self.sigObjectsAdded[list, bool].emit(ais_list, True) else: self.sigObjectsAdded[list].emit(ais_list) @pyqtSlot(object, str, object) def addObject(self, obj, name='', options={}): root = self.CQ ais, shape_display = make_AIS(obj, options) root.addChild( ObjectTreeItem(name, shape=obj, shape_display=shape_display, ais=ais, sig=self.sigObjectPropertiesChanged)) self.sigObjectsAdded.emit([ais]) @pyqtSlot(list) @pyqtSlot() def removeObjects(self, objects=None): if objects: removed_items_ais = [self.CQ.takeChild(i).ais for i in objects] else: removed_items_ais = [ch.ais for ch in self.CQ.takeChildren()] self.sigObjectsRemoved.emit(removed_items_ais) @pyqtSlot(bool) def stashObjects(self, action: bool): if action: self._stash = self.CQ.takeChildren() removed_items_ais = [ch.ais for ch in self._stash] self.sigObjectsRemoved.emit(removed_items_ais) else: self.removeObjects() self.CQ.addChildren(self._stash) ais_list = [el.ais for el in self._stash] self.sigObjectsAdded.emit(ais_list) @pyqtSlot() def removeSelected(self): ixs = self.tree.selectedIndexes() rows = [ix.row() for ix in ixs] self.removeObjects(rows) def export(self, export_type, precision=None): items = self.tree.selectedItems() # if CQ models is selected get all children if [item for item in items if item is self.CQ]: CQ = self.CQ shapes = [CQ.child(i).shape for i in range(CQ.childCount())] # otherwise collect all selected children of CQ else: shapes = [item.shape for item in items if item.parent() is self.CQ] fname = get_save_filename(export_type) if fname is not '': export(shapes, export_type, fname, precision) @pyqtSlot() def handleSelection(self): items = self.tree.selectedItems() if len(items) == 0: self._export_STL_action.setEnabled(False) self._export_STEP_action.setEnabled(False) return # emit list of all selected ais objects (might be empty) ais_objects = [item.ais for item in items if item.parent() is self.CQ] self.sigAISObjectsSelected.emit(ais_objects) # handle context menu and emit last selected CQ object (if present) item = items[-1] if item.parent() is self.CQ: self._export_STL_action.setEnabled(True) self._export_STEP_action.setEnabled(True) self._clear_current_action.setEnabled(True) self.sigCQObjectSelected.emit(item.shape) self.properties_editor.setParameters(item.properties, showTop=False) self.properties_editor.setEnabled(True) elif item is self.CQ and item.childCount() > 0: self._export_STL_action.setEnabled(True) self._export_STEP_action.setEnabled(True) else: self._export_STL_action.setEnabled(False) self._export_STEP_action.setEnabled(False) self._clear_current_action.setEnabled(False) self.properties_editor.setEnabled(False) self.properties_editor.clear() @pyqtSlot(list) def handleGraphicalSelection(self, shapes): self.tree.clearSelection() CQ = self.CQ for i in range(CQ.childCount()): item = CQ.child(i) for shape in shapes: if item.shape_display.wrapped.IsEqual(shape): item.setSelected(True) @pyqtSlot(QTreeWidgetItem, int) def handleChecked(self, item, col): if type(item) is ObjectTreeItem: if item.checkState(0): item.properties['Visible'] = True else: item.properties['Visible'] = False
class maintest(QtCore.QObject): paramListUpdated = QtCore.Signal(list) def __init__(self): super(maintest, self).__init__() p = [{ 'name': 'Basic parameter data types', 'type': 'group', 'helpwnd': self.printhelp, 'children': [{ 'name': 'Module 1', 'type': 'list', 'values': { 'module 1': module(), 'module 2': module(), 'module 3': module() }, 'set': self.setmodule, 'help': '%namehdr%Boatload of text is possible here. Can use markup too with external help window.' }, { 'name': 'Rocks to Skips', 'type': 'int', 'help': 'Another help example', 'helpwnd': None }] }] self.params = Parameter.create(name='Test', type='group', children=p) ExtendedParameter.setupExtended(self.params) self.module = None self.t = ParameterTree() self.reloadParams() def printhelp(self, msg, obj): print msg def setmodule(self, module): print "Changing Module" self.module = module self.module.paramListUpdated.connect(self.reloadParams) self.paramListUpdated.emit(self.paramList()) self.reloadParams() def reloadParams(self): ExtendedParameter.reloadParams(self.paramList(), self.t) def reloadParamsBad(self): """The following is kept as a reminder you must NOT do this, or else objects get deleted""" #Notes about my wasted day to report a bug later when I have more time: # *Calling the .clear() causes stuff to be deleted # *In file parameterTypes.py at line 180 you have the following: # self.widget.sigChanged.disconnect(self.widgetValueChanged) # If self.widget has been deleted, which will happen once the ParameterTree is cleared, the call # to .disconnect causes a bad crash (sometimes). However - you can insert a call to 'self.widget.value()' # before the .disconnect() call. The self.widget.value() actually checks if the object was deleted, and if so # it raises a nice exception with a handy error message. lst = self.paramList() self.t.clear() for p in lst: self.t.addParameters(p) def paramList(self): p = [self.params] if self.module is not None: for a in self.module.paramList(): p.append(a) return p
class TraceManagerImport(QtFixes.QDialog): def __init__(self, parent=None): super(TraceManagerImport, self).__init__(parent) layout = QVBoxLayout() # Set dialog layout self.setLayout(layout) self.setWindowTitle("Add Existing Trace") self.openCfg = QPushButton("Load .cfg File") self.openCfg.clicked.connect(self.loadCfg) layout.addWidget(self.openCfg) self.modName = QComboBox() self.modName.addItem("Select format for manual mode only...") self.modName.addItem("ChipWhisperer/Native") self.modName.addItem("DPAContestv3") if TraceContainerMySQL is not None: self.modName.addItem("MySQL", TraceContainerMySQL) self.modName.currentIndexChanged.connect(self.traceTypeChanged) layout.addWidget(self.modName) self.paramTree = ParameterTree() layout.addWidget(self.paramTree) buts = QHBoxLayout() cancel = QPushButton("Close") cancel.clicked.connect(self.abort) ok = QPushButton("Import") ok.clicked.connect(self.accept) buts.addWidget(ok) buts.addWidget(cancel) layout.addLayout(buts) self.tmanager = None def abort(self): self.tmanager = None self.reject() def traceTypeChanged(self, newindx): self.openCfg.setEnabled(False) newTT = self.modName.itemData(newindx) self.tmanagerParams = newTT.getParamsClass(openMode=True) self.tmanager = newTT(self.tmanagerParams) self.paramTree.clear() self.paramTree.addParameters(self.tmanagerParams.getParams()._PyQtGraphParameter) def getTrace(self): return self.tmanager def updateConfigData(self): if self.tmanager is not None: self.tmanager.updateConfigData() def loadCfg(self, fname=None): if fname is None: fname, _ = QFileDialog.getOpenFileName(self, 'Open file', QSettings().value("trace_last_file"), '*.cfg') if fname: QSettings().setValue("trace_last_file", fname) self.modName.setEnabled(False) #Figure out what format this is in tc = TraceContainerConfig(fname) fmt = tc.attr("format") #Generate a temp class for getting parameters from fmtclass = chipwhisperer.common.traces.TraceContainerTypes.TraceContainerFormatList[fmt] #Use temp class to finally initilize our "good" version self.tmanager = fmtclass() self.tmanager.config.loadTrace(fname) self.tmanager.loadAllConfig() self.paramTree.clear() self.paramTree.addParameters(self.tmanager.getParams()._PyQtGraphParameter)
class TraceManagerImport(QtFixes.QDialog): def __init__(self, parent=None): super(TraceManagerImport, self).__init__(parent) layout = QVBoxLayout() # Set dialog layout self.setLayout(layout) self.setWindowTitle("Add Existing Trace") self.openCfg = QPushButton("Load .cfg File") self.openCfg.clicked.connect(self.loadCfg) layout.addWidget(self.openCfg) self.modName = QComboBox() self.modName.addItem("Select format for manual mode only...") self.modName.addItem("ChipWhisperer/Native") self.modName.addItem("DPAContestv3") if TraceContainerMySQL is not None: self.modName.addItem("MySQL", TraceContainerMySQL) self.modName.currentIndexChanged.connect(self.traceTypeChanged) layout.addWidget(self.modName) self.paramTree = ParameterTree() layout.addWidget(self.paramTree) buts = QHBoxLayout() cancel = QPushButton("Close") cancel.clicked.connect(self.abort) ok = QPushButton("Import") ok.clicked.connect(self.accept) buts.addWidget(ok) buts.addWidget(cancel) layout.addLayout(buts) self.tmanager = None def abort(self): self.tmanager = None self.reject() def traceTypeChanged(self, newindx): self.openCfg.setEnabled(False) newTT = self.modName.itemData(newindx) self.tmanagerParams = newTT.getParamsClass(openMode=True) self.tmanager = newTT(self.tmanagerParams) self.paramTree.clear() self.paramTree.addParameters( self.tmanagerParams.getParams()._PyQtGraphParameter) def getTrace(self): return self.tmanager def updateConfigData(self): if self.tmanager is not None: self.tmanager.updateConfigData() def loadCfg(self, fname=None): if fname is None: fname, _ = QFileDialog.getOpenFileName( self, 'Open file', QSettings().value("trace_last_file"), '*.cfg') if fname: QSettings().setValue("trace_last_file", fname) self.modName.setEnabled(False) #Figure out what format this is in tc = TraceContainerConfig(fname) fmt = tc.attr("format") #Generate a temp class for getting parameters from fmtclass = chipwhisperer.common.traces.TraceContainerTypes.TraceContainerFormatList[ fmt] #Use temp class to finally initilize our "good" version self.tmanager = fmtclass() self.tmanager.config.loadTrace(fname) self.tmanager.loadAllConfig() self.paramTree.clear() self.paramTree.addParameters( self.tmanager.getParams()._PyQtGraphParameter)
class MeasuringApp(CamObject, QtGui.QMainWindow): def __init__(self, *args, **kwargs): CamObject.__init__(self) QtGui.QMainWindow.__init__(self) self.resize(700, 150) self.setWindowTitle('Workpiece Dimensional Control') self.area = DockArea() self.setCentralWidget(self.area) # Window Docks self.camMenuDock = Dock('Camera Menu', size=(1, 1)) self.procMenuDock = Dock('Analysis Menu', size=(1, 1)) self.paramDock = Dock('Parameter Tree', size=(250, 150)) self.area.addDock(self.camMenuDock, 'right') self.area.addDock(self.procMenuDock, 'right') self.area.addDock(self.paramDock, 'left') # Parameter Tree self._parameter_tree = ParameterTree() self._parameter_tree.setWindowTitle('Parameter Tree') self.paramDock.addWidget(self._parameter_tree) # Camera Menu self._cam_menu = pg.LayoutWidget(parent=self.camMenuDock) self._cam_buttons = self.cam_buttons() [btn.resize(btn.minimumSizeHint()) for btn in self._cam_buttons] [self._cam_menu.addWidget(btn, row=i, col=0) for btn, i in zip( self._cam_buttons, range(len(self._cam_buttons)))] self.camMenuDock.addWidget(self._cam_menu) # Analysis menu self._proc_menu = pg.LayoutWidget(parent=self.procMenuDock) self._proc_buttons = self.process_buttons() [btn.resize(btn.minimumSizeHint()) for btn in self._proc_buttons] [self._proc_menu.addWidget(btn, row=i, col=1) for btn, i in zip( self._proc_buttons, range(len(self._proc_buttons)))] self.procMenuDock.addWidget(self._proc_menu) # Original frames window self._orig_frame = FrameWindow("Original") # Flowchart input preparation window self._prep_flow = FlowchartPrepWindow('Flowchart Input preparation') # Calibrate system window self._calib_window = FlowchartCalibrateWindow() # Calibration Parameters self._calib_params = None # Measurement preparation window self._measure_prep_window = FlowchartMeasureWindow() # Realtime measurement window self._realtime_measure_window = MeasureRealtimeWindow() # Show Main Window self.show() # Result Queue init self.preproc_queue = queue.Queue() # FPS display count self.fps_display_count = 0 def cam_buttons(self): """ Menu buttons as list """ # Camera create button cam_create = QtGui.QPushButton('Find Camera', parent=self._cam_menu) cam_create.clicked.connect(self.create) # Camera open button cam_open = QtGui.QPushButton('Open Camera', parent=self._cam_menu) cam_open.clicked.connect(self.open) # Camera print info cam_print_info = QtGui.QPushButton('Print Info', parent=self._cam_menu) cam_print_info.clicked.connect(self.print_info) # Camera start grabbing cam_start_grab = QtGui.QPushButton( 'Start Grabbing', parent=self._cam_menu) cam_start_grab.clicked.connect(self.start_grabbing) # Camera stop grabbing cam_stop_grab = QtGui.QPushButton( 'Stop Grabbing', parent=self._cam_menu) cam_stop_grab.clicked.connect(self.stop_grabbing) # Camera close cam_close = QtGui.QPushButton('Close Camera', parent=self._cam_menu) cam_close.clicked.connect(self.close) return [ cam_create, cam_open, cam_print_info, cam_start_grab, cam_stop_grab, cam_close ] def process_buttons(self): """ Process buttons as list """ # Grab single frame button proc_grab_frame = QtGui.QPushButton( 'Grab Frame', parent=self._proc_menu) proc_grab_frame.clicked.connect(self.grab_single_frame) proc_discard_frame = QtGui.QPushButton( 'Discard Frame', parent=self._proc_menu) proc_discard_frame.clicked.connect(self.discard_frame) proc_calib_sys = QtGui.QPushButton( 'Calibrate System', parent=self._proc_menu) proc_calib_sys.clicked.connect(self.calibrate_system) proc_set_calib_const = QtGui.QPushButton( 'Set Calibration Constants', parent=self._proc_menu) proc_set_calib_const.clicked.connect(self.set_calib_const) proc_measure_prep = QtGui.QPushButton( 'Measurement Preparation', parent=self._proc_menu) proc_measure_prep.clicked.connect(self.measurement_preparation) proc_realtime_start = QtGui.QPushButton( 'Start Measurement', parent=self._proc_menu) proc_realtime_start.clicked.connect(self.start_realtime_measurement) proc_realtime_stop = QtGui.QPushButton( 'Stop Measurement', parent=self._proc_menu) proc_realtime_stop.clicked.connect(self.stop_realtime_measurement) return [ proc_grab_frame, proc_discard_frame, proc_calib_sys, proc_set_calib_const, proc_measure_prep, proc_realtime_start, proc_realtime_stop ] def open(self): """ Reimplementation of open function to update GUI numbers and sliders """ super().open() self._parameter_tree.clear() self._parameter_tree.addParameters( CameraParams(name='Camera Parameters', cam_obj=self), showTop=True) def close(self): """ Reimplementation of close function to update GUI numbers and sliders """ super().close() self._parameter_tree.clear() def set_default_params(self): """ Reimplementation of set_default_params function to update GUI """ super().set_default_params() self._cam_gain.value = self.cam.Gain() self._cam_exposure.value = self.cam.ExposureTime() self._cam_width.value = self.cam.Width.GetValue() self._cam_height.value = self.cam.Height.GetValue() self._cam_offsetx.value = self.cam.OffsetX.GetValue() self._cam_offsety.value = self.cam.OffsetY.GetValue() def start_grabbing(self, strategy=pylon.GrabStrategy_LatestImageOnly): """ Reimplementation of start_grabbing to show new window """ if not self.cam.IsGrabbing(): self._orig_frame.show() self.frame_queue = queue.Queue() fg_hndl = FrameGrabEventHandler(self.frame_queue) # Frame grab event registering self.cam.RegisterImageEventHandler( fg_hndl, pylon.RegistrationMode_ReplaceAll, pylon.Cleanup_Delete ) super().start_grabbing(strategy) self.frame_burst(graphicsObject=self._orig_frame) def stop_grabbing(self): """ Reimplementation of stop_grabbing to close new window """ if self.cam.IsGrabbing(): super().stop_grabbing() self._orig_frame.close() def frame_burst(self, graphicsObject, updateFrame=True, updateFPS=True, updateMeasurementResult=False, numProc=1, fps_display=5): """ Grab frames from camera using software trigger, scheduling and event handler. frame_burst reimplementation for FPS, frame and measurement result update. :param graphicsObject: object which methods are called and graphics updated :param updateFrame: bool :param updateFPS: bool :param updateMeasurementResult: bool :param numProc: 1 :param fps_display: int """ if self.fps_count == 0: if updateFPS: # Update graphicsObject FPS label graphicsObject.update_fps(self.fps) self.fps_start = pg.ptime.time() if self.cam.IsGrabbing(): for _ in range(numProc): if self.cam.WaitForFrameTriggerReady( 300, pylon.TimeoutHandling_ThrowException): self.cam.ExecuteSoftwareTrigger() frames = [self.frame_queue.get() for _ in range(numProc)] self.fps_display_count += 1 # print('FPS_display_count: {}'.format(self.fps_display_count)) if updateFrame and not (self.fps_display_count % fps_display): self.fps_display_count = 0 # Update graphicsObject ImageItem [graphicsObject.update_frame(frame) for frame in frames] self.fps_count += numProc if updateMeasurementResult: while not self.preproc_queue.empty(): graphicsObject.update_measurement_result( self._measure_prep_window.fc_process( dip.Image(self.preproc_queue.get()) )) # Average FPS on number of frames if self.fps_count >= 10: fps_time = pg.ptime.time() - self.fps_start self.fps = self.fps_count / fps_time print("Camera FPS: {:.2f}".format( self.cam.ResultingFrameRate.GetValue())) print("FPS: {:.2f}".format(self.fps)) self.fps_count = 0 QtCore.QTimer.singleShot( 1, lambda : self.frame_burst(graphicsObject, updateFrame, updateFPS, numProc, )) def grab_single_frame(self): self._prep_flow.set_frame(self._orig_frame.grab_single_frame()) self._prep_flow.show() def discard_frame(self): self._prep_flow.close() def calibrate_system(self): """ Show Calibration flowchart and set input frame """ self._calib_window.setInput(self._prep_flow.get_dip_slice()) self._calib_window.show() def set_calib_const(self): """ Set and show calibration parameters in parameterTree """ standard_dict = self._calib_window.output() self._calib_params = CalibrationParams( name='Calibration Parameters', standard_dict=standard_dict) self._parameter_tree.clear() self._parameter_tree.addParameters(self._calib_params, showTop=True) def measurement_preparation(self): """ Show Measurement preparation flowchart and set input frame """ self._measure_prep_window.setInput( self._prep_flow.get_dip_slice(), self._calib_params.obj_mm_px.value() ) self._measure_prep_window.show() def start_realtime_measurement( self, strategy=pylon.GrabStrategy_LatestImageOnly): """ Show window for realtime measurement and start grabbing frames from cam """ if not self.cam.IsGrabbing(): self._realtime_measure_window.show() procParallel = ProcessParallel( self._measure_prep_window, numberProc=3 ) # Queue for storing frames self.frame_queue = queue.Queue() # Queue for storing measurement result list self.preproc_queue = queue.Queue() self.fm_hndl = FrameMeasureEventHandler( self.frame_queue, self.preproc_queue, procParallel ) # Frame grab event registering self.cam.RegisterImageEventHandler( self.fm_hndl, pylon.RegistrationMode_ReplaceAll, pylon.Cleanup_Delete ) super().start_grabbing(strategy) self.frame_burst( graphicsObject=self._realtime_measure_window, updateFPS=False, updateMeasurementResult=True, fps_display=10, # numProc=procParallel.getNumProc() ) def stop_realtime_measurement(self): """ Close window for realtime measurement and stop grabbing frames from cam """ if self.cam.IsGrabbing(): super().stop_grabbing() self._realtime_measure_window.close() self.cam.DeregisterImageEventHandler(self.fm_hndl)
class ObjectTree(QWidget, ComponentMixin): name = 'Object Tree' _stash = [] preferences = Parameter.create(name='Preferences', children=[{ 'name': 'Clear all before each run', 'type': 'bool', 'value': True }, { 'name': 'STL precision', 'type': 'float', 'value': .1 }]) sigObjectsAdded = pyqtSignal(list) sigObjectsRemoved = pyqtSignal(list) sigCQObjectSelected = pyqtSignal(object) sigItemChanged = pyqtSignal(QTreeWidgetItem, int) sigObjectPropertiesChanged = pyqtSignal() def __init__(self, parent): super(ObjectTree, self).__init__(parent) self.tree = tree = QTreeWidget(self) self.properties_editor = ParameterTree(self) tree.setHeaderHidden(True) tree.setItemsExpandable(False) tree.setRootIsDecorated(False) tree.setContextMenuPolicy(Qt.ActionsContextMenu) #forward itemChanged singal tree.itemChanged.connect(\ lambda item,col: self.sigItemChanged.emit(item,col)) #handle visibility changes form tree tree.itemChanged.connect(self.handleChecked) self.CQ = CQRootItem() self.Imports = ImportRootItem() self.Helpers = HelpersRootItem() root = tree.invisibleRootItem() root.addChild(self.CQ) root.addChild(self.Imports) root.addChild(self.Helpers) self._export_STL_action = \ QAction('Export as STL', self, enabled=False, triggered=lambda: \ self.export('*stl','stl', self.preferences['STL precision'])) self._export_STEP_action = \ QAction('Export as SETP', self, enabled=False, triggered=lambda: \ self.export('*step','step',[])) self._clear_current_action = QAction(icon('delete'), 'Clear current', self, enabled=False, triggered=self.removeSelected) self._toolbar_actions = \ [QAction(icon('delete-many'),'Clear all',self,triggered=self.removeObjects), self._clear_current_action,] self.prepareMenu() tree.itemSelectionChanged.connect(self.handleSelection) tree.customContextMenuRequested.connect(self.showMenu) self.prepareLayout() def prepareMenu(self): self.tree.setContextMenuPolicy(Qt.CustomContextMenu) self._context_menu = QMenu(self) self._context_menu.addActions(self._toolbar_actions) self._context_menu.addActions( (self._export_STL_action, self._export_STEP_action)) def prepareLayout(self): self._splitter = splitter((self.tree, self.properties_editor), stretch_factors=(2, 1), orientation=Qt.Vertical) layout(self, (self._splitter, ), top_widget=self) self._splitter.show() def showMenu(self, position): item = self.tree.selectedItems()[-1] if item.parent() is self.CQ: self._export_STL_action.setEnabled(True) else: self._export_STL_action.setEnabled(False) self._context_menu.exec_(self.tree.viewport().mapToGlobal(position)) def menuActions(self): return {'Tools': [self._export_STL_action]} def toolbarActions(self): return self._toolbar_actions def addLines(self): origin = (0, 0, 0) ais_list = [] for name, color, direction in zip(('X', 'Y', 'Z'), (RED, GREEN, BLUE), ((1, 0, 0), (0, 1, 0), (0, 0, 1))): line_placement = Geom_Line( gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction))) line = AIS_Line(line_placement.GetHandle()) line.SetColor(color) self.Helpers.addChild(ObjectTreeItem(name, ais=line)) ais_list.append(line) self.sigObjectsAdded.emit(ais_list) self.tree.expandToDepth(1) @pyqtSlot(dict, bool) @pyqtSlot(dict) def addObjects(self, objects, clean=False, root=None, alpha=0.): if clean or self.preferences['Clear all before each run']: self.removeObjects() ais_list = [] #if root is None: root = self.CQ #remove Vector objects objects_f = \ {k:v for k,v in objects.items() if type(v.val()) not in (Vector,)} for name, shape in objects_f.items(): ais = make_AIS(shape) ais.SetTransparency(alpha) ais_list.append(ais) root.addChild( ObjectTreeItem(name, shape=shape, ais=ais, sig=self.sigObjectPropertiesChanged)) self.sigObjectsAdded.emit(ais_list) @pyqtSlot(object, str, float) def addObject( self, obj, name='', alpha=.0, ): root = self.CQ ais = make_AIS(obj) ais.SetTransparency(alpha) root.addChild( ObjectTreeItem(name, shape=obj, ais=ais, sig=self.sigObjectPropertiesChanged)) self.sigObjectsAdded.emit([ais]) @pyqtSlot(list) @pyqtSlot() def removeObjects(self, objects=None): if objects: removed_items_ais = [self.CQ.takeChild(i).ais for i in objects] else: removed_items_ais = [ch.ais for ch in self.CQ.takeChildren()] self.sigObjectsRemoved.emit(removed_items_ais) @pyqtSlot(bool) def stashObjects(self, action: bool): if action: self._stash = self.CQ.takeChildren() removed_items_ais = [ch.ais for ch in self._stash] self.sigObjectsRemoved.emit(removed_items_ais) else: self.removeObjects() self.CQ.addChildren(self._stash) ais_list = [el.ais for el in self._stash] self.sigObjectsAdded.emit(ais_list) @pyqtSlot() def removeSelected(self): ixs = self.tree.selectedIndexes() rows = [ix.row() for ix in ixs] self.removeObjects(rows) def export(self, file_wildcard, export_type, precision=None): item = self.tree.selectedItems()[-1] if item.parent() is self.CQ: shape = item.shape else: return fname, _ = QFileDialog.getSaveFileName(self, filter=file_wildcard) if fname is not '': export(shape, export_type, fname, precision) @pyqtSlot() def handleSelection(self): item = self.tree.selectedItems()[-1] if item.parent() is self.CQ: self._export_STL_action.setEnabled(True) self._export_STEP_action.setEnabled(True) self._clear_current_action.setEnabled(True) self.sigCQObjectSelected.emit(item.shape) self.properties_editor.setParameters(item.properties, showTop=False) self.properties_editor.setEnabled(True) else: self._export_STL_action.setEnabled(False) self._export_STEP_action.setEnabled(False) self._clear_current_action.setEnabled(False) self.properties_editor.setEnabled(False) self.properties_editor.clear() @pyqtSlot(QTreeWidgetItem, int) def handleChecked(self, item, col): if type(item) is ObjectTreeItem: if item.checkState(0): item.properties['Visible'] = True else: item.properties['Visible'] = False