class MPLPlotWidget(PlotWidget): """ Base class for matplotlib-based plot widgets. Per default, add a canvas and the matplotlib NavBar. """ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): super().__init__(parent=parent) #: the plot widget self.plot = MPLPlot() #: the matplotlib toolbar self.mplBar = NavBar(self.plot, self) self.addMplBarOptions() defaultIconSize = int(16 * dpiScalingFactor(self)) self.mplBar.setIconSize(QtCore.QSize(defaultIconSize, defaultIconSize)) layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.plot) layout.addWidget(self.mplBar) self.setLayout(layout) def setMeta(self, data: DataDictBase) -> None: """Add meta info contained in the data to the figure. :param data: data object containing the meta information if meta field ``title`` or ``info`` are in the data object, then they will be added as text info to the figure. """ if data.has_meta('title'): self.plot.setFigureTitle(data.meta_val('title')) if data.has_meta('info'): self.plot.setFigureInfo(data.meta_val('info')) all_meta = {} for k, v in sorted(data.meta_items()): this_meta = str(v) if len(this_meta) > 200: this_meta = this_meta[:200] + "..." all_meta[k] = this_meta self.plot.setMetaInfo(all_meta) def addMplBarOptions(self) -> None: """Add options for displaying ``info`` meta data and copying the figure to the clipboard to the plot toolbar.""" self.mplBar.addSeparator() infoAction = self.mplBar.addAction('Show Info') infoAction.setCheckable(True) infoAction.toggled.connect(self.plot.setShowInfo) self.mplBar.addSeparator() self.mplBar.addAction('Copy Figure', self.plot.toClipboard) self.mplBar.addAction('Copy Meta', self.plot.metaToClipboard)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._molecule = nx.Graph() self._mapping = MappingModel(self._molecule) layout = QHBoxLayout() canvas_layout = QVBoxLayout() self.embeddings_box = QComboBox() self.embeddings_box.addItems(EMBEDDINGS.keys()) self.embeddings_box.setEditable(False) self.embeddings_box.currentTextChanged.connect(self._set_embedding) canvas_layout.addWidget(self.embeddings_box) self.figure = Figure() self.canvas = MappingView(self.figure) canvas_layout.addWidget(self.canvas) canvas_toolbar = NavigationToolbar(self.canvas, self.canvas, False) canvas_toolbar.addSeparator() hide_mapping = QAction('Hide Mapping', self, icon=self.style().standardIcon( QStyle.SP_DesktopIcon)) hide_mapping.triggered.connect(self.canvas.hide_mapping) canvas_toolbar.addAction(hide_mapping) remove_mapping = QAction('Remove Mapping', self, icon=self.style().standardIcon( QStyle.SP_DialogDiscardButton)) remove_mapping.triggered.connect(self.canvas.remove_mapping) canvas_toolbar.addAction(remove_mapping) canvas_layout.addWidget(canvas_toolbar, alignment=Qt.AlignBottom) layout.addLayout(canvas_layout) self._table = QTableView() self._table.horizontalHeader().setStretchLastSection(True) layout.addWidget(self._table) self.setLayout(layout) self._set_embedding(self.embeddings_box.currentText())
class PlotWidget(QtWidgets.QWidget): """ Qt widget to hold the matplotlib canvas and the tools for interacting with the plots """ def __init__(self, data, xlabel, ylabel): QtWidgets.QWidget.__init__(self) self.setLayout(QtWidgets.QVBoxLayout()) self.canvas = PlotCanvas(data, xlabel, ylabel) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.addSeparator() self.ACshowselector = QtWidgets.QAction('Activate/Clear RangeSelector') self.ACshowselector.setIconText('RANGE SELECTOR') self.ACshowselector.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold)) self.ACshowselector.triggered.connect(self.toggle_showselector) self.toolbar.addAction(self.ACshowselector) self.toolbar.addSeparator() self.layout().addWidget(self.toolbar) self.layout().addWidget(self.canvas) def toggle_showselector(self): self.canvas.toggle_rangeselector()
class _MPLPlotWidget(PlotWidget): """ Base class for matplotlib-based plot widgets. Per default, add a canvas and the matplotlib NavBar. """ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): super().__init__(parent=parent) setMplDefaults(self) scaling = np.rint(self.logicalDpiX() / 96.0) defaultIconSize = 16 * scaling self.plot = MPLPlot() self.mplBar = NavBar(self.plot, self) self.addMplBarOptions() self.mplBar.setIconSize(QtCore.QSize(defaultIconSize, defaultIconSize)) layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.plot) layout.addWidget(self.mplBar) self.setLayout(layout) def setMeta(self, data: DataDictBase) -> None: if data.has_meta('title'): self.plot.setFigureTitle(data.meta_val('title')) if data.has_meta('info'): self.plot.setFigureInfo(data.meta_val('info')) meta_info = {} for meta_key in ('qcodes_guid', 'qcodes_sample_name', 'qcodes_experiment_name', 'qcodes_dataset_name', 'qcodes_runId', 'qcodes_db', 'qcodes_completedTS', 'qcodes_runTS'): if data.has_meta(meta_key): key_without_prefix = (meta_key.replace("qcodes_", "") if meta_key.startswith("qcodes_") else meta_key) meta_info[key_without_prefix] = data.meta_val(meta_key) self.plot.setMetaInfo(meta_info=meta_info) def addMplBarOptions(self) -> None: tlCheck = QtWidgets.QCheckBox('Tight layout') tlCheck.toggled.connect(self.plot.setTightLayout) infoCheck = QtWidgets.QCheckBox('Info') infoCheck.toggled.connect(self.plot.setShowInfo) self.mplBar.addSeparator() self.mplBar.addWidget(tlCheck) self.mplBar.addSeparator() self.mplBar.addWidget(infoCheck) self.mplBar.addSeparator() self.mplBar.addAction('Copy plot', self.plot.toClipboard) self.mplBar.addAction('Copy metadata', self.plot.metaToClipboard)
class MPLPlotWidget(QtGui.QWidget): """ Base class for matplotlib-based plot widgets. Per default, add a canvas and the matplotlib NavBar. """ def __init__(self, parent=None): super().__init__(parent=parent) setMplDefaults() self.plot = MPLPlot() self.mplBar = NavBar(self.plot, self) self.addMplBarOptions() self.toolLayout = QtGui.QHBoxLayout() self.layout = QtGui.QVBoxLayout(self) self.layout.addLayout(self.toolLayout) self.layout.addWidget(self.plot) self.layout.addWidget(self.mplBar) def setData(self, data: DataDictBase): raise NotImplementedError def setMeta(self, data: DataDictBase): if data.has_meta('title'): self.plot.setFigureTitle(data.meta_val('title')) if data.has_meta('info'): self.plot.setFigureInfo(data.meta_val('info')) def addMplBarOptions(self): tlCheck = QtGui.QCheckBox('Tight layout') tlCheck.toggled.connect(self.plot.setTightLayout) infoCheck = QtGui.QCheckBox('Info') infoCheck.toggled.connect(self.plot.setShowInfo) self.mplBar.addSeparator() self.mplBar.addWidget(tlCheck) self.mplBar.addSeparator() self.mplBar.addWidget(infoCheck) self.mplBar.addSeparator() self.mplBar.addAction('Copy', self.plot.toClipboard)
class _MPLPlotWidget(PlotWidget): """ Base class for matplotlib-based plot widgets. Per default, add a canvas and the matplotlib NavBar. """ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): super().__init__(parent=parent) setMplDefaults(self) scaling = np.rint(self.logicalDpiX() / 96.0) defaultIconSize = 16 * scaling self.plot = MPLPlot() self.mplBar = NavBar(self.plot, self) self.addMplBarOptions() self.mplBar.setIconSize(QtCore.QSize(defaultIconSize, defaultIconSize)) layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.plot) layout.addWidget(self.mplBar) self.setLayout(layout) def setMeta(self, data: DataDictBase) -> None: if data.has_meta('title'): self.plot.setFigureTitle(data.meta_val('title')) if data.has_meta('info'): self.plot.setFigureInfo(data.meta_val('info')) def addMplBarOptions(self) -> None: tlCheck = QtWidgets.QCheckBox('Tight layout') tlCheck.toggled.connect(self.plot.setTightLayout) infoCheck = QtWidgets.QCheckBox('Info') infoCheck.toggled.connect(self.plot.setShowInfo) self.mplBar.addSeparator() self.mplBar.addWidget(tlCheck) self.mplBar.addSeparator() self.mplBar.addWidget(infoCheck) self.mplBar.addSeparator() self.mplBar.addAction('Copy', self.plot.toClipboard)
def _create_window(self): ''' Create the QT window and all widgets ''' self.setWindowTitle("Our QT Plot") if self.settings.value("geometry") == None: # First launch on this computer self.left = 200 self.top = 400 self.width = 1240 self.height = 960 self.setGeometry(self.left, self.top, self.width, self.height) else: # restored saved windows position self.restoreGeometry(self.settings.value("geometry")) nav_toolbar_home = NavigationToolbar.home def new_home(self, *args, **kwargs): print ('new home') nav_toolbar_home(self, *args, **kwargs) NavigationToolbar.home = new_home self.tab_widget = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() #self.tabs.resize(300,200) # Add tabs self.tab_widget.addTab(self.tab1,"Timeseries Plot") self.tab_widget.addTab(self.tab2,"XY Plot") # Create first timeseries tab self.tab1.layout = QHBoxLayout() timeseries_canvas = FigureCanvas(Figure()) self.main_figure = timeseries_canvas.figure self.tab1.layout.addWidget(timeseries_canvas) timeseries_nav_tb = NavigationToolbar(timeseries_canvas, self) timeseries_nav_tb.setOrientation(Qt.Vertical) # Relove last plot button timeseries_removePlotButton = QToolButton() timeseries_removePlotButton.setIcon(QApplication.style().standardIcon(QStyle.SP_DialogOkButton)) timeseries_removePlotButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) timeseries_removePlotButton.setToolTip('Remove Last Plot') timeseries_nav_tb.addSeparator() timeseries_nav_tb.addWidget(timeseries_removePlotButton) timeseries_removePlotButton.clicked.connect(self._remove_last_subplot) # Clear plot button timeseries_clear_button = QToolButton() timeseries_clear_button.setIcon(QApplication.style().standardIcon(QStyle.SP_BrowserStop)) timeseries_clear_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) timeseries_clear_button.setToolTip('Clear All Plots') timeseries_nav_tb.addWidget(timeseries_clear_button) timeseries_clear_button.clicked.connect(self._remove_all_timeseries_subplots) timeseries_nav_tb.setFixedWidth(36) self.tab1.layout.addWidget(timeseries_nav_tb) self.tab1.setLayout(self.tab1.layout) # Create XY figure tab self.tab2.layout = QHBoxLayout() xy_canvas = FigureCanvas(Figure()) self.xy_figure = xy_canvas.figure self.xy_axes = self.xy_figure.subplots(1, 1) self.tab2.layout.addWidget(xy_canvas) xy_nav_tb = NavigationToolbar(xy_canvas, self) xy_nav_tb.setOrientation(Qt.Vertical) xy_clear_button = QToolButton() xy_clear_button.setIcon(QApplication.style().standardIcon(QStyle.SP_BrowserStop)) xy_clear_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) xy_clear_button.setToolTip('Clear All Plots') xy_nav_tb.addSeparator() xy_nav_tb.addWidget(xy_clear_button) xy_clear_button.clicked.connect(self._clear_xy_axes) xy_nav_tb.setFixedWidth(36) #xy_nav_tb.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.tab2.layout.addWidget(xy_nav_tb) self.tab2.setLayout(self.tab2.layout) self.setCentralWidget(self.tab_widget) #self.addToolBar(NavigationToolbar(timeseries_canvas, self)) # Add time series plot buttons self.tsp_widget = QDockWidget("Select Timeseries Plot", self) self.tsp_button_Group = QGroupBox() self.tsp_button_Group.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) tsp_button_GroupLayout = QVBoxLayout() tsplotButtonList = [] for time_series_buttons_dict_key, time_series_buttons_dict_value in sorted(self.time_series_buttons_dict.iteritems()): buttonwidget = QPushButton(time_series_buttons_dict_key) #buttonwidget.clicked.connect(lambda: self._add_new_plot(time_series_buttons_dict_value)) buttonwidget.clicked.connect(partial(self._add_new_plot, time_series_buttons_dict_value)) tsp_button_GroupLayout.addWidget(buttonwidget) tsplotButtonList.append(buttonwidget) tsp_button_GroupLayout.addStretch(1) self.tsp_button_Group.setLayout(tsp_button_GroupLayout) self.tsp_widget.setWidget(self.tsp_button_Group) self.tsp_widget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.tsp_widget.setFloating(False) self.addDockWidget(Qt.LeftDockWidgetArea, self.tsp_widget) # Add xy plot buttons self.xyp_widget = QDockWidget("Select XY Plot", self) self.xyp_button_Group = QGroupBox() self.xyp_button_Group.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) xyp_button_GroupLayout = QVBoxLayout() xyplotButtonList = [] for xy_buttons_dict_key, xy_buttons_dict_value in self.xy_buttons_dict.items(): buttonwidget = QPushButton(xy_buttons_dict_key) #buttonwidget.clicked.connect(lambda: self._add_new_plot(xy_buttons_dict_value)) buttonwidget.clicked.connect(partial(self._add_new_xy_plot, xy_buttons_dict_value)) xyp_button_GroupLayout.addWidget(buttonwidget) xyplotButtonList.append(buttonwidget) #xyp_button_GroupLayout.addWidget(xy_clear_button) xyp_button_GroupLayout.addStretch(1) self.xyp_button_Group.setLayout(xyp_button_GroupLayout) self.xyp_widget.setWidget(self.xyp_button_Group) self.xyp_widget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.xyp_widget.setFloating(False) self.addDockWidget(Qt.LeftDockWidgetArea, self.xyp_widget) # Add load data button self.openDataWidget = QDockWidget("Open Data File", self) self.openDataGroup = QGroupBox() self.openDataGroup.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) openDataGroupLayout = QHBoxLayout() openDataButton = QPushButton("Load Datafile") openDataButton.setDefault(False) #openDataButton.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) openDataButton.clicked.connect(self._load_datafile) openDataGroupLayout.addWidget(openDataButton) self.openDataGroup.setLayout(openDataGroupLayout) self.openDataWidget.setWidget(self.openDataGroup) self.openDataWidget.setFloating(False) self.addDockWidget(Qt.TopDockWidgetArea, self.openDataWidget) self.memory_status_qlabel = QLabel("Memory: {:.1f}%".format(0)) self.statusBar().addPermanentWidget(self.memory_status_qlabel)
class FigureTab: cursors = [15000, 45000] colors = ['orange', 'violet'] def __init__(self, layout, vna): # create figure self.figure = Figure() if sys.platform != 'win32': self.figure.set_facecolor('none') self.canvas = FigureCanvas(self.figure) layout.addWidget(self.canvas) # create navigation toolbar self.toolbar = NavigationToolbar(self.canvas, None, False) self.toolbar.layout().setSpacing(6) # remove subplots action actions = self.toolbar.actions() if int(matplotlib.__version__[0]) < 2: self.toolbar.removeAction(actions[7]) else: self.toolbar.removeAction(actions[6]) self.toolbar.addSeparator() self.cursorLabels = {} self.cursorValues = {} self.cursorMarkers = {} self.cursorPressed = {} for i in range(len(self.cursors)): self.cursorMarkers[i] = None self.cursorPressed[i] = False self.cursorLabels[i] = QLabel('Cursor %d, kHz' % (i + 1)) self.cursorLabels[i].setStyleSheet('color: %s' % self.colors[i]) self.cursorValues[i] = QSpinBox() self.cursorValues[i].setMinimumSize(90, 0) self.cursorValues[i].setSingleStep(10) self.cursorValues[i].setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self.toolbar.addWidget(self.cursorLabels[i]) self.toolbar.addWidget(self.cursorValues[i]) self.cursorValues[i].valueChanged.connect( partial(self.set_cursor, i)) self.canvas.mpl_connect('button_press_event', partial(self.press_marker, i)) self.canvas.mpl_connect('motion_notify_event', partial(self.move_marker, i)) self.canvas.mpl_connect('button_release_event', partial(self.release_marker, i)) self.toolbar.addSeparator() self.plotButton = QPushButton('Rescale') self.toolbar.addWidget(self.plotButton) layout.addWidget(self.toolbar) self.plotButton.clicked.connect(self.plot) self.mode = None self.vna = vna def add_cursors(self, axes): if self.mode == 'gain_short' or self.mode == 'gain_open': columns = ['Freq., kHz', 'G, dB', r'$\angle$ G, deg'] else: columns = [ 'Freq., kHz', 'Re(Z), \u03A9', 'Im(Z), \u03A9', '|Z|, \u03A9', r'$\angle$ Z, deg', 'SWR', r'|$\Gamma$|', r'$\angle$ $\Gamma$, deg', 'RL, dB' ] y = len(self.cursors) * 0.04 + 0.01 for i in range(len(columns)): self.figure.text(0.19 + 0.1 * i, y, columns[i], horizontalalignment='right') self.cursorRows = {} for i in range(len(self.cursors)): y = len(self.cursors) * 0.04 - 0.03 - 0.04 * i self.figure.text(0.01, y, 'Cursor %d' % (i + 1), color=self.colors[i]) self.cursorRows[i] = {} for j in range(len(columns)): self.cursorRows[i][j] = self.figure.text( 0.19 + 0.1 * j, y, '', horizontalalignment='right') if self.mode == 'smith': self.cursorMarkers[i], = axes.plot(0.0, 0.0, marker='o', color=self.colors[i]) else: self.cursorMarkers[i] = axes.axvline(0.0, color=self.colors[i], linewidth=2) self.set_cursor(i, self.cursorValues[i].value()) def set_cursor(self, index, value): FigureTab.cursors[index] = value marker = self.cursorMarkers[index] if marker is None: return row = self.cursorRows[index] freq = value gamma = self.vna.gamma(freq) if self.mode == 'smith': marker.set_xdata(gamma.real) marker.set_ydata(gamma.imag) else: marker.set_xdata(freq) row[0].set_text('%d' % freq) if self.mode == 'gain_short': gain = self.vna.gain_short(freq) magnitude = 20.0 * np.log10(np.absolute(gain)) angle = np.angle(gain, deg=True) row[1].set_text(unicode_minus('%.1f' % magnitude)) row[2].set_text(unicode_minus('%.1f' % angle)) elif self.mode == 'gain_open': gain = self.vna.gain_open(freq) magnitude = 20.0 * np.log10(np.absolute(gain)) angle = np.angle(gain, deg=True) row[1].set_text(unicode_minus('%.1f' % magnitude)) row[2].set_text(unicode_minus('%.1f' % angle)) else: swr = self.vna.swr(freq) z = self.vna.impedance(freq) rl = 20.0 * np.log10(np.absolute(gamma)) if rl > -0.01: rl = 0.0 row[1].set_text(metric_prefix(z.real)) row[2].set_text(metric_prefix(z.imag)) row[3].set_text(metric_prefix(np.absolute(z))) angle = np.angle(z, deg=True) if np.abs(angle) < 0.1: angle = 0.0 row[4].set_text(unicode_minus('%.1f' % angle)) row[5].set_text(unicode_minus('%.2f' % swr)) row[6].set_text(unicode_minus('%.2f' % np.absolute(gamma))) angle = np.angle(gamma, deg=True) if np.abs(angle) < 0.1: angle = 0.0 row[7].set_text(unicode_minus('%.1f' % angle)) row[8].set_text(unicode_minus('%.2f' % rl)) self.canvas.draw() def press_marker(self, index, event): if not event.inaxes: return if self.mode == 'smith': return marker = self.cursorMarkers[index] if marker is None: return contains, misc = marker.contains(event) if not contains: return self.cursorPressed[index] = True def move_marker(self, index, event): if not event.inaxes: return if self.mode == 'smith': return if not self.cursorPressed[index]: return self.cursorValues[index].setValue(event.xdata) def release_marker(self, index, event): self.cursorPressed[index] = False def xlim(self, freq): start = freq[0] stop = freq[-1] min = np.minimum(start, stop) max = np.maximum(start, stop) margin = (max - min) / 50 return (min - margin, max + margin) def plot(self): getattr(self, 'plot_%s' % self.mode)() def update(self, mode): start = self.vna.dut.freq[0] stop = self.vna.dut.freq[-1] min = np.minimum(start, stop) max = np.maximum(start, stop) for i in range(len(self.cursors)): value = self.cursors[i] self.cursorValues[i].setRange(min, max) self.cursorValues[i].setValue(value) self.set_cursor(i, value) getattr(self, 'update_%s' % mode)() def plot_curves(self, freq, data1, label1, limit1, data2, label2, limit2): matplotlib.rcdefaults() matplotlib.rcParams['axes.formatter.use_mathtext'] = True self.figure.clf() bottom = len(self.cursors) * 0.04 + 0.13 self.figure.subplots_adjust(left=0.16, bottom=bottom, right=0.84, top=0.96) axes1 = self.figure.add_subplot(111) axes1.cla() axes1.xaxis.grid() axes1.set_xlabel('kHz') axes1.set_ylabel(label1) xlim = self.xlim(freq) axes1.set_xlim(xlim) if limit1 is not None: axes1.set_ylim(limit1) self.curve1, = axes1.plot(freq, data1, color='blue', label=label1) self.add_cursors(axes1) if data2 is None: self.canvas.draw() return axes1.tick_params('y', color='blue', labelcolor='blue') axes1.yaxis.label.set_color('blue') axes2 = axes1.twinx() axes2.spines['left'].set_color('blue') axes2.spines['right'].set_color('red') axes2.set_ylabel(label2) axes2.set_xlim(xlim) if limit2 is not None: axes2.set_ylim(limit2) axes2.tick_params('y', color='red', labelcolor='red') axes2.yaxis.label.set_color('red') self.curve2, = axes2.plot(freq, data2, color='red', label=label2) self.canvas.draw() def plot_gain(self, gain): freq = self.vna.dut.freq data1 = 20.0 * np.log10(np.absolute(gain)) data2 = np.angle(gain, deg=True) self.plot_curves(freq, data1, 'G, dB', (-110, 110.0), data2, r'$\angle$ G, deg', (-198, 198)) def plot_gain_short(self): self.mode = 'gain_short' self.plot_gain(self.vna.gain_short(self.vna.dut.freq)) def plot_gain_open(self): self.mode = 'gain_open' self.plot_gain(self.vna.gain_open(self.vna.dut.freq)) def update_gain(self, gain, mode): if self.mode == mode: self.curve1.set_xdata(self.vna.dut.freq) self.curve1.set_ydata(20.0 * np.log10(np.absolute(gain))) self.curve2.set_xdata(self.vna.dut.freq) self.curve2.set_ydata(np.angle(gain, deg=True)) self.canvas.draw() else: self.mode = mode self.plot_gain(gain) def update_gain_short(self): self.update_gain(self.vna.gain_short(self.vna.dut.freq), 'gain_short') def update_gain_open(self): self.update_gain(self.vna.gain_open(self.vna.dut.freq), 'gain_open') def plot_magphase(self, freq, data, label, mode): self.mode = mode data1 = np.absolute(data) data2 = np.angle(data, deg=True) max = np.fmax(0.01, data1.max()) label1 = r'|%s|' % label label2 = r'$\angle$ %s, deg' % label self.plot_curves(freq, data1, label1, (-0.05 * max, 1.05 * max), data2, label2, (-198, 198)) def update_magphase(self, freq, data, label, mode): if self.mode == mode: self.curve1.set_xdata(freq) self.curve1.set_ydata(np.absolute(data)) self.curve2.set_xdata(freq) self.curve2.set_ydata(np.angle(data, deg=True)) self.canvas.draw() else: self.plot_magphase(freq, data, label, mode) def plot_open(self): self.plot_magphase(self.vna.open.freq, self.vna.open.data, 'open', 'open') def update_open(self): self.update_magphase(self.vna.open.freq, self.vna.open.data, 'open', 'open') def plot_short(self): self.plot_magphase(self.vna.short.freq, self.vna.short.data, 'short', 'short') def update_short(self): self.update_magphase(self.vna.short.freq, self.vna.short.data, 'short', 'short') def plot_load(self): self.plot_magphase(self.vna.load.freq, self.vna.load.data, 'load', 'load') def update_load(self): self.update_magphase(self.vna.load.freq, self.vna.load.data, 'load', 'load') def plot_dut(self): self.plot_magphase(self.vna.dut.freq, self.vna.dut.data, 'dut', 'dut') def update_dut(self): self.update_magphase(self.vna.dut.freq, self.vna.dut.data, 'dut', 'dut') def plot_smith_grid(self, axes, color): load = 50.0 ticks = np.array([0.0, 0.2, 0.5, 1.0, 2.0, 5.0]) for tick in ticks * load: axis = np.logspace(-4, np.log10(1.0e3), 200) * load z = tick + 1.0j * axis gamma = (z - load) / (z + load) axes.plot(gamma.real, gamma.imag, color=color, linewidth=0.4, alpha=0.3) axes.plot(gamma.real, -gamma.imag, color=color, linewidth=0.4, alpha=0.3) z = axis + 1.0j * tick gamma = (z - load) / (z + load) axes.plot(gamma.real, gamma.imag, color=color, linewidth=0.4, alpha=0.3) axes.plot(gamma.real, -gamma.imag, color=color, linewidth=0.4, alpha=0.3) if tick == 0.0: axes.text(1.0, 0.0, u'\u221E', color=color, ha='left', va='center', clip_on=True, fontsize='x-large') axes.text(-1.0, 0.0, u'0\u03A9', color=color, ha='left', va='bottom', clip_on=True) continue lab = u'%d\u03A9' % tick x = (tick - load) / (tick + load) axes.text(x, 0.0, lab, color=color, ha='left', va='bottom', clip_on=True) lab = u'j%d\u03A9' % tick z = 1.0j * tick gamma = (z - load) / (z + load) * 1.05 x = gamma.real y = gamma.imag angle = np.angle(gamma) * 180.0 / np.pi - 90.0 axes.text(x, y, lab, color=color, ha='center', va='center', clip_on=True, rotation=angle) lab = u'\u2212j%d\u03A9' % tick axes.text(x, -y, lab, color=color, ha='center', va='center', clip_on=True, rotation=-angle) def plot_smith(self): self.mode = 'smith' matplotlib.rcdefaults() self.figure.clf() bottom = len(self.cursors) * 0.04 + 0.05 self.figure.subplots_adjust(left=0.0, bottom=bottom, right=1.0, top=1.0) axes1 = self.figure.add_subplot(111) self.plot_smith_grid(axes1, 'blue') gamma = self.vna.gamma(self.vna.dut.freq) self.curve1, = axes1.plot(gamma.real, gamma.imag, color='red') axes1.axis('equal') axes1.set_xlim(-1.12, 1.12) axes1.set_ylim(-1.12, 1.12) axes1.xaxis.set_visible(False) axes1.yaxis.set_visible(False) for loc, spine in axes1.spines.items(): spine.set_visible(False) self.add_cursors(axes1) self.canvas.draw() def update_smith(self): if self.mode == 'smith': gamma = self.vna.gamma(self.vna.dut.freq) self.curve1.set_xdata(gamma.real) self.curve1.set_ydata(gamma.imag) self.canvas.draw() else: self.plot_smith() def plot_imp(self): self.mode = 'imp' freq = self.vna.dut.freq z = self.vna.impedance(freq) data1 = np.fmin(9.99e4, np.absolute(z)) data2 = np.angle(z, deg=True) max = np.fmax(0.01, data1.max()) self.plot_curves(freq, data1, '|Z|, \u03A9', (-0.05 * max, 1.05 * max), data2, r'$\angle$ Z, deg', (-198, 198)) def update_imp(self): if self.mode == 'imp': freq = self.vna.dut.freq z = self.vna.impedance(freq) data1 = np.fmin(9.99e4, np.absolute(z)) data2 = np.angle(z, deg=True) self.curve1.set_xdata(freq) self.curve1.set_ydata(data1) self.curve2.set_xdata(freq) self.curve2.set_ydata(data2) self.canvas.draw() else: self.plot_imp() def plot_swr(self): self.mode = 'swr' freq = self.vna.dut.freq data1 = self.vna.swr(freq) self.plot_curves(freq, data1, 'SWR', (0.9, 3.1), None, None, None) def update_swr(self): if self.mode == 'swr': self.curve1.set_xdata(self.vna.dut.freq) self.curve1.set_ydata(self.vna.swr(self.vna.dut.freq)) self.canvas.draw() else: self.plot_swr() def plot_gamma(self): self.plot_magphase(self.vna.dut.freq, self.vna.gamma(self.vna.dut.freq), r'$\Gamma$', 'gamma') def update_gamma(self): self.update_magphase(self.vna.dut.freq, self.vna.gamma(self.vna.dut.freq), r'$\Gamma$', 'gamma') def plot_rl(self): self.mode = 'rl' freq = self.vna.dut.freq gamma = self.vna.gamma(freq) data1 = 20.0 * np.log10(np.absolute(gamma)) self.plot_curves(freq, data1, 'RL, dB', (-105, 5.0), None, None, None) def update_rl(self): if self.mode == 'rl': freq = self.vna.dut.freq gamma = self.vna.gamma(freq) data1 = 20.0 * np.log10(np.absolute(gamma)) self.curve1.set_xdata(freq) self.curve1.set_ydata(data1) self.canvas.draw() else: self.plot_rl()
class dae2DPlot(QtWidgets.QDialog): plotDefaults = [ daePlot2dDefaults('black', 0.5, 'solid', 'o', 6, 'black', 'black'), daePlot2dDefaults('blue', 0.5, 'solid', 's', 6, 'blue', 'black'), daePlot2dDefaults('red', 0.5, 'solid', '^', 6, 'red', 'black'), daePlot2dDefaults('green', 0.5, 'solid', 'p', 6, 'green', 'black'), daePlot2dDefaults('c', 0.5, 'solid', 'h', 6, 'c', 'black'), daePlot2dDefaults('m', 0.5, 'solid', '*', 6, 'm', 'black'), daePlot2dDefaults('k', 0.5, 'solid', 'd', 6, 'k', 'black'), daePlot2dDefaults('y', 0.5, 'solid', 'x', 6, 'y', 'black'), daePlot2dDefaults('black', 0.5, 'dashed', 'o', 6, 'black', 'black'), daePlot2dDefaults('blue', 0.5, 'dashed', 's', 6, 'blue', 'black'), daePlot2dDefaults('red', 0.5, 'dashed', '^', 6, 'red', 'black'), daePlot2dDefaults('green', 0.5, 'dashed', 'p', 6, 'green', 'black'), daePlot2dDefaults('c', 0.5, 'dashed', 'h', 6, 'c', 'black'), daePlot2dDefaults('m', 0.5, 'dashed', '*', 6, 'm', 'black'), daePlot2dDefaults('k', 0.5, 'dashed', 'd', 6, 'k', 'black'), daePlot2dDefaults('y', 0.5, 'dashed', 'x', 6, 'y', 'black'), daePlot2dDefaults('black', 0.5, 'dotted', 'o', 6, 'black', 'black'), daePlot2dDefaults('blue', 0.5, 'dotted', 's', 6, 'blue', 'black'), daePlot2dDefaults('red', 0.5, 'dotted', '^', 6, 'red', 'black'), daePlot2dDefaults('green', 0.5, 'dotted', 'p', 6, 'green', 'black'), daePlot2dDefaults('c', 0.5, 'dotted', 'h', 6, 'c', 'black'), daePlot2dDefaults('m', 0.5, 'dotted', '*', 6, 'm', 'black'), daePlot2dDefaults('k', 0.5, 'dotted', 'd', 6, 'k', 'black'), daePlot2dDefaults('y', 0.5, 'dotted', 'x', 6, 'y', 'black') ] def __init__(self, parent, updateInterval=0, animated=False): QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.Window) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.plotter = parent self.legendOn = True self.gridOn = True self.curves = [] self.funcAnimation = None self._isAnimating = False self._timer = None self._cv_dlg = None self.xmin_policy = 0 self.xmax_policy = 0 self.ymin_policy = 1 self.ymax_policy = 1 if animated == True: self.updateInterval = updateInterval self.plotType = daeChooseVariable.plot2DAnimated elif updateInterval == 0: self.updateInterval = 0 self.plotType = daeChooseVariable.plot2D else: self.updateInterval = int(updateInterval) self.plotType = daeChooseVariable.plot2DAutoUpdated self.setWindowTitle("2D plot") self.setWindowIcon(QtGui.QIcon(join(images_dir, 'line-chart.png'))) exit = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'close.png')), 'Exit', self) exit.setShortcut('Ctrl+Q') exit.setStatusTip('Exit application') exit.triggered.connect(self.close) export = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'template.png')), 'Export template', self) export.setShortcut('Ctrl+X') export.setStatusTip('Export template') export.triggered.connect(self.slotExportTemplate) properties = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'preferences.png')), 'Options', self) properties.setShortcut('Ctrl+P') properties.setStatusTip('Options') properties.triggered.connect(self.slotProperties) grid = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'grid.png')), 'Grid on/off', self) grid.setShortcut('Ctrl+G') grid.setStatusTip('Grid on/off') grid.triggered.connect(self.slotToggleGrid) legend = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'legend.png')), 'Legend on/off', self) legend.setShortcut('Ctrl+L') legend.setStatusTip('Legend on/off') legend.triggered.connect(self.slotToggleLegend) viewdata = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'data.png')), 'View tabular data', self) viewdata.setShortcut('Ctrl+T') viewdata.setStatusTip('View tabular data') viewdata.triggered.connect(self.slotViewTabularData) export_csv = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'csv.png')), 'Export CSV', self) export_csv.setShortcut('Ctrl+S') export_csv.setStatusTip('Export CSV') export_csv.triggered.connect(self.slotExportCSV) fromUserData = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'add-user-data.png')), 'Add line from the user-provided data...', self) fromUserData.setShortcut('Ctrl+D') fromUserData.setStatusTip('Add line from the user-provided data') fromUserData.triggered.connect(self.slotFromUserData) remove_line = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'remove.png')), 'Remove line', self) remove_line.setShortcut('Ctrl+R') remove_line.setStatusTip('Remove line') remove_line.triggered.connect(self.slotRemoveLine) new_line = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'add.png')), 'Add line', self) new_line.setShortcut('Ctrl+A') new_line.setStatusTip('Add line') if animated == True: new_line.triggered.connect(self.newAnimatedCurve) else: new_line.triggered.connect(self.newCurve) play_animation = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'media-playback-start.png')), 'Start animation', self) play_animation.setShortcut('Ctrl+S') play_animation.setStatusTip('Start animation') play_animation.triggered.connect(self.playAnimation) self.play_animation = play_animation # save it stop_animation = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'media-playback-stop.png')), 'Stop animation', self) stop_animation.setShortcut('Ctrl+E') stop_animation.setStatusTip('Stop animation') stop_animation.triggered.connect(self.stopAnimation) self.stop_animation = stop_animation # save it export_video = QtWidgets.QAction( QtGui.QIcon(join(images_dir, 'save-video.png')), 'Export video/sequence of images', self) export_video.setShortcut('Ctrl+V') export_video.setStatusTip('Export video/sequence of images') export_video.triggered.connect(self.exportVideo) self.actions_to_disable = [ export, viewdata, export_csv, grid, legend, properties ] self.actions_to_disable_permanently = [ fromUserData ] #[new_line, fromUserData, remove_line] self.toolbar_widget = QtWidgets.QWidget(self) layoutToolbar = QtWidgets.QVBoxLayout(self.toolbar_widget) layoutToolbar.setContentsMargins(0, 0, 0, 0) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self.toolbar_widget.setSizePolicy(sizePolicy) layoutPlot = QtWidgets.QVBoxLayout(self) layoutPlot.setContentsMargins(2, 2, 2, 2) self.figure = Figure((8, 6.5), dpi=100, facecolor='white') #"#E5E5E5") self.canvas = FigureCanvas(self.figure) self.canvas.setParent(self) self.canvas.axes = self.figure.add_subplot(111) self.mpl_toolbar = NavigationToolbar(self.canvas, self.toolbar_widget, False) #self.mpl_toolbar.addSeparator() self.mpl_toolbar.addAction(export) self.mpl_toolbar.addAction(export_csv) self.mpl_toolbar.addAction(viewdata) self.mpl_toolbar.addSeparator() self.mpl_toolbar.addAction(grid) self.mpl_toolbar.addAction(legend) self.mpl_toolbar.addSeparator() self.mpl_toolbar.addAction(new_line) self.mpl_toolbar.addAction(fromUserData) self.mpl_toolbar.addAction(remove_line) self.mpl_toolbar.addSeparator() #self.mpl_toolbar.addAction(properties) #self.mpl_toolbar.addSeparator() #self.mpl_toolbar.addAction(exit) if self.plotType == daeChooseVariable.plot2DAnimated: self.mpl_toolbar.addSeparator() self.mpl_toolbar.addAction(play_animation) self.mpl_toolbar.addAction(stop_animation) self.mpl_toolbar.addAction(export_video) self.fp9 = matplotlib.font_manager.FontProperties(family='sans-serif', style='normal', variant='normal', weight='normal', size=9) self.fp10 = matplotlib.font_manager.FontProperties(family='sans-serif', style='normal', variant='normal', weight='normal', size=10) self.fp12 = matplotlib.font_manager.FontProperties(family='sans-serif', style='normal', variant='normal', weight='normal', size=12) self.textTime = self.figure.text(0.01, 0.01, '', fontproperties=self.fp10) self.xtransform = 1.0 self.ytransform = 1.0 for xlabel in self.canvas.axes.get_xticklabels(): xlabel.set_fontproperties(self.fp10) for ylabel in self.canvas.axes.get_yticklabels(): ylabel.set_fontproperties(self.fp10) layoutToolbar.addWidget(self.mpl_toolbar) layoutPlot.addWidget(self.canvas) layoutPlot.addWidget(self.toolbar_widget) if animated == False and self.updateInterval > 0: self._timer = QtCore.QTimer() self._timer.timeout.connect(self.updateCurves) self._timer.start(self.updateInterval) def closeEvent(self, event): #print("dae2DPlot.closeEvent") if self.funcAnimation: self.funcAnimation.event_source.stop() if self._timer: self._timer.stop() return QtWidgets.QDialog.closeEvent(self, event) def updateCurves(self): try: # these three not used for (line, variable, domainIndexes, domainPoints, fun, times, xPoints, yPoints_2D) in self.curves: results = fun(variable, domainIndexes, domainPoints) if self.xtransform != 1: xPoints = numpy.array(results[5]) * self.xtransform else: xPoints = results[5] if self.ytransform != 1: yPoints = numpy.array(results[6]) * self.ytransform else: yPoints = results[6] currentTime = results[7] line.set_xdata(xPoints) line.set_ydata(yPoints) if self.textTime: t = 'Time = {0} s'.format(currentTime) self.textTime.set_text(t) #self.reformatPlot() except Exception as e: print((str(e))) #@QtCore.pyqtSlot() def slotExportTemplate(self): try: curves = [] template = { 'curves': curves, 'plotType': self.plotType, 'updateInterval': self.updateInterval, 'xlabel': self.canvas.axes.get_xlabel(), 'xmin': self.canvas.axes.get_xlim()[0], 'xmax': self.canvas.axes.get_xlim()[1], 'xscale': self.canvas.axes.get_xscale(), 'xtransform': 1.0, 'ylabel': self.canvas.axes.get_ylabel(), 'ymin': self.canvas.axes.get_ylim()[0], 'ymax': self.canvas.axes.get_ylim()[1], 'yscale': self.canvas.axes.get_yscale(), 'ytransform': 1.0, 'legendOn': self.legendOn, 'gridOn': self.gridOn, 'plotTitle': self.canvas.axes.get_title(), 'windowTitle': str(self.windowTitle()), 'xmin_policy': int(self.xmin_policy), 'xmax_policy': int(self.xmax_policy), 'ymin_policy': int(self.ymin_policy), 'ymax_policy': int(self.ymax_policy) } for (line, variable, domainIndexes, domainPoints, fun, times, xPoints, yPoints_2D) in self.curves: # variableName, indexes, points, linelabel, style = {linecolor, linewidth, linestyle, marker, markersize, markerfacecolor, markeredgecolor} style = daePlot2dDefaults(line.get_color(), line.get_linewidth(), line.get_linestyle(), line.get_marker(), line.get_markersize(), line.get_markerfacecolor(), line.get_markeredgecolor()) curves.append((variable.Name, domainIndexes, domainPoints, line.get_label(), style.to_dict())) s = json.dumps(template, indent=2, sort_keys=True) filename, ok = QtWidgets.QFileDialog.getSaveFileName( self, "Save 2D plot template", "template.pt", "Templates (*.pt)") if not ok: return f = open(filename, 'w') f.write(s) f.close() except Exception as e: print((str(e))) #@QtCore.pyqtSlot() def slotProperties(self): figure_edit(self.canvas, self) #@QtCore.pyqtSlot() def slotToggleLegend(self): self.legendOn = not self.legendOn self.updateLegend() #@QtCore.pyqtSlot() def slotToggleGrid(self): self.gridOn = not self.gridOn self.updateGrid() def updateLegend(self): if self.legendOn: self.canvas.axes.legend(loc=0, prop=self.fp9, numpoints=1, fancybox=True) else: self.canvas.axes.legend_ = None self.canvas.draw() #self.reformatPlot() def updateGrid(self): self.canvas.axes.grid(self.gridOn) self.canvas.draw() #self.reformatPlot() #@QtCore.pyqtSlot() def slotExportCSV(self): strInitialFilename = QtCore.QDir.current().path() strInitialFilename += "/untitled.csv" strExt = "Comma separated files (*.csv)" strCaption = "Save file" fileName, ok = QtWidgets.QFileDialog.getSaveFileName( self, strCaption, strInitialFilename, strExt) if not ok: return datafile = open(str(fileName), 'w') lines = self.canvas.axes.get_lines() for line in lines: xlabel = self.canvas.axes.get_xlabel() ylabel = line.get_label() x = line.get_xdata() y = line.get_ydata() datafile.write('\"' + xlabel + '\",\"' + ylabel + '\"\n') for i in range(0, len(x)): datafile.write('%.14e,%.14e\n' % (x[i], y[i])) datafile.write('\n') #@QtCore.pyqtSlot() def slotViewTabularData(self): lines = self.canvas.axes.get_lines() tableDialog = daeTableDialog(self) tableDialog.setWindowTitle('Raw data') table = tableDialog.ui.tableWidget nr = 0 ncol = len(lines) for line in lines: n = len(line.get_xdata()) if nr < n: nr = n xlabel = self.canvas.axes.get_xlabel() table.setRowCount(nr) table.setColumnCount(ncol) horHeader = [] verHeader = [] for i, line in enumerate(lines): xlabel = self.canvas.axes.get_xlabel() ylabel = line.get_label() x = line.get_xdata() y = line.get_ydata() horHeader.append(ylabel) for k in range(0, len(x)): newItem = QtWidgets.QTableWidgetItem('%.14f' % y[k]) table.setItem(k, i, newItem) for k in range(0, len(x)): verHeader.append('%.14f' % x[k]) table.setHorizontalHeaderLabels(horHeader) table.setVerticalHeaderLabels(verHeader) table.resizeRowsToContents() tableDialog.exec_() #@QtCore.pyqtSlot() def slotRemoveLine(self): lines = self.canvas.axes.get_lines() items = [] for line in lines: label = line.get_label() items.append(label) nameToRemove, ok = QtWidgets.QInputDialog.getItem( self, "Choose line to remove", "Lines:", items, 0, False) if ok: for i, line in enumerate(lines): label = line.get_label() if label == str(nameToRemove): self.canvas.axes.lines.pop(i) #self.reformatPlot() # updateLegend will also call canvas.draw() self.updateLegend() return def newFromTemplate(self, template): """ template is a dictionary: .. code-block:: javascript { "curves": [ [ "variableName", [ 1, -1 ], [ "0.05", "*" ], "variableName(0.05, *)", { "color": [ 0.0, 0.0, 0.0, 1.0 ], "linestyle": "-", "linewidth": 1.0, "marker": "o", "markeredgecolor": "#000000ff", "markerfacecolor": "#000000ff", "markersize": 0.0 } ] ], "gridOn": true, "legendOn": true, "plotTitle": "", "plotType": 0, "updateInterval": 0, "windowTitle": "tutorial_dealii_8.ActiveSurface.cs(2.0, *)", "xlabel": "y-cordinate", "xmax": 31.45, "xmax_policy": 0, "xmin": -0.4500000000000002, "xmin_policy": 0, "xscale": "linear", "xtransform": 1.0, "ylabel": "cs (mol/m**2)", "ymax": 0.34935417753491, "ymax_policy": 1, "ymin": -0.00938086024107475, "ymin_policy": 1, "yscale": "linear", "ytransform": 1.0 } """ processes = {} for process in self.plotter.getProcesses(): processes[process.Name] = process if len(processes) == 0: return if len(template) == 0: return False curves = template['curves'] if 'plotType' in template: self.plotType = int(template['plotType']) if 'xtransform' in template: self.xtransform = template['xtransform'] if 'ytransform' in template: self.ytransform = template['ytransform'] for i, curve in enumerate(curves): variableName = curve[0] domainIndexes = curve[1] domainPoints = curve[2] label = None pd = None if len(curve) > 3: label = curve[3] if len(curve) > 4: pd = daePlot2dDefaults.from_dict(curve[4]) windowTitle = "Select process for variable {0} (of {1})".format( i + 1, len(curves)) var_to_look_for = "Variable: {0}({1})".format( variableName, ','.join(domainPoints)) items = sorted(processes.keys()) processName, ok = self.showSelectProcessDialog( windowTitle, var_to_look_for, items) if not ok: return False process = processes[str(processName)] for variable in process.Variables: if variableName == variable.Name: if self.plotType == daeChooseVariable.plot2D or self.plotType == daeChooseVariable.plot2DAutoUpdated: variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, currentTime = daeChooseVariable.get2DData( variable, domainIndexes, domainPoints) self._addNewCurve(variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, currentTime, label, pd) break elif self.plotType == daeChooseVariable.plot2DAnimated: variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, times = daeChooseVariable.get2DAnimatedData( variable, domainIndexes, domainPoints) self._addNewAnimatedCurve(variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, times, None, None) for action in self.actions_to_disable_permanently: action.setEnabled(False) break else: raise RuntimeError('Invalid plot type') if 'xlabel' in template: self.canvas.axes.set_xlabel(template['xlabel'], fontproperties=self.fp12) if 'xmin' in template and 'xmax' in template: self.canvas.axes.set_xlim(float(template['xmin']), float(template['xmax'])) if 'xscale' in template: self.canvas.axes.set_xscale(template['xscale']) if 'ylabel' in template: self.canvas.axes.set_ylabel(template['ylabel'], fontproperties=self.fp12) if 'ymin' in template and 'ymax' in template: self.canvas.axes.set_ylim(float(template['ymin']), float(template['ymax'])) if 'yscale' in template: self.canvas.axes.set_yscale(template['yscale']) if 'gridOn' in template: self.gridOn = template['gridOn'] self.canvas.axes.grid(self.gridOn) if 'legendOn' in template: self.legendOn = template['legendOn'] if self.legendOn: self.canvas.axes.legend(loc=0, prop=self.fp9, numpoints=1, fancybox=True) else: self.canvas.axes.legend_ = None if 'plotTitle' in template: self.canvas.axes.set_title(template['plotTitle']) if 'windowTitle' in template: self.setWindowTitle(template['windowTitle']) if 'xmin_policy' in template: self.xmin_policy = int(template['xmin_policy']) if 'xmax_policy' in template: self.xmax_policy = int(template['xmax_policy']) if 'ymin_policy' in template: self.ymin_policy = int(template['ymin_policy']) if 'ymax_policy' in template: self.ymax_policy = int(template['ymax_policy']) #fmt = matplotlib.ticker.ScalarFormatter(useOffset = False) #fmt.set_scientific(False) #fmt.set_powerlimits((-3, 4)) #self.canvas.axes.xaxis.set_major_formatter(fmt) #self.canvas.axes.yaxis.set_major_formatter(fmt) self.figure.tight_layout() self.canvas.draw() return True def showSelectProcessDialog(self, windowTitle, label, items): dlg = QtWidgets.QInputDialog(self) dlg.resize(500, 300) dlg.setWindowTitle(windowTitle) dlg.setLabelText(label) dlg.setComboBoxItems(items) dlg.setComboBoxEditable(False) dlg.setOption(QtWidgets.QInputDialog.UseListViewForComboBoxItems) if dlg.exec_() == QtWidgets.QDialog.Accepted: return str(dlg.textValue()), True else: return '', False def _updateFrame(self, frame): lines = [] for curve in self.curves: line = curve[0] lines.append(line) times = curve[5] xPoints = curve[6] yPoints = curve[7] yData = yPoints[frame] line.set_ydata(yData) time = times[frame] if self.xmin_policy == 0: # From 1st frame xmin = numpy.min(xPoints) elif self.xmin_policy == 1: # Overall min value xmin = numpy.min(xPoints) elif self.xmin_policy == 2: # Adaptive xmin = numpy.min(xPoints) else: # Do not change it xmin = self.canvas.axes.get_xlim()[0] if self.xmax_policy == 0: # From 1st frame xmax = numpy.max(xPoints) dx = 0.5 * (xmax - xmin) * 0.05 elif self.xmax_policy == 1: # Overall max value xmax = numpy.max(xPoints) dx = 0.5 * (xmax - xmin) * 0.05 elif self.xmax_policy == 2: # Adaptive xmax = numpy.max(xPoints) dx = 0.5 * (xmax - xmin) * 0.05 else: # Do not change it xmax = self.canvas.axes.get_xlim()[1] dx = 0.0 if self.ymin_policy == 0: # From 1st frame ymin = numpy.min(yPoints[0]) elif self.ymin_policy == 1: # Overall min value ymin = numpy.min(yPoints) elif self.ymin_policy == 2: # Adaptive ymin = numpy.min(yPoints[frame]) else: # Do not change it ymin = self.canvas.axes.get_ylim()[0] if self.ymax_policy == 0: # From 1st frame ymax = numpy.max(yPoints[0]) dy = 0.5 * (ymax - ymin) * 0.05 elif self.ymax_policy == 1: # Overall max value ymax = numpy.max(yPoints) dy = 0.5 * (ymax - ymin) * 0.05 elif self.ymax_policy == 2: # Adaptive ymax = numpy.max(yPoints[frame]) dy = 0.5 * (ymax - ymin) * 0.05 else: # Do not change it ymax = self.canvas.axes.get_ylim()[1] dy = 0.0 self.canvas.axes.set_xlim(xmin - dx, xmax + dx) self.canvas.axes.set_ylim(ymin - dy, ymax + dy) self.canvas.axes.set_title('time = %f s' % time, fontproperties=self.fp10) if frame == len(times) - 1: # the last frame for action in self.actions_to_disable: action.setEnabled(True) del self.funcAnimation self.funcAnimation = None self.play_animation.setIcon( QtGui.QIcon(join(images_dir, 'media-playback-start.png'))) self.play_animation.setStatusTip('Start animation') self.play_animation.setText('Start animation') self._isAnimating = False return lines def _startAnimation(self): #if len(self.curves) != 1: # return # Set properties for the frame 0 curve = self.curves[0] times = curve[5] frames = numpy.arange(0, len(times)) self.canvas.axes.set_title('time = %f s' % times[0], fontproperties=self.fp10) self.funcAnimation = animation.FuncAnimation( self.figure, self._updateFrame, frames, interval=self.updateInterval, blit=False, repeat=False) self.play_animation.setIcon( QtGui.QIcon(join(images_dir, 'media-playback-pause.png'))) self.play_animation.setStatusTip('Pause animation') self.play_animation.setText('Pause animation') self._isAnimating = True #At the end do not call show() nor save(), they will be ran by a caller #@QtCore.pyqtSlot() def playAnimation(self): if self.funcAnimation: # animation started - pause/resume it if self._isAnimating: # pause it for action in self.actions_to_disable: action.setEnabled(True) self.funcAnimation.event_source.stop() self.play_animation.setIcon( QtGui.QIcon(join(images_dir, 'media-playback-start.png'))) self.play_animation.setStatusTip('Start animation') self.play_animation.setText('Start animation') self._isAnimating = False self.canvas.draw() else: # restart it for action in self.actions_to_disable: action.setEnabled(False) self.funcAnimation.event_source.start() self.play_animation.setIcon( QtGui.QIcon(join(images_dir, 'media-playback-pause.png'))) self.play_animation.setStatusTip('Pause animation') self.play_animation.setText('Pause animation') self._isAnimating = True self.canvas.draw() else: # start animation for action in self.actions_to_disable: action.setEnabled(False) self._startAnimation() self.canvas.draw() #@QtCore.pyqtSlot() def stopAnimation(self): if self.funcAnimation: # animated started - stop it for action in self.actions_to_disable: action.setEnabled(True) self.funcAnimation.event_source.stop() del self.funcAnimation self.funcAnimation = None self.play_animation.setIcon( QtGui.QIcon(join(images_dir, 'media-playback-start.png'))) self.play_animation.setStatusTip('Start animation') self.play_animation.setText('Start animation') self._isAnimating = False # Go back to frame 0 self._updateFrame(0) self.canvas.draw() #@QtCore.pyqtSlot() def exportVideo(self): dlg = daeSavePlot2DVideo() for enc in sorted(animation.writers.list()): dlg.ui.comboEncoders.addItem(str(enc)) dlg.ui.lineeditCodec.setText('') dlg.ui.lineeditFilename.setText( os.path.join(os.path.expanduser('~'), 'video.avi')) dlg.ui.spinFPS.setValue(10) dlg.ui.lineeditExtraArgs.setText(json.dumps( [])) # ['-pix_fmt', 'yuv420p'] dlg.ui.spinBitrate.setValue(-1) if dlg.exec_() != QtWidgets.QDialog.Accepted: return False filename = str(dlg.ui.lineeditFilename.text()) fps = int(dlg.ui.spinFPS.value()) encoder = str(dlg.ui.comboEncoders.currentText()) codec = str(dlg.ui.lineeditCodec.text()) bitrate = int(dlg.ui.spinBitrate.value()) extra_args = [] try: extra_args = list(json.loads(str(dlg.ui.lineeditExtraArgs.text()))) except: pass if bitrate == -1: bitrate = None if codec == '': codec = None if not extra_args: extra_args = None print('%s(fps = %s, codec = %s, bitrate = %s, extra_args = %s) -> %s' % (encoder, fps, codec, bitrate, extra_args, filename)) # First stop the existing animation, if already started self.stopAnimation() Writer = animation.writers[encoder] writer = Writer(fps=fps, codec=codec, bitrate=bitrate, extra_args=extra_args) self._startAnimation() self.funcAnimation.save(filename, writer=writer) def slotFromUserData(self): dlg = daeUserData() if dlg.exec_() != QtWidgets.QDialog.Accepted: return self.newCurveFromUserData(dlg.xLabel, dlg.yLabel, dlg.lineLabel, dlg.xPoints, dlg.yPoints) def newCurveFromUserData(self, xAxisLabel, yAxisLabel, lineLabel, xPoints, yPoints): class dummyVariable(object): def __init__(self, name='', units=''): self.Name = name self.Units = units self._addNewCurve(dummyVariable(lineLabel), [], [], xAxisLabel, yAxisLabel, xPoints, yPoints, None, lineLabel, None) return True #@QtCore.pyqtSlot() def newCurve(self): processes = self.plotter.getProcesses() if not self._cv_dlg: self._cv_dlg = daeChooseVariable(self.plotType) self._cv_dlg.updateProcessesList(processes) self._cv_dlg.setWindowTitle('Choose variable for 2D plot') if self._cv_dlg.exec_() != QtWidgets.QDialog.Accepted: return False variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, currentTime = self._cv_dlg.getPlot2DData( ) self._addNewCurve(variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, currentTime, None, None) return True def newVar1_vs_Var2Curve(self): processes = self.plotter.getProcesses() if not self._cv_dlg: self._cv_dlg = daeChooseVariable(self.plotType) self._cv_dlg.updateProcessesList(processes) self._cv_dlg.setWindowTitle( 'Choose the 1st variable (x axis) for 2D plot') if self._cv_dlg.exec_() != QtWidgets.QDialog.Accepted: return False variable1, domainIndexes1, domainPoints1, xAxisLabel1, yAxisLabel1, xPoints1, yPoints1, currentTime1 = self._cv_dlg.getPlot2DData( ) self._cv_dlg.setWindowTitle( 'Choose the 2nd variable (y axis) for 2D plot') if self._cv_dlg.exec_() != QtWidgets.QDialog.Accepted: return False variable2, domainIndexes2, domainPoints2, xAxisLabel2, yAxisLabel2, xPoints2, yPoints2, currentTime2 = self._cv_dlg.getPlot2DData( ) if len(yPoints1) != len(yPoints2): QtWidgets.QMessageBox.warning( None, 'Variable1 vs. variable2 2D plot', 'The number of points in variables do not match') return False self._addNewCurve(variable2, domainIndexes2, domainPoints2, yAxisLabel1, yAxisLabel2, yPoints1, yPoints2, currentTime2, None, None) return True #@QtCore.pyqtSlot() def newAnimatedCurve(self): processes = self.plotter.getProcesses() if not self._cv_dlg: self._cv_dlg = daeChooseVariable(self.plotType) self._cv_dlg.updateProcessesList(processes) self._cv_dlg.setWindowTitle('Choose variable for animated 2D plot') if self._cv_dlg.exec_() != QtWidgets.QDialog.Accepted: return False dlg = daeAnimationParameters() if dlg.exec_() != QtWidgets.QDialog.Accepted: return self.updateInterval = int(dlg.ui.spinUpdateInterval.value()) self.xmin_policy = dlg.ui.comboXmin.currentIndex() self.xmax_policy = dlg.ui.comboXmax.currentIndex() self.ymin_policy = dlg.ui.comboYmin.currentIndex() self.ymax_policy = dlg.ui.comboYmax.currentIndex() variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, times = self._cv_dlg.getPlot2DAnimatedData( ) self._addNewAnimatedCurve(variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, times, None, None) for action in self.actions_to_disable_permanently: action.setEnabled(False) self._updateFrame(0) return True def _addNewAnimatedCurve(self, variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints_2D, times, label=None, pd=None): domains = '(' + ', '.join(domainPoints) + ')' if not label: label = variable.Name.replace("&", "").replace(";", "") + domains line = self.addLine(xAxisLabel, yAxisLabel, xPoints, yPoints_2D[0], label, pd) self.setWindowTitle(label) # update fun is None self.curves.append((line, variable, domainIndexes, domainPoints, None, times, xPoints, yPoints_2D)) def _addNewCurve(self, variable, domainIndexes, domainPoints, xAxisLabel, yAxisLabel, xPoints, yPoints, currentTime, label=None, pd=None): domains = "(" for i in range(0, len(domainPoints)): if i != 0: domains += ", " domains += domainPoints[i] domains += ")" if not label: label = variable.Name.replace("&", "").replace(";", "") + domains line = self.addLine(xAxisLabel, yAxisLabel, xPoints, yPoints, label, pd) self.setWindowTitle(label) # everything after update fun is none self.curves.append((line, variable, domainIndexes, domainPoints, daeChooseVariable.get2DData, None, None, None)) def addLine(self, xAxisLabel, yAxisLabel, xPoints, yPoints, label, pd): no_lines = len(self.canvas.axes.get_lines()) if not pd: n = no_lines % len(dae2DPlot.plotDefaults) pd = dae2DPlot.plotDefaults[n] xPoints_ = numpy.array(xPoints) * self.xtransform yPoints_ = numpy.array(yPoints) * self.ytransform line, = self.canvas.axes.plot(xPoints_, yPoints_, label=label, color=pd.color, linewidth=pd.linewidth, \ linestyle=pd.linestyle, marker=pd.marker, markersize=pd.markersize, \ markerfacecolor=pd.markerfacecolor, markeredgecolor=pd.markeredgecolor) if no_lines == 0: # Set labels, fonts, gridlines and limits only when adding the first line self.canvas.axes.set_xlabel(xAxisLabel, fontproperties=self.fp12) self.canvas.axes.set_ylabel(yAxisLabel, fontproperties=self.fp12) t = self.canvas.axes.xaxis.get_offset_text() t.set_fontproperties(self.fp10) t = self.canvas.axes.yaxis.get_offset_text() t.set_fontproperties(self.fp10) self.updateGrid() # Update the legend and (x,y) limits after every addition self.updateLegend() self.reformatPlot() return line def reformatPlot(self): lines = self.canvas.axes.get_lines() xmin = 1e20 xmax = -1e20 ymin = 1e20 ymax = -1e20 for line in lines: if numpy.min(line.get_xdata()) < xmin: xmin = numpy.min(line.get_xdata()) if numpy.max(line.get_xdata()) > xmax: xmax = numpy.max(line.get_xdata()) if numpy.min(line.get_ydata()) < ymin: ymin = numpy.min(line.get_ydata()) if numpy.max(line.get_ydata()) > ymax: ymax = numpy.max(line.get_ydata()) dx = (xmax - xmin) * 0.05 dy = (ymax - ymin) * 0.05 xmin -= dx xmax += dx ymin -= dy ymax += dy self.canvas.axes.set_xlim(xmin, xmax) self.canvas.axes.set_ylim(ymin, ymax) #self.canvas.axes.grid(self.gridOn) #if self.legendOn: # self.canvas.axes.legend(loc = 0, prop=self.fp9, numpoints = 1, fancybox=True) #else: # self.canvas.axes.legend_ = None self.figure.tight_layout() self.canvas.draw()
class PlotWindow(QtWidgets.QMainWindow): def __init__(self, nrows=1, ncols=1, **kwargs): matplotlib.use('Qt5Agg') qapp = QtWidgets.QApplication.instance() if qapp is None: qapp = QtWidgets.QApplication(sys.argv) self.qapp = qapp super().__init__() self._main = QtWidgets.QWidget() self.setStyle(QStyleFactory.create('Fusion')) self.setCentralWidget(self._main) self.layout = QGridLayout(self._main) marker_kw = {} for k in marker_default_params.keys(): if k in kwargs.keys(): marker_kw[k] = kwargs.pop(k) title = kwargs.pop('title', None) icon = kwargs.pop('icon', None) if icon != None: self.setWindowIcon(QtGui.QIcon(str(icon))) marker_kw['interactive'] = kwargs.pop('interactive', True) marker_kw['top_axes'] = kwargs.pop('top_axes', None) marker_kw['link_all'] = kwargs.pop('link_all', False) self.single_trace = kwargs.pop('single_trace', False) subplot_kw = kwargs.pop('subplot_kw', {}) sharex = kwargs.pop('sharex', False) sharey = kwargs.pop('sharey', False) gridspec_kw = kwargs.pop('gridspec_kw', None) self.fig = plt.figure(**kwargs) self.axes_grid = self.fig.subplots(nrows, ncols, squeeze=False, sharex=False, sharey=False, subplot_kw=subplot_kw, gridspec_kw=gridspec_kw) self.axes = self.axes_grid.flatten() self.nrows = nrows self.ncols = ncols self.canvas = self.fig.canvas self.canvas.mpl_disconnect(self.canvas.manager.key_press_handler_id) self.canvas.manager.show = self._show self.layout.addWidget(self.canvas, 0, 0, (self.nrows * self.ncols) + 1, 1) self.toolbar = NavigationToolbar(self.canvas, self, coordinates=False) self.build_toolbar() self.addToolBar(self.toolbar) self.fig.canvas.toolbar = self.toolbar self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus) self.canvas.setFocus() p = self.palette() p.setColor(self.backgroundRole(), Qt.white) self.setPalette(p) title = 'Figure {}'.format( self.fig.canvas.manager.num) if title == None else title self.setWindowTitle(title) self._drop_event_handler = None self.fig.marker_enable(**marker_kw) self.fig.qapp = self.qapp self.fig.app = self self.draw_updates = False self.axes_cb_group = [] self.current_data_format = None self.data_format_options = None for i, ax in enumerate(self.axes): ax_cb = AxesCheckBoxGroup( self, ax, "Axes {},{}".format(i // self.nrows, i % self.nrows)) self.axes_cb_group.append(ax_cb) def keyPressEvent(self, event): if event.key() in (QtCore.Qt.Key_F5, ): self.fig.canvas.draw() super().keyPressEvent(event) def set_draw_updates(self, state): prev = self.draw_updates self.draw_updates = state return prev def add_toolbar_actions(self, *widgets, end=True): for icon_path, name, tooltip, action in widgets: icon = QtGui.QPixmap(str(icon_path)) icon.setDevicePixelRatio(self.canvas._dpi_ratio) a = self.toolbar.addAction(QtGui.QIcon(icon), name, action) a.setToolTip(tooltip) if end: locLabel = QLabel("", self.toolbar) locLabel.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) locLabel.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Ignored)) self.toolbar.addWidget(locLabel) def build_toolbar(self): self.toolbar.removeAction(self.toolbar._actions['configure_subplots']) self.toolbar.removeAction(self.toolbar._actions['forward']) self.toolbar.removeAction(self.toolbar._actions['back']) widgets = [ (str(dir_ / 'icons/layout_large.png'), 'Layout', 'Apply Tight Layout', self.set_tight_layout), (str(dir_ / 'icons/copy_large.png'), 'Copy', 'Copy To Clipboard', self.copy_figure), (str(dir_ / 'icons/erase_large.png'), 'Delete', 'Remove All Markers', self.remove_all), (str(dir_ / 'icons/autoscale_y.png'), 'Autoscale', 'Autoscale Y-Axis', self.autoscale_y), (str(dir_ / 'icons/autoscale_x.png'), 'Autoscale', 'Autoscale X-Axis', self.autoscale_x), (str(dir_ / 'icons/set_format_large.png'), 'Set Data Format', 'Set Data Format', self.set_data_format), ] self.add_toolbar_actions(*widgets, end=False) self.toolbar.addSeparator() def add_drop_event_handler(self, handler): self._drop_event_handler = handler if self._drop_event_handler != None: self.setAcceptDrops(True) def dragEnterEvent(self, e): if e.mimeData().hasText(): text = e.mimeData().text() m = re.search(r's\d+p$', text) if m != None: e.accept() else: e.ignore() else: e.ignore() def dropEvent(self, e): text = e.mimeData().text() self._drop_event_handler(text) self.change_data_format(self.current_data_format) self.update_axes_groups() self.autoscale_x() self.remove_all() self.fig.canvas.draw() def set_data_format(self): dialog = DataFormatDialog(self, self.change_data_format, self.data_format_options) dialog.show() def change_data_format(self, options): self.current_data_format = options for i, ax in enumerate(self.axes): self._data_format_handler(ax, options[i]) self.autoscale_y() def add_data_format_handler(self, func, format_options, initial=None): self._data_format_handler = func self.data_format_options = format_options self.current_data_format = [initial] * len(self.axes) def autoscale_x(self): for ax_cb in self.axes_cb_group: ax_cb.scale_visible(yscale=False) self.fig.canvas.draw() def autoscale_y(self): for ax_cb in self.axes_cb_group: ax_cb.scale_visible(xscale=False) self.fig.canvas.draw() def remove_all(self): for ax in self.fig._top_axes: ax.marker_delete_all() ax.draw_lines_markers() for l_ax in ax.marker_linked_axes: l_ax.marker_delete_all() l_ax.draw_lines_markers() def set_tight_layout(self): self.fig.tight_layout() self.canvas.draw() def copy_figure(self): buf = io.BytesIO() self.fig.savefig(buf) image = Image.open(buf) output = io.BytesIO() image.convert("RGB").save(output, "BMP") data = output.getvalue()[14:] output.close() win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data) win32clipboard.CloseClipboard() buf.close() def update_axes_groups(self): for i, ax_cb in enumerate(self.axes_cb_group): ax_cb.update_checkboxes() def create_axes_groups(self): for i, ax_cb in enumerate(self.axes_cb_group): ax_cb.add_to_layout(self.layout, i, 1) self.layout.addWidget(QGroupBox(), i + 1, 1) self.layout.setColumnStretch(0, 1) self.layout.setRowStretch(i + 1, 1) def _show(self): self.create_axes_groups() self.set_draw_updates(True) self.show() plt.close(self.fig)
class FigureTab: cursors = [15000, 45000] colors = ['orange', 'violet'] def __init__(self, layout, vna): # create figure self.figure = Figure() if sys.platform != 'win32': self.figure.set_facecolor('none') self.canvas = FigureCanvas(self.figure) layout.addWidget(self.canvas) # create navigation toolbar self.toolbar = NavigationToolbar(self.canvas, None, False) self.toolbar.layout().setSpacing(6) # remove subplots action actions = self.toolbar.actions() if int(matplotlib.__version__[0]) < 2: self.toolbar.removeAction(actions[7]) else: self.toolbar.removeAction(actions[6]) self.toolbar.addSeparator() self.cursorLabels = {} self.cursorValues = {} self.cursorMarkers = {} self.cursorPressed = {} for i in range(len(self.cursors)): self.cursorMarkers[i] = None self.cursorPressed[i] = False self.cursorLabels[i] = QLabel('Cursor %d, kHz' % (i + 1)) self.cursorLabels[i].setStyleSheet('color: %s' % self.colors[i]) self.cursorValues[i] = QSpinBox() self.cursorValues[i].setMinimumSize(90, 0) self.cursorValues[i].setSingleStep(10) self.cursorValues[i].setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self.toolbar.addWidget(self.cursorLabels[i]) self.toolbar.addWidget(self.cursorValues[i]) self.cursorValues[i].valueChanged.connect(partial(self.set_cursor, i)) self.canvas.mpl_connect('button_press_event', partial(self.press_marker, i)) self.canvas.mpl_connect('motion_notify_event', partial(self.move_marker, i)) self.canvas.mpl_connect('button_release_event', partial(self.release_marker, i)) self.toolbar.addSeparator() self.plotButton = QPushButton('Rescale') self.toolbar.addWidget(self.plotButton) layout.addWidget(self.toolbar) self.plotButton.clicked.connect(self.plot) self.mode = None self.vna = vna def add_cursors(self, axes): if self.mode == 'gain_short' or self.mode == 'gain_open': columns = ['Freq., kHz', 'G, dB', r'$\angle$ G, deg'] else: columns = ['Freq., kHz', 'Re(Z), \u03A9', 'Im(Z), \u03A9', '|Z|, \u03A9', r'$\angle$ Z, deg', 'SWR', r'|$\Gamma$|', r'$\angle$ $\Gamma$, deg', 'RL, dB'] y = len(self.cursors) * 0.04 + 0.01 for i in range(len(columns)): self.figure.text(0.19 + 0.1 * i, y, columns[i], horizontalalignment = 'right') self.cursorRows = {} for i in range(len(self.cursors)): y = len(self.cursors) * 0.04 - 0.03 - 0.04 * i self.figure.text(0.01, y, 'Cursor %d' % (i + 1), color = self.colors[i]) self.cursorRows[i] = {} for j in range(len(columns)): self.cursorRows[i][j] = self.figure.text(0.19 + 0.1 * j, y, '', horizontalalignment = 'right') if self.mode == 'smith': self.cursorMarkers[i], = axes.plot(0.0, 0.0, marker = 'o', color = self.colors[i]) else: self.cursorMarkers[i] = axes.axvline(0.0, color = self.colors[i], linewidth = 2) self.set_cursor(i, self.cursorValues[i].value()) def set_cursor(self, index, value): FigureTab.cursors[index] = value marker = self.cursorMarkers[index] if marker is None: return row = self.cursorRows[index] freq = value gamma = self.vna.gamma(freq) if self.mode == 'smith': marker.set_xdata(gamma.real) marker.set_ydata(gamma.imag) else: marker.set_xdata(freq) row[0].set_text('%d' % freq) if self.mode == 'gain_short': gain = self.vna.gain_short(freq) magnitude = 20.0 * np.log10(np.absolute(gain)) angle = np.angle(gain, deg = True) row[1].set_text(unicode_minus('%.1f' % magnitude)) row[2].set_text(unicode_minus('%.1f' % angle)) elif self.mode == 'gain_open': gain = self.vna.gain_open(freq) magnitude = 20.0 * np.log10(np.absolute(gain)) angle = np.angle(gain, deg = True) row[1].set_text(unicode_minus('%.1f' % magnitude)) row[2].set_text(unicode_minus('%.1f' % angle)) else: swr = self.vna.swr(freq) z = self.vna.impedance(freq) rl = 20.0 * np.log10(np.absolute(gamma)) if rl > -0.01: rl = 0.0 row[1].set_text(metric_prefix(z.real)) row[2].set_text(metric_prefix(z.imag)) row[3].set_text(metric_prefix(np.absolute(z))) angle = np.angle(z, deg = True) if np.abs(angle) < 0.1: angle = 0.0 row[4].set_text(unicode_minus('%.1f' % angle)) row[5].set_text(unicode_minus('%.2f' % swr)) row[6].set_text(unicode_minus('%.2f' % np.absolute(gamma))) angle = np.angle(gamma, deg = True) if np.abs(angle) < 0.1: angle = 0.0 row[7].set_text(unicode_minus('%.1f' % angle)) row[8].set_text(unicode_minus('%.2f' % rl)) self.canvas.draw() def press_marker(self, index, event): if not event.inaxes: return if self.mode == 'smith': return marker = self.cursorMarkers[index] if marker is None: return contains, misc = marker.contains(event) if not contains: return self.cursorPressed[index] = True def move_marker(self, index, event): if not event.inaxes: return if self.mode == 'smith': return if not self.cursorPressed[index]: return self.cursorValues[index].setValue(event.xdata) def release_marker(self, index, event): self.cursorPressed[index] = False def xlim(self, freq): start = freq[0] stop = freq[-1] min = np.minimum(start, stop) max = np.maximum(start, stop) margin = (max - min) / 50 return (min - margin, max + margin) def plot(self): getattr(self, 'plot_%s' % self.mode)() def update(self, mode): start = self.vna.dut.freq[0] stop = self.vna.dut.freq[-1] min = np.minimum(start, stop) max = np.maximum(start, stop) for i in range(len(self.cursors)): value = self.cursors[i] self.cursorValues[i].setRange(min, max) self.cursorValues[i].setValue(value) self.set_cursor(i, value) getattr(self, 'update_%s' % mode)() def plot_curves(self, freq, data1, label1, limit1, data2, label2, limit2): matplotlib.rcdefaults() matplotlib.rcParams['axes.formatter.use_mathtext'] = True self.figure.clf() bottom = len(self.cursors) * 0.04 + 0.13 self.figure.subplots_adjust(left = 0.16, bottom = bottom, right = 0.84, top = 0.96) axes1 = self.figure.add_subplot(111) axes1.cla() axes1.xaxis.grid() axes1.set_xlabel('kHz') axes1.set_ylabel(label1) xlim = self.xlim(freq) axes1.set_xlim(xlim) if limit1 is not None: axes1.set_ylim(limit1) self.curve1, = axes1.plot(freq, data1, color = 'blue', label = label1) self.add_cursors(axes1) if data2 is None: self.canvas.draw() return axes1.tick_params('y', color = 'blue', labelcolor = 'blue') axes1.yaxis.label.set_color('blue') axes2 = axes1.twinx() axes2.spines['left'].set_color('blue') axes2.spines['right'].set_color('red') axes2.set_ylabel(label2) axes2.set_xlim(xlim) if limit2 is not None: axes2.set_ylim(limit2) axes2.tick_params('y', color = 'red', labelcolor = 'red') axes2.yaxis.label.set_color('red') self.curve2, = axes2.plot(freq, data2, color = 'red', label = label2) self.canvas.draw() def plot_gain(self, gain): freq = self.vna.dut.freq data1 = 20.0 * np.log10(np.absolute(gain)) data2 = np.angle(gain, deg = True) self.plot_curves(freq, data1, 'G, dB', (-110, 110.0), data2, r'$\angle$ G, deg', (-198, 198)) def plot_gain_short(self): self.mode = 'gain_short' self.plot_gain(self.vna.gain_short(self.vna.dut.freq)) def plot_gain_open(self): self.mode = 'gain_open' self.plot_gain(self.vna.gain_open(self.vna.dut.freq)) def update_gain(self, gain, mode): if self.mode == mode: self.curve1.set_xdata(self.vna.dut.freq) self.curve1.set_ydata(20.0 * np.log10(np.absolute(gain))) self.curve2.set_xdata(self.vna.dut.freq) self.curve2.set_ydata(np.angle(gain, deg = True)) self.canvas.draw() else: self.mode = mode self.plot_gain(gain) def update_gain_short(self): self.update_gain(self.vna.gain_short(self.vna.dut.freq), 'gain_short') def update_gain_open(self): self.update_gain(self.vna.gain_open(self.vna.dut.freq), 'gain_open') def plot_magphase(self, freq, data, label, mode): self.mode = mode data1 = np.absolute(data) data2 = np.angle(data, deg = True) max = np.fmax(0.01, data1.max()) label1 = r'|%s|' % label label2 = r'$\angle$ %s, deg' % label self.plot_curves(freq, data1, label1, (-0.05 * max, 1.05 * max), data2, label2, (-198, 198)) def update_magphase(self, freq, data, label, mode): if self.mode == mode: self.curve1.set_xdata(freq) self.curve1.set_ydata(np.absolute(data)) self.curve2.set_xdata(freq) self.curve2.set_ydata(np.angle(data, deg = True)) self.canvas.draw() else: self.plot_magphase(freq, data, label, mode) def plot_open(self): self.plot_magphase(self.vna.open.freq, self.vna.open.data, 'open', 'open') def update_open(self): self.update_magphase(self.vna.open.freq, self.vna.open.data, 'open', 'open') def plot_short(self): self.plot_magphase(self.vna.short.freq, self.vna.short.data, 'short', 'short') def update_short(self): self.update_magphase(self.vna.short.freq, self.vna.short.data, 'short', 'short') def plot_load(self): self.plot_magphase(self.vna.load.freq, self.vna.load.data, 'load', 'load') def update_load(self): self.update_magphase(self.vna.load.freq, self.vna.load.data, 'load', 'load') def plot_dut(self): self.plot_magphase(self.vna.dut.freq, self.vna.dut.data, 'dut', 'dut') def update_dut(self): self.update_magphase(self.vna.dut.freq, self.vna.dut.data, 'dut', 'dut') def plot_smith_grid(self, axes, color): load = 50.0 ticks = np.array([0.0, 0.2, 0.5, 1.0, 2.0, 5.0]) for tick in ticks * load: axis = np.logspace(-4, np.log10(1.0e3), 200) * load z = tick + 1.0j * axis gamma = (z - load)/(z + load) axes.plot(gamma.real, gamma.imag, color = color, linewidth = 0.4, alpha = 0.3) axes.plot(gamma.real, -gamma.imag, color = color, linewidth = 0.4, alpha = 0.3) z = axis + 1.0j * tick gamma = (z - load)/(z + load) axes.plot(gamma.real, gamma.imag, color = color, linewidth = 0.4, alpha = 0.3) axes.plot(gamma.real, -gamma.imag, color = color, linewidth = 0.4, alpha = 0.3) if tick == 0.0: axes.text(1.0, 0.0, u'\u221E', color = color, ha = 'left', va = 'center', clip_on = True, fontsize = 'x-large') axes.text(-1.0, 0.0, u'0\u03A9', color = color, ha = 'left', va = 'bottom', clip_on = True) continue lab = u'%d\u03A9' % tick x = (tick - load) / (tick + load) axes.text(x, 0.0, lab, color = color, ha = 'left', va = 'bottom', clip_on = True) lab = u'j%d\u03A9' % tick z = 1.0j * tick gamma = (z - load)/(z + load) * 1.05 x = gamma.real y = gamma.imag angle = np.angle(gamma) * 180.0 / np.pi - 90.0 axes.text(x, y, lab, color = color, ha = 'center', va = 'center', clip_on = True, rotation = angle) lab = u'\u2212j%d\u03A9' % tick axes.text(x, -y, lab, color = color, ha = 'center', va = 'center', clip_on = True, rotation = -angle) def plot_smith(self): self.mode = 'smith' matplotlib.rcdefaults() self.figure.clf() bottom = len(self.cursors) * 0.04 + 0.05 self.figure.subplots_adjust(left = 0.0, bottom = bottom, right = 1.0, top = 1.0) axes1 = self.figure.add_subplot(111) self.plot_smith_grid(axes1, 'blue') gamma = self.vna.gamma(self.vna.dut.freq) self.curve1, = axes1.plot(gamma.real, gamma.imag, color = 'red') axes1.axis('equal') axes1.set_xlim(-1.12, 1.12) axes1.set_ylim(-1.12, 1.12) axes1.xaxis.set_visible(False) axes1.yaxis.set_visible(False) for loc, spine in axes1.spines.items(): spine.set_visible(False) self.add_cursors(axes1) self.canvas.draw() def update_smith(self): if self.mode == 'smith': gamma = self.vna.gamma(self.vna.dut.freq) self.curve1.set_xdata(gamma.real) self.curve1.set_ydata(gamma.imag) self.canvas.draw() else: self.plot_smith() def plot_imp(self): self.mode = 'imp' freq = self.vna.dut.freq z = self.vna.impedance(freq) data1 = np.fmin(9.99e4, np.absolute(z)) data2 = np.angle(z, deg = True) max = np.fmax(0.01, data1.max()) self.plot_curves(freq, data1, '|Z|, \u03A9', (-0.05 * max, 1.05 * max), data2, r'$\angle$ Z, deg', (-198, 198)) def update_imp(self): if self.mode == 'imp': freq = self.vna.dut.freq z = self.vna.impedance(freq) data1 = np.fmin(9.99e4, np.absolute(z)) data2 = np.angle(z, deg = True) self.curve1.set_xdata(freq) self.curve1.set_ydata(data1) self.curve2.set_xdata(freq) self.curve2.set_ydata(data2) self.canvas.draw() else: self.plot_imp() def plot_swr(self): self.mode = 'swr' freq = self.vna.dut.freq data1 = self.vna.swr(freq) self.plot_curves(freq, data1, 'SWR', (0.9, 3.1), None, None, None) def update_swr(self): if self.mode == 'swr': self.curve1.set_xdata(self.vna.dut.freq) self.curve1.set_ydata(self.vna.swr(self.vna.dut.freq)) self.canvas.draw() else: self.plot_swr() def plot_gamma(self): self.plot_magphase(self.vna.dut.freq, self.vna.gamma(self.vna.dut.freq), r'$\Gamma$', 'gamma') def update_gamma(self): self.update_magphase(self.vna.dut.freq, self.vna.gamma(self.vna.dut.freq), r'$\Gamma$', 'gamma') def plot_rl(self): self.mode = 'rl' freq = self.vna.dut.freq gamma = self.vna.gamma(freq) data1 = 20.0 * np.log10(np.absolute(gamma)) self.plot_curves(freq, data1, 'RL, dB', (-105, 5.0), None, None, None) def update_rl(self): if self.mode == 'rl': freq = self.vna.dut.freq gamma = self.vna.gamma(freq) data1 = 20.0 * np.log10(np.absolute(gamma)) self.curve1.set_xdata(freq) self.curve1.set_ydata(data1) self.canvas.draw() else: self.plot_rl()
class Ui_MainWindow(QMainWindow): def __init__(self): super(Ui_MainWindow, self).__init__() def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1900, 980) MainWindow.setMaximumSize(1900, 980) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") ###################################### Creating Table Widget ###################################### self.tableWidget = QtWidgets.QTableWidget(self.centralwidget) self.tableWidget.setGeometry(QtCore.QRect(50, 540, 850, 300)) font = QtGui.QFont() font.setBold(True) font.setItalic(True) font.setWeight(75) self.tableWidget.setFont(font) self.tableWidget.setObjectName("tableWidget") self.tableWidget.setColumnCount(4) # self.tableWidget.setRowCount(0) self.tableWidget.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.tableWidget.setHorizontalHeaderLabels( ("PW", " PA ", " PRI ", " Classification ")) self.tableWidget.setStatusTip("Table widget") #self.tableWidget.setRowCount(0) ####################################### Creating Pushbutton ############################################ self.pushButton_Start = QtWidgets.QPushButton(self.centralwidget) self.pushButton_Start.setGeometry(QtCore.QRect(400, 870, 180, 51)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setItalic(False) font.setUnderline(False) font.setWeight(75) self.pushButton_Start.setFont(font) self.pushButton_Start.setStyleSheet( "border-top-color: rgb(144, 255, 248);\n" "border-bottom-color: rgb(157, 255, 121);") self.pushButton_Start.setObjectName("pushButton_Start") ######################################## Creating Widgets(Plotting Graphs) #################################### self.Graph1 = pg.PlotWidget(self.centralwidget) self.Graph1.setGeometry(QtCore.QRect(50, 80, 850, 410)) self.Graph1.setLabel('left', 'PRI') self.Graph1.setLabel('bottom', 'Pulse Count') self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.Graph2 = pg.LayoutWidget(self.centralwidget) self.Graph2.setGeometry(QtCore.QRect(1000, 80, 850, 410)) self.Graph2.addWidget(self.canvas) self.Graph2.addWidget(self.toolbar) self.Dialer = QtWidgets.QDial(self.centralwidget) self.Dialer.setGeometry(QtCore.QRect(1640, 300, 150, 150)) self.Dialer.setNotchesVisible(True) # self.Dialer.setWrapping(True) self.Dialer.setMinimum(1) self.Dialer.setMaximum(100) self.Graph3 = pg.PlotWidget(self.centralwidget) self.Graph3.setGeometry(QtCore.QRect(1000, 520, 850, 410)) self.Graph3.setObjectName("Graph3") self.Graph3.setLabel('left', 'Count') self.Graph3.setLabel('bottom', 'TOA') ## Set Graph background colour ( White-'w',Black-'k',Green-'g',Red-'r',Yellow-'y',Blue-'b',cyan (bright blue-green)-'c',magenta (bright pink)-'m' ) ########### self.Graph1.setBackground('k') #self.Graph2.setBackground('k') self.Graph3.setBackground('k') MainWindow.setCentralWidget(self.centralwidget) ############################################## Status Bar #################################################### self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) ############################################## Tool Bar #################################################### self.toolbar = QtWidgets.QToolBar(MainWindow) self.toolbar.setObjectName("toolbar") self.toolbar.setMovable(False) self.toolbar.setGeometry(QtCore.QRect(0, 0, 1900, 50)) self.toolbar.setIconSize(QtCore.QSize(60, 60)) self.toolbar.addSeparator() ###################################### Creating Tool Bar Icons ################################################# self.btn1 = QtWidgets.QAction(MainWindow) self.btn1.setIcon(QtGui.QIcon("IP.png")) self.btn1.setObjectName("btn1") self.toolbar.addAction(self.btn1) self.btn2 = QtWidgets.QAction(MainWindow) self.btn2.setIcon(QtGui.QIcon("pulse1.png")) self.btn2.setObjectName("btn2") self.toolbar.addAction(self.btn2) self.btn3 = QtWidgets.QAction(MainWindow) self.btn3.setIcon(QtGui.QIcon("")) self.btn3.setObjectName("btn3") self.toolbar.addAction(self.btn3) self.btn4 = QtWidgets.QAction(MainWindow) self.btn4.setIcon(QtGui.QIcon("")) self.btn4.setObjectName("btn4") self.toolbar.addAction(self.btn4) self.pushButton_Start.clicked.connect(self.StartPA) self.Dialer.valueChanged.connect(self.Plot_DTOA) self.btn1.triggered.connect(self.Window1) self.btn2.triggered.connect(self.Window2) self.tableWidget.rowCount() self.df = pd.read_csv("pdw.csv") self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.thread = MyThread() # self.thread.change_value.connect(self.setProgressVal) self.thread.StopFlag = False self.thread.start() self.thread.StartPulseAnalysis = False self.thread.pd_PDW_Update.connect(self.updateGraphs) self.thread.Track_Update.connect(self.updateTrackTable) # Track_Update = pyqtSignal(bytearray) def StartPA(self): if self.thread.StartPulseAnalysis == False: self.pushButton_Start.setText("Stop PA") self.thread.StartPulseAnalysis = True elif self.thread.StartPulseAnalysis == True: self.pushButton_Start.setText("Start PA") self.thread.StartPulseAnalysis = False def updateGraphs(self, df_Pdw): print('Update Graphs') print(df_Pdw.head()) #Munny write here for Graphs updation def updateTrackTable(self, TrackData): print('Update Table') print(TrackData) #Munny Write here for Tbale updation def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.pushButton_Start.setText(_translate("MainWindow", "Start PA")) def Window1(self): self.Wd1 = Tool1_Window() self.Wd1.show() def Window2(self): self.Wd2 = Tool2_Window() self.Wd2.show() def Plot_DTOA(self): pen1 = pg.mkPen(color=(0, 150, 0), width=3, style=QtCore.Qt.SolidLine) TOA = self.df['TOA'] PW = self.df['PW'] PA = self.df['PA'] PC = self.df['PC'] DTOA = self.df['DTOA'] #print(PA) self.Graph1.plot(PC, DTOA, pen=pen1) #self.Graph1.setYRange() x_lim = self.Dialer.value() * 1000 #print("count", x_lim) y = [] #for index in range(10): cur_TOA = 0 index = 0 while cur_TOA < x_lim: for t in range(TOA[index], TOA[index] + PW[index]): y.append(1) for t in range(TOA[index] + PW[index], TOA[index + 1]): y.append(0) cur_TOA = TOA[index + 1] index = index + 1 x = [*range(0, len(y), 1)] plt.plot(x, y, "g") plt.title('DTOA vs Pulse Count') plt.ylabel('DTOA (micro sec)') plt.xlabel('Pulse Count->') plt.xlim(0, x_lim) plt.ylim(-1, +5) # plt.hlines(y=0, xmin=0, xmax=4000, linewidth=1, color='k', linestyles="--") self.canvas.draw() def Exit(self): reply = QMessageBox.question( self, 'Confirm Exit', 'Are you sure you want to exit Hub Configuration?', QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: quit()
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setWindowTitle("Demo14_3, 交互操作") self.__labMove = QLabel("Mouse Move:") self.__labMove.setMinimumWidth(200) self.ui.statusBar.addWidget(self.__labMove) self.__labPick = QLabel("Mouse Pick:") self.__labPick.setMinimumWidth(200) self.ui.statusBar.addWidget(self.__labPick) mpl.rcParams['font.sans-serif'] = ['SimHei'] #显示汉字为 楷体, 汉字不支持 粗体,斜体等设置 mpl.rcParams['font.size'] = 11 ## Windows自带的一些字体 ## 黑体:SimHei 宋体:SimSun 新宋体:NSimSun 仿宋:FangSong 楷体:KaiTi mpl.rcParams['axes.unicode_minus'] = False #减号unicode编码 self.__fig = None #Figue对象 self.__createFigure() #创建Figure和FigureCanvas对象,初始化界面 self.__drawFig1X2() ## ==============自定义功能函数======================== def __createFigure(self): ##创建绘图系统 self.__fig = mpl.figure.Figure(figsize=(8, 5)) #单位英寸 figCanvas = FigureCanvas(self.__fig) #创建FigureCanvas对象,必须传递一个Figure对象 self.__naviBar = NavigationToolbar(figCanvas, self) #创建NavigationToolbar工具栏 actList = self.__naviBar.actions() #关联的Action列表 for act in actList: #获得每个Action的标题和tooltip,可注释掉 print("text=%s,\ttoolTip=%s" % (act.text(), act.toolTip())) self.__changeActionLanguage() #改工具栏的语言为汉语 ##工具栏改造 actList[6].setVisible(False) #隐藏Subplots 按钮 actList[7].setVisible(False) #隐藏Customize按钮 act8 = actList[8] #分隔条 self.__naviBar.insertAction(act8, self.ui.actTightLayout) #"紧凑布局"按钮 self.__naviBar.insertAction(act8, self.ui.actSetCursor) #"十字光标"按钮 count = len(actList) #Action的个数 lastAction = actList[count - 1] #最后一个Action self.__naviBar.insertAction(lastAction, self.ui.actScatterAgain) #"重绘散点"按钮 lastAction.setVisible(False) #隐藏其原有的坐标提示 self.__naviBar.addSeparator() self.__naviBar.addAction(self.ui.actQuit) #"退出"按钮 self.__naviBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) #显示方式 self.addToolBar(self.__naviBar) #添加作为主窗口工具栏 self.setCentralWidget(figCanvas) figCanvas.setCursor(Qt.CrossCursor) ## 必须保留变量cid,否则可能被垃圾回收 self._cid1 = figCanvas.mpl_connect("motion_notify_event", self.do_canvas_mouseMove) self._cid2 = figCanvas.mpl_connect("axes_enter_event", self.do_axes_mouseEnter) self._cid3 = figCanvas.mpl_connect("axes_leave_event", self.do_axes_mouseLeave) self._cid4 = figCanvas.mpl_connect("pick_event", self.do_series_pick) self._cid5 = figCanvas.mpl_connect("scroll_event", self.do_scrollZoom) def __changeActionLanguage(self): actList = self.__naviBar.actions() #关联的Action列表 actList[0].setText("复位") #Home actList[0].setToolTip("复位到原始视图") #Reset original view actList[1].setText("回退") #Back actList[1].setToolTip("回到前一视图") #Back to previous view actList[2].setText("前进") #Forward actList[2].setToolTip("前进到下一视图") #Forward to next view actList[4].setText("平动") #Pan actList[4].setToolTip( "左键平移坐标轴,右键缩放坐标轴") #Pan axes with left mouse, zoom with right actList[5].setText("缩放") #Zoom actList[5].setToolTip("框选矩形框缩放") #Zoom to rectangle actList[6].setText("子图") #Subplots actList[6].setToolTip("设置子图") #Configure subplots actList[7].setText("定制") #Customize actList[7].setToolTip("定制图表参数") #Edit axis, curve and image parameters actList[9].setText("保存") #Save actList[9].setToolTip("保存图表") #Save the figure def __drawScatters(self, N=15): x = range(N) #序列0,1,....N-1 ## x=np.random.rand(N) y = np.random.rand(N) colors = np.random.rand(N) #0~1之间随机数 self.__markerSize = ( 40 * (0.2 + np.random.rand(N)))**2 #0 to 15 point radius self.__axScatter.scatter(x, y, s=self.__markerSize, c=colors, marker='*', alpha=0.5, label="scatter series", picker=True) #允许被拾取pick #s=The marker size in points**2 #c=color, sequence, or sequence of color, optional, default: 'b' ## marker : `~matplotlib.markers.MarkerStyle`, optional, default: 'o' self.__axScatter.set_title("散点图") self.__axScatter.set_xlabel('序号') # X轴标题 def __drawFig1X2(self): #初始化绘图 gs = self.__fig.add_gridspec(1, 2) #1行,2列 ## ax1=self.__fig.add_subplot(1,1,1) #添加一个Axes对象,并返回此对象,不支持constrained_layout ax1 = self.__fig.add_subplot(gs[0, 0], label="Line2D plot") t = np.linspace(0, 10, 40) y1 = np.sin(t) y2 = np.cos(2 * t) ax1.plot(t, y1, 'r-o', label="sin series", linewidth=1, markersize=5, picker=True) #绘制一条曲线 ax1.plot(t, y2, 'b:', label="cos series", linewidth=2) #绘制一条曲线 ax1.set_xlabel('X 轴') ax1.set_ylabel('Y 轴') ax1.set_xlim([0, 10]) ax1.set_ylim([-1.5, 1.5]) ax1.set_title("曲线") ax1.legend() #自动创建Axes的图例 self.__axScatter = self.__fig.add_subplot(gs[0, 1], label="scatter plot") #创建子图 self.__drawScatters(N=15) #绘制散点图 ## ==============event处理函数========================== ## ==========由connectSlotsByName()自动连接的槽函数============ @pyqtSlot() ## 紧凑布局 def on_actTightLayout_triggered(self): self.__fig.tight_layout() # 对所有子图 进行一次tight_layout self.__fig.canvas.draw() @pyqtSlot() ## 设置鼠标光标 def on_actSetCursor_triggered(self): self.__fig.canvas.setCursor(Qt.CrossCursor) @pyqtSlot() ## 重新绘制散点图 def on_actScatterAgain_triggered(self): self.__axScatter.clear() #清除子图 self.__drawScatters(N=15) self.__fig.canvas.draw() #刷新 ## =================自定义槽函数========== #event类型 matplotlib.backend_bases.MouseEvent def do_canvas_mouseMove(self, event): if event.inaxes == None: return info = "%s: xdata=%.2f,ydata=%.2f " % (event.inaxes.get_label(), event.xdata, event.ydata) self.__labMove.setText(info) ## event类型:matplotlib.backend_bases.LocationEvent def do_axes_mouseEnter(self, event): event.inaxes.patch.set_facecolor('g') #设置背景颜色 event.inaxes.patch.set_alpha(0.2) #透明度 event.canvas.draw() def do_axes_mouseLeave(self, event): event.inaxes.patch.set_facecolor('w') #设置背景颜色 event.canvas.draw() ##event 类型: matplotlib.backend_bases.PickEvent def do_series_pick(self, event): series = event.artist # 产生事件的对象 index = event.ind[0] #索引号,是array([int32])类型,可能有多个对象被pick,只取第1个 #是否有ind属性与具体的对象有关 if isinstance(series, mpl.collections.PathCollection): #scatter()生成的序列 markerSize = self.__markerSize[index] info = "%s: index=%d, marker size=%d " % ( event.mouseevent.inaxes.get_label(), index, markerSize) elif isinstance(series, mpl.lines.Line2D): #plot()生成的序列 ## xdata=series.get_xdata() #两种方法都可以 ## x=xdata[index] ## ydata=series.get_ydata() ## y=ydata[index] x = event.mouseevent.xdata #标量数据点 y = event.mouseevent.ydata #标量数据点 info = "%s: index=%d, data_xy=(%.2f, %.2f) " % (series.get_label(), index, x, y) self.__labPick.setText(info) #event类型 matplotlib.backend_bases.MouseEvent def do_scrollZoom(self, event): #通过鼠标滚轮缩放 ax = event.inaxes # 产生事件的axes对象 if ax == None: return self.__naviBar.push_current( ) #Push the current view limits and position onto the stack,这样才可以还原 xmin, xmax = ax.get_xbound() #获取范围 xlen = xmax - xmin ymin, ymax = ax.get_ybound() #获取范围 ylen = ymax - ymin xchg = event.step * xlen / 20 #step [scalar],positive = ’up’, negative ='down' xmin = xmin + xchg xmax = xmax - xchg ychg = event.step * ylen / 20 ymin = ymin + ychg ymax = ymax - ychg ax.set_xbound(xmin, xmax) ax.set_ybound(ymin, ymax) event.canvas.draw()