def __init__(self, view): QtGui.QMenu.__init__(self) self.view = view self.valid = False ## tells us whether the ui needs to be updated self.setTitle("ViewBox options") self.viewAll = QtGui.QAction("View All", self) self.viewAll.triggered.connect(self.autoRange) self.addAction(self.viewAll) self.axes = [] self.ctrl = [] self.widgetGroups = [] self.dv = QtGui.QDoubleValidator(self) for axis in 'XY': m = QtGui.QMenu() m.setTitle("%s Axis" % axis) w = QtGui.QWidget() ui = AxisCtrlTemplate() ui.setupUi(w) a = QtGui.QWidgetAction(self) a.setDefaultWidget(w) m.addAction(a) self.addMenu(m) self.axes.append(m) self.ctrl.append(ui) wg = WidgetGroup(w) self.widgetGroups.append(w) connects = [ (ui.mouseCheck.toggled, 'MouseToggled'), (ui.manualRadio.clicked, 'ManualClicked'), (ui.minText.editingFinished, 'MinTextChanged'), (ui.maxText.editingFinished, 'MaxTextChanged'), (ui.autoRadio.clicked, 'AutoClicked'), (ui.autoPercentSpin.valueChanged, 'AutoSpinChanged'), (ui.linkCombo.currentIndexChanged, 'LinkComboChanged'), ] for sig, fn in connects: sig.connect(getattr(self, axis.lower()+fn)) ## exporting is handled by GraphicsScene now #self.export = QtGui.QMenu("Export") #self.setExportMethods(view.exportMethods) #self.addMenu(self.export) self.leftMenu = QtGui.QMenu("Mouse Mode") group = QtGui.QActionGroup(self) pan = self.leftMenu.addAction("3 button", self.set3ButtonMode) zoom = self.leftMenu.addAction("1 button", self.set1ButtonMode) pan.setCheckable(True) zoom.setCheckable(True) pan.setActionGroup(group) zoom.setActionGroup(group) self.mouseModes = [pan, zoom] self.addMenu(self.leftMenu) self.view.sigStateChanged.connect(self.viewStateChanged) self.updateState()
def __init__(self, view): QtGui.QMenu.__init__(self) self.view = weakref.ref( view ) ## keep weakref to view to avoid circular reference (don't know why, but this prevents the ViewBox from being collected) self.valid = False ## tells us whether the ui needs to be updated self.viewMap = weakref.WeakValueDictionary( ) ## weakrefs to all views listed in the link combos self.setTitle("ViewBox options") self.viewAll = QtGui.QAction("View All", self) self.viewAll.triggered.connect(self.autoRange) self.addAction(self.viewAll) self.axes = [] self.ctrl = [] self.widgetGroups = [] self.dv = QtGui.QDoubleValidator(self) for axis in 'XY': m = QtGui.QMenu() m.setTitle("%s Axis" % axis) w = QtGui.QWidget() ui = AxisCtrlTemplate() ui.setupUi(w) a = QtGui.QWidgetAction(self) a.setDefaultWidget(w) m.addAction(a) self.addMenu(m) self.axes.append(m) self.ctrl.append(ui) wg = WidgetGroup(w) self.widgetGroups.append(w) connects = [(ui.mouseCheck.toggled, 'MouseToggled'), (ui.manualRadio.clicked, 'ManualClicked'), (ui.minText.editingFinished, 'MinTextChanged'), (ui.maxText.editingFinished, 'MaxTextChanged'), (ui.autoRadio.clicked, 'AutoClicked'), (ui.autoPercentSpin.valueChanged, 'AutoSpinChanged'), (ui.linkCombo.currentIndexChanged, 'LinkComboChanged'), (ui.autoPanCheck.toggled, 'AutoPanToggled'), (ui.visibleOnlyCheck.toggled, 'VisibleOnlyToggled')] for sig, fn in connects: sig.connect(getattr(self, axis.lower() + fn)) self.ctrl[0].invertCheck.hide() ## no invert for x-axis self.ctrl[1].invertCheck.toggled.connect(self.yInvertToggled) ## exporting is handled by GraphicsScene now #self.export = QtGui.QMenu("Export") #self.setExportMethods(view.exportMethods) #self.addMenu(self.export) self.leftMenu = QtGui.QMenu("Mouse Mode") group = QtGui.QActionGroup(self) pan = self.leftMenu.addAction("3 button", self.set3ButtonMode) zoom = self.leftMenu.addAction("1 button", self.set1ButtonMode) pan.setCheckable(True) zoom.setCheckable(True) pan.setActionGroup(group) zoom.setActionGroup(group) self.mouseModes = [pan, zoom] self.addMenu(self.leftMenu) self.view().sigStateChanged.connect(self.viewStateChanged) self.updateState()
def generateUi(opts): """Convenience function for generating common UI types""" widget = QtGui.QWidget() l = QtGui.QFormLayout() l.setSpacing(0) widget.setLayout(l) ctrls = {} row = 0 for opt in opts: if len(opt) == 2: k, t = opt o = {} elif len(opt) == 3: k, t, o = opt else: raise Exception("Widget specification must be (name, type) or (name, type, {opts})") if t == 'intSpin': w = QtGui.QSpinBox() if 'max' in o: w.setMaximum(o['max']) if 'min' in o: w.setMinimum(o['min']) if 'value' in o: w.setValue(o['value']) elif t == 'doubleSpin': w = QtGui.QDoubleSpinBox() if 'max' in o: w.setMaximum(o['max']) if 'min' in o: w.setMinimum(o['min']) if 'value' in o: w.setValue(o['value']) elif t == 'spin': w = SpinBox() w.setOpts(**o) elif t == 'check': w = QtGui.QCheckBox() if 'checked' in o: w.setChecked(o['checked']) elif t == 'combo': w = QtGui.QComboBox() for i in o['values']: w.addItem(i) #elif t == 'colormap': #w = ColorMapper() elif t == 'color': w = ColorButton() else: raise Exception("Unknown widget type '%s'" % str(t)) if 'tip' in o: w.setToolTip(o['tip']) w.setObjectName(k) l.addRow(k, w) if o.get('hidden', False): w.hide() label = l.labelForField(w) label.hide() ctrls[k] = w w.rowNum = row row += 1 group = WidgetGroup(widget) return widget, group, ctrls
def __init__(self, parent=None, name=None, labels=None, title=None, viewBox=None, axisItems=None, enableMenu=True, **kargs): """ Create a new PlotItem. All arguments are optional. Any extra keyword arguments are passed to PlotItem.plot(). ============== ========================================================================================== **Arguments** *title* Title to display at the top of the item. Html is allowed. *labels* A dictionary specifying the axis labels to display:: {'left': (args), 'bottom': (args), ...} The name of each axis and the corresponding arguments are passed to :func:`PlotItem.setLabel() <pyqtgraph.PlotItem.setLabel>` Optionally, PlotItem my also be initialized with the keyword arguments left, right, top, or bottom to achieve the same effect. *name* Registers a name for this view so that others may link to it *viewBox* If specified, the PlotItem will be constructed with this as its ViewBox. *axisItems* Optional dictionary instructing the PlotItem to use pre-constructed items for its axes. The dict keys must be axis names ('left', 'bottom', 'right', 'top') and the values must be instances of AxisItem (or at least compatible with AxisItem). ============== ========================================================================================== """ GraphicsWidget.__init__(self, parent) self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) ## Set up control buttons path = os.path.dirname(__file__) #self.autoImageFile = os.path.join(path, 'auto.png') #self.lockImageFile = os.path.join(path, 'lock.png') self.autoBtn = ButtonItem(pyqtgraph.pixmaps.getPixmap('auto'), 14, self) self.autoBtn.mode = 'auto' self.autoBtn.clicked.connect(self.autoBtnClicked) #self.autoBtn.hide() self.buttonsHidden = False ## whether the user has requested buttons to be hidden self.mouseHovering = False self.layout = QtGui.QGraphicsGridLayout() self.layout.setContentsMargins(1,1,1,1) self.setLayout(self.layout) self.layout.setHorizontalSpacing(0) self.layout.setVerticalSpacing(0) if viewBox is None: viewBox = ViewBox() self.vb = viewBox self.vb.sigStateChanged.connect(self.viewStateChanged) self.setMenuEnabled(enableMenu, enableMenu) ## en/disable plotitem and viewbox menus if name is not None: self.vb.register(name) self.vb.sigRangeChanged.connect(self.sigRangeChanged) self.vb.sigXRangeChanged.connect(self.sigXRangeChanged) self.vb.sigYRangeChanged.connect(self.sigYRangeChanged) self.layout.addItem(self.vb, 2, 1) self.alpha = 1.0 self.autoAlpha = True self.spectrumMode = False self.legend = None ## Create and place axis items if axisItems is None: axisItems = {} self.axes = {} for k, pos in (('top', (1,1)), ('bottom', (3,1)), ('left', (2,0)), ('right', (2,2))): axis = axisItems.get(k, AxisItem(orientation=k)) axis.linkToView(self.vb) self.axes[k] = {'item': axis, 'pos': pos} self.layout.addItem(axis, *pos) axis.setZValue(-1000) axis.setFlag(axis.ItemNegativeZStacksBehindParent) self.titleLabel = LabelItem('', size='11pt') self.layout.addItem(self.titleLabel, 0, 1) self.setTitle(None) ## hide for i in range(4): self.layout.setRowPreferredHeight(i, 0) self.layout.setRowMinimumHeight(i, 0) self.layout.setRowSpacing(i, 0) self.layout.setRowStretchFactor(i, 1) for i in range(3): self.layout.setColumnPreferredWidth(i, 0) self.layout.setColumnMinimumWidth(i, 0) self.layout.setColumnSpacing(i, 0) self.layout.setColumnStretchFactor(i, 1) self.layout.setRowStretchFactor(2, 100) self.layout.setColumnStretchFactor(1, 100) ## Wrap a few methods from viewBox for m in [ 'setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', 'setAutoVisible', 'setRange', 'autoRange', 'viewRect', 'viewRange', 'setMouseEnabled', 'enableAutoRange', 'disableAutoRange', 'setAspectLocked', 'invertY', 'register', 'unregister']: ## NOTE: If you update this list, please update the class docstring as well. setattr(self, m, getattr(self.vb, m)) self.items = [] self.curves = [] self.itemMeta = weakref.WeakKeyDictionary() self.dataItems = [] self.paramList = {} self.avgCurves = {} ### Set up context menu w = QtGui.QWidget() self.ctrl = c = Ui_Form() c.setupUi(w) dv = QtGui.QDoubleValidator(self) menuItems = [ ('Transforms', c.transformGroup), ('Downsample', c.decimateGroup), ('Average', c.averageGroup), ('Alpha', c.alphaGroup), ('Grid', c.gridGroup), ('Points', c.pointsGroup), ] self.ctrlMenu = QtGui.QMenu() self.ctrlMenu.setTitle('Plot Options') self.subMenus = [] for name, grp in menuItems: sm = QtGui.QMenu(name) act = QtGui.QWidgetAction(self) act.setDefaultWidget(grp) sm.addAction(act) self.subMenus.append(sm) self.ctrlMenu.addMenu(sm) self.stateGroup = WidgetGroup() for name, w in menuItems: self.stateGroup.autoAdd(w) self.fileDialog = None c.alphaGroup.toggled.connect(self.updateAlpha) c.alphaSlider.valueChanged.connect(self.updateAlpha) c.autoAlphaCheck.toggled.connect(self.updateAlpha) c.xGridCheck.toggled.connect(self.updateGrid) c.yGridCheck.toggled.connect(self.updateGrid) c.gridAlphaSlider.valueChanged.connect(self.updateGrid) c.fftCheck.toggled.connect(self.updateSpectrumMode) c.logXCheck.toggled.connect(self.updateLogMode) c.logYCheck.toggled.connect(self.updateLogMode) c.downsampleSpin.valueChanged.connect(self.updateDownsampling) c.downsampleCheck.toggled.connect(self.updateDownsampling) c.autoDownsampleCheck.toggled.connect(self.updateDownsampling) c.subsampleRadio.toggled.connect(self.updateDownsampling) c.meanRadio.toggled.connect(self.updateDownsampling) c.clipToViewCheck.toggled.connect(self.updateDownsampling) self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked) self.ctrl.averageGroup.toggled.connect(self.avgToggled) self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation) self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation) self.hideAxis('right') self.hideAxis('top') self.showAxis('left') self.showAxis('bottom') if labels is None: labels = {} for label in list(self.axes.keys()): if label in kargs: labels[label] = kargs[label] del kargs[label] for k in labels: if isinstance(labels[k], basestring): labels[k] = (labels[k],) self.setLabel(k, *labels[k]) if title is not None: self.setTitle(title) if len(kargs) > 0: self.plot(**kargs)
async def process_profile_data(self): self.profile.connect(self.profile_addr) while True: await self.profile.recv_string() name = await self.profile.recv_string() data_type = await self.profile.recv_string() data = await self.profile.recv_serialized(self.deserializer, copy=False) if data_type == "profile": heartbeat = data['heartbeat'] version = data['version'] if heartbeat not in self.heartbeat_data: if version not in self.metadata: continue metadata = self.metadata[version] self.heartbeat_data[heartbeat] = HeartbeatData( data['heartbeat'], metadata) heartbeat_data = self.heartbeat_data[heartbeat] if name.startswith('worker'): heartbeat_data.add_worker_data(name, data['times']) elif name.startswith('localCollector'): heartbeat_data.add_local_collector_data( name, data['times']) elif name.startswith('globalCollector'): heartbeat_data.add_global_collector_data(data['times']) if version > self.current_version: self.current_version = version self.percent_per_heartbeat_data = collections.defaultdict( lambda: 0) if self.percent_per_heartbeat_trace: self.percent_per_heartbeat.removeItem( self.percent_per_heartbeat_trace) self.percent_per_heartbeat_trace = None parents = set() for k, v in self.metadata[version].items(): parent = v['parent'] parents.add(parent) if parent not in self.enabled_nodes: widget = QtWidgets.QCheckBox(self.widget) widget.node = parent widget.setCheckState(QtCore.Qt.Checked) self.enabled_nodes[parent] = widget self.trace_layout.addRow(parent, widget) deleted_nodes = self.parents.difference(parents) for node in deleted_nodes: self.trace_layout.removeRow( self.enabled_nodes[node]) del self.enabled_nodes[node] trace = self.time_per_heartbeat_traces[node] self.time_per_heartbeat.removeItem(trace) self.time_per_heartbeat_legend.removeItem(trace) del self.time_per_heartbeat_traces[node] del self.time_per_heartbeat_data[node] self.parents = parents self.trace_group.sigChanged.disconnect( self.state_changed) self.trace_group = WidgetGroup() self.trace_group.sigChanged.connect(self.state_changed) for node, ctrl in self.enabled_nodes.items(): self.trace_group.addWidget(ctrl, node) self.time_per_heartbeat_data["heartbeat"][-1] = heartbeat self.time_per_heartbeat_data["heartbeat"] = np.roll( self.time_per_heartbeat_data["heartbeat"], -1) total = 1 for node, time in heartbeat_data.total_time_per_heartbeat.items( ): self.time_per_heartbeat_data[node][-1] = time self.time_per_heartbeat_data[node] = np.roll( self.time_per_heartbeat_data[node], -1) self.percent_per_heartbeat_data[ node] = time / heartbeat_data.total_heartbeat_time total -= time / heartbeat_data.total_heartbeat_time self.percent_per_heartbeat_data['Transfer'] = total i = 0 for node, times in self.time_per_heartbeat_data.items(): if node == "heartbeat": continue if node not in self.time_per_heartbeat_traces: symbol, color = symbols_colors[i] self.time_per_heartbeat_traces[ node] = self.time_per_heartbeat.plot( x=self. time_per_heartbeat_data["heartbeat"], y=times, name=node, symbol=symbol, symbolBrush=color) else: self.time_per_heartbeat_traces[node].setData( x=self.time_per_heartbeat_data["heartbeat"], y=times) i += 1 nodes, times = zip( *self.percent_per_heartbeat_data.items()) if self.percent_per_heartbeat_trace is None: x = np.arange(len(nodes)) self.percent_per_heartbeat_trace = pg.BarGraphItem( x=x, height=times, width=1, brush='b') self.percent_per_heartbeat.addItem( self.percent_per_heartbeat_trace) xticks = dict(zip(x, nodes)) ax = self.percent_per_heartbeat.getAxis('bottom') ax.setTicks([xticks.items()]) else: self.percent_per_heartbeat_trace.setOpts(height=times) self.heartbeats_per_second_data[ -1] = 1 / heartbeat_data.total_heartbeat_time self.heartbeats_per_second_data = np.roll( self.heartbeats_per_second_data, -1) if self.heartbeats_per_second_trace is None: symbol, color = symbols_colors[0] self.heartbeats_per_second_trace = self.heartbeats_per_second.plot( x=self.time_per_heartbeat_data["heartbeat"], y=self.heartbeats_per_second_data, symbol=symbol, symbolBrush=color) else: self.heartbeats_per_second_trace.setData( x=self.time_per_heartbeat_data["heartbeat"], y=self.heartbeats_per_second_data) now = dt.datetime.now() now = now.strftime("%H:%M:%S") last_updated = f"Last Updated: {now}" self.last_updated.setText(last_updated) text = f"Seconds/Heartbeat: {heartbeat_data.total_heartbeat_time:.6f}<br/>Heartbeat: {heartbeat}" self.total_heartbeat_time.setText(text) text = f"Heartbeats/Second: {1/heartbeat_data.total_heartbeat_time:.0f}<br/>Heartbeat: {heartbeat}" self.heartbeat_per_second.setText(text) del self.heartbeat_data[heartbeat] elif data_type == "metadata": graph_name = data['graph'] version = data['version'] logger.info("Received metadata for %s v%d", graph_name, version) self.metadata[version] = data['metadata']
def __init__(self, broker_addr="", profile_addr="", graph_name="graph", loop=None): super().__init__() if loop is None: self.app = QtGui.QApplication([]) loop = QEventLoop(self.app) asyncio.set_event_loop(loop) self.ctx = zmq.asyncio.Context() if broker_addr: self.broker = self.ctx.socket(zmq.SUB) self.broker.setsockopt_string(zmq.SUBSCRIBE, 'profiler') self.broker.connect(broker_addr) else: self.broker = None self.graph_name = graph_name self.profile_addr = profile_addr self.profile = self.ctx.socket(zmq.SUB) self.profile.setsockopt_string(zmq.SUBSCRIBE, self.graph_name) self.task = None self.deserializer = Deserializer() self.current_version = 0 self.metadata = {} # {version : metadata} self.parents = set() self.heartbeat_data = {} self.widget = QtWidgets.QWidget() self.layout = QtGui.QGridLayout(self.widget) self.widget.setLayout(self.layout) self.enabled_nodes = {} self.trace_layout = QtGui.QFormLayout(self.widget) hbox = QtWidgets.QHBoxLayout(self.widget) selectAll = QtWidgets.QPushButton("Select All", self.widget) selectAll.clicked.connect(self.selectAll) unselectAll = QtWidgets.QPushButton("Unselect All", self.widget) unselectAll.clicked.connect(self.unselectAll) hbox.addWidget(selectAll) hbox.addWidget(unselectAll) self.trace_layout.addRow(hbox) self.trace_group = WidgetGroup() self.trace_group.sigChanged.connect(self.state_changed) self.layout.addLayout(self.trace_layout, 0, 0, -1, 1) self.graphicsLayoutWidget = pg.GraphicsLayoutWidget() self.layout.addWidget(self.graphicsLayoutWidget, 0, 1, -1, -1) self.time_per_heartbeat = self.graphicsLayoutWidget.addPlot(row=0, col=0) self.time_per_heartbeat.showGrid(True, True) self.time_per_heartbeat.setLabel('bottom', "Heartbeat") self.time_per_heartbeat.setLabel('left', "Time (Sec)") self.time_per_heartbeat_data = collections.defaultdict( lambda: np.array([np.nan] * 100)) self.time_per_heartbeat_traces = {} self.time_per_heartbeat_legend = self.time_per_heartbeat.addLegend() self.heartbeats_per_second = self.graphicsLayoutWidget.addPlot(row=0, col=1) self.heartbeats_per_second.showGrid(True, True) self.heartbeats_per_second.setLabel('bottom', "Heartbeat") self.heartbeats_per_second.setLabel('left', "Heartbeats/Second") self.heartbeats_per_second_data = np.array([np.nan] * 100) self.heartbeats_per_second_trace = None self.percent_per_heartbeat = self.graphicsLayoutWidget.addPlot( row=1, col=0, rowspan=1, colspan=2) self.percent_per_heartbeat.showGrid(True, True) self.percent_per_heartbeat_trace = None self.last_updated = pg.LabelItem( parent=self.time_per_heartbeat.getViewBox()) self.total_heartbeat_time = pg.LabelItem( parent=self.percent_per_heartbeat.getViewBox()) self.heartbeat_per_second = pg.LabelItem( parent=self.heartbeats_per_second.getViewBox()) self.win = ProfilerWindow(self) self.win.setWindowTitle('Profiler') self.win.setCentralWidget(self.widget) self.win.show() with loop: loop.run_until_complete( asyncio.gather(self.process_broker_message(), self.monitor()))