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(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)
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)
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 _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)
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()
def __init__(self, title, shape=None, *args): super().__init__(*args) self.setWindowTitle(title) self.setWindowFlag(Qt.Window, True) self.setContentsMargins(0, 0, 0, 0) self._descriptor = '' self.setFocusPolicy(Qt.StrongFocus) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.title = title if shape is None: self.fig, self.axis = plt.subplots() self.axis.set_title(self.title) else: rows, columns = shape self.fig, self.axis = plt.subplots(rows, columns) self.fig.suptitle(title) self.fig.set_dpi(100) self.figure_widget = FigureCanvas(self.fig) self.figure_widget.setMinimumHeight(500) toolbar = NavigationToolbar(self.figure_widget, self) toolbar.addAction('Edit', self.show_editor) layout.addWidget(self.figure_widget) layout.addWidget(toolbar) self._command_box = None self._run_command_btn = None self._highlighter = None self.setLayout(layout)
def initUI(self): self.setWindowTitle('NyqLab: Scope') # Figure self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) toolbar = NavigationToolbar(self.canvas, self) # Eye diagram toolbar button action_eye = QtWidgets.QAction(QtGui.QIcon('media/eye'), 'Scope', self) action_eye.triggered.connect(self.onEyeClick) toolbar.addAction(action_eye) # Axis self.ax_t = self.figure.add_subplot(2, 1, 1) self.ax_t.grid(True, which='major', linestyle='--') self.ax_t.set_xlabel('$t / T_\mathrm{b}$') self.ax_t.axis(self.ax_t_lim_free) self.ax_t.xaxis.set_major_locator(ticker.MultipleLocator(1.0)) self.ax_t.yaxis.set_major_locator(ticker.MultipleLocator(0.5)) self.ax_f = self.figure.add_subplot(2, 1, 2) self.ax_f.axis(self.ax_f_lim) self.ax_f.grid(True, which='major', linestyle='--') self.ax_f.set_xlabel('$f / R_\mathrm{b}$') self.ax_f.xaxis.set_major_locator(ticker.MultipleLocator(0.5)) self.ax_f.yaxis.set_major_locator(ticker.MultipleLocator(10.0)) layout = QtWidgets.QVBoxLayout() layout.addWidget(toolbar) layout.addWidget(self.canvas) widget = QtWidgets.QWidget(self) self.setCentralWidget(widget) widget.setLayout(layout) self.resize(800, 500)
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 Plot(object): def __init__(self, figure, identifier, filepath): loader = UiLoader() self.ui = loader.load('plot_window.ui', PlotWindow()) # Tell Windows how to handle our windows in the the taskbar, making pinning work properly and stuff: if os.name == 'nt': self.ui.newWindow.connect(set_win_appusermodel) self.set_window_title(identifier, filepath) # figure.tight_layout() self.figure = figure self.canvas = FigureCanvas(figure) self.navigation_toolbar = NavigationToolbar(self.canvas, self.ui) self.lock_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/lock-unlock'), 'Lock axes', self.on_lock_axes_triggered) self.lock_action.setCheckable(True) self.lock_action.setToolTip('Lock axes') self.copy_to_clipboard_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/clipboard--arrow'), 'Copy to clipboard', self.on_copy_to_clipboard_triggered) self.copy_to_clipboard_action.setToolTip('Copy to clipboard') self.copy_to_clipboard_action.setShortcut(QtGui.QKeySequence.Copy) self.ui.verticalLayout_canvas.addWidget(self.canvas) self.ui.verticalLayout_navigation_toolbar.addWidget( self.navigation_toolbar) self.lock_axes = False self.axis_limits = None self.update_window_size() self.ui.show() def on_lock_axes_triggered(self): if self.lock_action.isChecked(): self.lock_axes = True self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock')) else: self.lock_axes = False self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock-unlock')) def on_copy_to_clipboard_triggered(self): lyse.figure_to_clipboard(self.figure) @inmain_decorator() def save_axis_limits(self): axis_limits = {} for i, ax in enumerate(self.figure.axes): # Save the limits of the axes to restore them afterward: axis_limits[i] = ax.get_xlim(), ax.get_ylim() self.axis_limits = axis_limits @inmain_decorator() def clear(self): self.figure.clear() @inmain_decorator() def restore_axis_limits(self): for i, ax in enumerate(self.figure.axes): try: xlim, ylim = self.axis_limits[i] ax.set_xlim(xlim) ax.set_ylim(ylim) except KeyError: continue @inmain_decorator() def set_window_title(self, identifier, filepath): self.ui.setWindowTitle( str(identifier) + ' - ' + os.path.basename(filepath)) @inmain_decorator() def update_window_size(self): l, w = self.figure.get_size_inches() dpi = self.figure.get_dpi() self.canvas.resize(int(l * dpi), int(w * dpi)) self.ui.adjustSize() @inmain_decorator() def draw(self): self.canvas.draw() def show(self): self.ui.show() @property def is_shown(self): return self.ui.isVisible()
class Plot(object): def __init__(self, figure, identifier, filepath): self.identifier = identifier loader = UiLoader() self.ui = loader.load(os.path.join(LYSE_DIR, 'plot_window.ui'), PlotWindow()) # Tell Windows how to handle our windows in the the taskbar, making pinning work properly and stuff: if os.name == 'nt': self.ui.newWindow.connect(set_win_appusermodel) self.set_window_title(identifier, filepath) # figure.tight_layout() self.figure = figure self.canvas = figure.canvas self.navigation_toolbar = NavigationToolbar(self.canvas, self.ui) self.lock_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/lock-unlock'), 'Lock axes', self.on_lock_axes_triggered) self.lock_action.setCheckable(True) self.lock_action.setToolTip('Lock axes') self.copy_to_clipboard_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/clipboard--arrow'), 'Copy to clipboard', self.on_copy_to_clipboard_triggered) self.copy_to_clipboard_action.setToolTip('Copy to clipboard') self.copy_to_clipboard_action.setShortcut(QtGui.QKeySequence.Copy) self.ui.verticalLayout_canvas.addWidget(self.canvas) self.ui.verticalLayout_navigation_toolbar.addWidget( self.navigation_toolbar) self.lock_axes = False self.axis_limits = None self.update_window_size() self.ui.show() def on_lock_axes_triggered(self): if self.lock_action.isChecked(): self.lock_axes = True self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock')) else: self.lock_axes = False self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock-unlock')) def on_copy_to_clipboard_triggered(self): lyse.figure_to_clipboard(self.figure) @inmain_decorator() def save_axis_limits(self): axis_limits = {} for i, ax in enumerate(self.figure.axes): # Save the limits of the axes to restore them afterward: axis_limits[i] = ax.get_xlim(), ax.get_ylim() self.axis_limits = axis_limits @inmain_decorator() def clear(self): self.figure.clear() @inmain_decorator() def restore_axis_limits(self): for i, ax in enumerate(self.figure.axes): try: xlim, ylim = self.axis_limits[i] ax.set_xlim(xlim) ax.set_ylim(ylim) except KeyError: continue @inmain_decorator() def set_window_title(self, identifier, filepath): self.ui.setWindowTitle( str(identifier) + ' - ' + os.path.basename(filepath)) @inmain_decorator() def update_window_size(self): l, w = self.figure.get_size_inches() dpi = self.figure.get_dpi() self.canvas.resize(int(l * dpi), int(w * dpi)) self.ui.adjustSize() @inmain_decorator() def draw(self): self.canvas.draw() def show(self): self.ui.show() @property def is_shown(self): return self.ui.isVisible() def analysis_complete(self, figure_in_use): """To be overriden by subclasses. Called as part of the post analysis plot actions""" pass def get_window_state(self): """Called when the Plot window is about to be closed due to a change in registered Plot window class Can be overridden by subclasses if custom information should be saved (although bear in mind that you will passing the information from the previous Plot subclass which might not be what you want unless the old and new classes have a common ancestor, or the change in Plot class is triggered by a reload of the module containing your Plot subclass). Returns a dictionary of information on the window state. If you have overridden this method, please call the base method first and then update the returned dictionary with your additional information before returning it from your method. """ return { 'window_geometry': self.ui.saveGeometry(), 'axis_lock_state': self.lock_axes, 'axis_limits': self.axis_limits, } def restore_window_state(self, state): """Called when the Plot window is recreated due to a change in registered Plot window class. Can be overridden by subclasses if custom information should be restored (although bear in mind that you will get the information from the previous Plot subclass which might not be what you want unless the old and new classes have a common ancestor, or the change in Plot class is triggered by a reload of the module containing your Plot subclass). If overriding, please call the parent method in addition to your new code Arguments: state: A dictionary of information to restore """ geometry = state.get('window_geometry', None) if geometry is not None: self.ui.restoreGeometry(geometry) axis_limits = state.get('axis_limits', None) axis_lock_state = state.get('axis_lock_state', None) if axis_lock_state is not None: if axis_lock_state: # assumes the default state is False for new windows self.lock_action.trigger() if axis_limits is not None: self.axis_limits = axis_limits self.restore_axis_limits() def on_close(self): """Called when the window is closed. Note that this only happens if the Plot window class has changed. Clicking the "X" button in the window title bar has been overridden to hide the window instead of closing it.""" # release selected toolbar action as selecting an action acquires a lock # that is associated with the figure canvas (which is reused in the new # plot window) and this must be released before closing the window or else # it is held forever self.navigation_toolbar.pan() self.navigation_toolbar.zoom() self.navigation_toolbar.pan() self.navigation_toolbar.pan()
class Mpltab(QtWidgets.QWidget): """ Widget to keep track of mpl figure in tab""" def __init__(self, MplCanvas, plotId, tabId, mainWindow, parent=None, toolbar=True, docked=True, dockingIcon=None, unDockingIcon=None): super().__init__(parent=parent) # Save both plotId and tabId self.plotId = plotId self.tabId = tabId # If docked, as standard the Mpltab is created as docked in a DetachableTabWidget self.docked = docked self.mainWindow = mainWindow self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) # Remove border around layout self.dockingIcon = dockingIcon self.unDockingIcon = unDockingIcon if toolbar: # If a toolbar is wanted, create it, otherwise create menubar above figure self.menubar = NavigationToolbar(MplCanvas, None) else: self.menubar = QtWidgets.QMenuBar(self) self.menubar.setFixedHeight(25) # ad menubar and canvas to layout self.layout.addWidget(self.menubar) self.layout.addWidget(MplCanvas) if not self.dockingIcon is None and not self.unDockingIcon is None: self.dockAction = QtWidgets.QAction(self.unDockingIcon, 'Undock', self.menubar) else: self.dockAction = QtWidgets.QAction('Undock', self.menubar) if not self.docked: # start undocked self.undock(parent=self.parent) self.setLayout(self.layout) # Add shortcut to dock self.dockAction.setShortcut("Ctrl+D") self.menubar.addAction(self.dockAction) self.dockAction.triggered.connect(self.toggleDocked) def toggleDocked(self): if self.docked: self.parent().parent().detachTab(index=self.tabId, point=QtGui.QCursor().pos()) else: parent = self.parent() parent.dock( ) #attachTab(parent.contentWidget, parent.objectName(), parent.windowIcon()) def undock(self, newParent): self.docked = False self.dockAction.setText('Dock') if not self.dockingIcon is None and not self.unDockingIcon is None: self.dockAction.setIcon(self.dockingIcon) self.setParent(newParent) def dock(self, newParent): self.docked = True self.dockAction.setText('Undock') if not self.dockingIcon is None and not self.unDockingIcon is None: self.dockAction.setIcon(self.unDockingIcon) self.setParent(newParent)
class CameraGui(QDialog): def set_vars(self): self.camera = 'vertical_mot' self.name = self.camera self.fluorescence_mode = True self.update_id = np.random.randint(0, 2**31 - 1) self.ROI = None self.no_lim = True def __init__(self): super(CameraGui, self).__init__(None) self.set_vars() self.connect_to_labrad() self.populate() self.Plotter = LivePlotter(self) def populate(self): self.setWindowTitle(self.name) self.canvas = MplCanvas() self.nav = NavigationToolbar(self.canvas, self) #select_cam = self.nav.addAction('&Select camera') #select_cam.addAction('horizontal mot') #select_cam.addAction('vertical mot (cavity)') self.nav.addAction('Launch live plotter', self.launch_plotter) self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.nav) self.layout.addWidget(self.canvas) self.setLayout(self.layout) width = self.canvas.width() height = self.nav.height() + self.canvas.height() self.setFixedSize(width, height) #Labrad connection: @inlineCallbacks def connect_to_labrad(self): #self.cxn = connect(name=self.name) self.cxn = connection() yield self.cxn.connect(name='camera viewer') server = yield self.cxn.get_server('camera') yield server.signal__update(self.update_id) yield server.addListener(listener=self.receive_update, source=None, ID=self.update_id) print('connected') def receive_update(self, c, update_json): update = json.loads(update_json) for key, value in update.items(): if key == self.camera: if self.fluorescence_mode: if not (('exc' in value[0]) or ('background' in value[0])): print(value) if 'gnd' in value[0]: print(value[0]) str_end = '_fluorescence.png' keyword = 'mot_cavity_' split_str = value[0].partition(str_end) parse_name = split_str[0].partition(keyword) print(parse_name) beginning = parse_name[0] shot_num = int(parse_name[-1]) offset = 3 mod_shot = shot_num - offset new_path = beginning + keyword + str( mod_shot) + str_end print(new_path) self.file_to_show = new_path #value[0] else: self.file_to_show = value[0] print(self.file_to_show) time.sleep(.1) self.Plotter.show_window() self.show_window() def show_window(self): try: if not self.no_lim: xlim = self.canvas.ax.get_xlim() ylim = self.canvas.ax.get_ylim() #self.canvas.ax.clear() #MOVED to after attempt to load fig it.fig_gui_window_ROI(self.file_to_show, self.canvas.ax, self.ROI) if not self.no_lim: self.canvas.ax.set_xlim(xlim) self.canvas.ax.set_ylim(ylim) else: self.no_lim = False print(self.canvas.ax.get_xlim()) self.canvas.ax.set_title("{:.3e}".format(self.Plotter.title), color='w', y=.85, size=42) self.canvas.draw() print('redrawn') except: #self.no_lim = True print('Error loading file: not refreshed') def launch_plotter(self): self.Plotter.show()
class LivePlotter(QDialog): def set_class_vars(self): self.script = it.fig_plotter self.n_show = 30 self.live_data = np.full(self.n_show, None) def __init__(self, parent): super(LivePlotter, self).__init__() self.parent = parent self.set_class_vars() self.populate() def populate(self): self.setWindowTitle('Live plotter') self.canvas = MplCanvas() self.nav = NavigationToolbar(self.canvas, self) '''Changed nav toolbar''' self.nav.addAction('Reset optimizer', self.reset_opt) self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.nav) self.layout.addWidget(self.canvas) self.setLayout(self.layout) #self.canvas.ax.set_ylim((0, 5e-5)) #self.canvas.ax.set_xlim((0, .04)) width = self.canvas.width() height = self.nav.height() + self.canvas.height() + 20 self.setFixedSize(width, height) def reset_opt(self): self.live_data = np.full(self.n_show, None) def live_plot(self): #try: roi = self.get_ROI() this_shot = self.script(self.parent.file_to_show, roi) self.title = this_shot empty_data = np.where(self.live_data == None) if len(empty_data[0]) == 0: self.live_data[0:self.n_show - 1] = self.live_data[1:self.n_show] self.live_data[-1] = this_shot else: self.live_data[empty_data[0][0]] = this_shot #except AttributeError: # print('Not loaded') def get_ROI(self): xlim = self.parent.canvas.ax.get_xlim() ylim = self.parent.canvas.ax.get_ylim() ROI_exact = [xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]] return [int(x) for x in ROI_exact] def show_window(self): self.live_plot() self.canvas.ax.clear() self.canvas.ax.plot(self.live_data, 'o') #self.canvas.ax.title(np.std(self.live_data)) self.canvas.draw()
class GraphWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.x_label = "Eje X" self.y_label = "Eje Y" self.title = " " self.figure = Figure() self.canvas = FigureCanvas(self.figure) # Canvas a agregar al widget self.toolbar = NavigationToolbar( self.canvas, self) # cada gráfico tiene un toolbar con herramientos para # trabajar sobre él vertical_layout = QVBoxLayout() # Se le agregan nuevos botones al toolbar, además de los que vienen por defecto y se les asigna una función para # cuando son clickeados. self.toolbar.addAction(QIcon("Resources\saveall.png"), "Guardar ambos gráficos", self.save_all_pressed) self.toolbar.addAction(QIcon("Resources\mark.png"), "Marcar puntos", self.mark_points) self.toolbar.addAction(QIcon("Resources\log_lin_approx.jpg"), "Cambiar escala", self.change_scale) self.toolbar.addAction(QIcon("Resources\scatter_or_lineal.png"), "Lineal o Dispersión", self.change_format) self.cid = self.figure.canvas.mpl_connect( 'button_press_event', self) # Evento de cuando se aprieta un botón vertical_layout.addWidget( self.canvas) # Se le agrega el canvas al widget vertical_layout.addWidget( self.toolbar) # Se le agrega el toolbar al widget # Arreglos de los puntos marcados por el usuario. self.x_marked_points = [] self.y_marked_points = [] # self.canvas.axes = self.canvas.figure.add_subplot( 111) # Plotea el canvas. Si no se entiende que es el ploteo, # mirar # https://stackoverflow.com/questions/3584805/in-matplotlib-what-does-the-argument-mean-in-fig-add-subplot111 self.setLayout(vertical_layout) # callbacks de los nuevos botones del toolbar. En None por defecto self.save_all_callback = None self.redraw_callback = None self.mark_points_flag = False # Flag que indica si los puntos se deben mostrar o estar escondidos. self.log_flag = True # Flag que indica si el grafico se encuentra en escala logaritmica. self.continuous_line_flag = False # Flag que indica si el grafico de medicion se muestra como una curva continua o de # dispersion def __call__(self, event): # Se llama con un evento de click en el widget. if self.mark_points_flag: # Si el flag de mostrar puntos está activado se agregarán las coordenadas del # click a un nuevo punto self.x_marked_points.append(event.xdata) self.y_marked_points.append(event.ydata) self.redraw_callback() def save_all_pressed( self ): # Funcion que se llama el tocar en el toolbar el boton de guardar todos los graficos. self.save_all_callback() # Callback que exporte todos los graficos. def mark_points( self ): # Funcion que se llama el tocar en el toolbar el boton de marcar puntos. self.mark_points_flag = not self.mark_points_flag # Función que togglea el flag de marcar puntos. def clear_marked_points(self): # Se limpian los puntos marcados self.x_marked_points = [] self.y_marked_points = [] def change_scale(self): self.log_flag = not self.log_flag self.redraw_callback() def change_format(self): self.continuous_line_flag = not self.continuous_line_flag self.redraw_callback()
class CavityClockGui(QDialog): def __init__(self): super(CavityClockGui, self).__init__(None) self.update_id = np.random.randint(0, 2**31 - 1) self.expt = "Waiting for updates" self.data_path = None self.update = None #Specify analysis frameworks self.analysis_script = fits.do_gaussian_fit self.mode = lambda update, preset: None self.connect_to_labrad_cav() self.connect_to_labrad_clock() self.populate() #self.Plotter = LivePlotter(self) def populate(self): self.setWindowTitle("Clock + cavity gui") self.canvas = MplCanvas() self.nav = NavigationToolbar(self.canvas, self) #self.nav.addAction('Select analysis method') #self.nav.addAction('Launch live plotter', self.launch_plotter) self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.nav) self.layout.addWidget(self.canvas) self.setLayout(self.layout) width = self.canvas.width() height = self.nav.height() + self.canvas.height() self.setFixedSize(width, height) self.add_subplot_buttons() # Labrad connection: @inlineCallbacks def connect_to_labrad_cav(self): #self.cxn = connect(name=self.name) self.cxn = connection() yield self.cxn.connect(name='cavity viewer') server = yield self.cxn.get_server('cavity_probe_pico') yield server.signal__update(self.update_id) yield server.addListener(listener=self.receive_update, source=None, ID=self.update_id) print('connected to cavity probe pico server') @inlineCallbacks def connect_to_labrad_clock(self): #self.cxn = connect(name=self.name) self.cxn = connection() yield self.cxn.connect(name='clock viewer') server = yield self.cxn.get_server('clock_pico') yield server.signal__update(self.update_id) yield server.addListener(listener=self.receive_update, source=None, ID=self.update_id) print('connected to clock pico server') def preserve_lim(self): all_ax = self.canvas.trace_axes + self.canvas.data_axes lims = np.zeros((len(all_ax), 4)) for i in np.arange(len(all_ax)): lims[i, 0:2] = all_ax[i].get_xlim() lims[i, 2:4] = all_ax[i].get_ylim() return lims def enforce_lim(self, lims, preset): all_ax = self.canvas.trace_axes + self.canvas.data_axes for i in np.arange(len(all_ax)): if preset[i]: all_ax[i].set_xlim(lims[i, 0:2]) current_y = all_ax[i].get_ylim() if current_y[1] > lims[i, 3]: lims[i, 3] = current_y[1] if current_y[0] < lims[i, 2]: lims[i, 2] = current_y[0] all_ax[i].set_ylim(lims[i, 2:4]) def save_fig(self, ax, fig, title): #https://stackoverflow.com/questions/4325733/save-a-subplot-in-matplotlib extent = ax.get_window_extent().transformed( fig.dpi_scale_trans.inverted()) extent_expanded = extent.expanded(1.5, 2) fig.savefig(title, bbox_inches=extent_expanded) def receive_update(self, c, update_json): update = json.loads(update_json) self.update = update this_expt, this_path = listeners.get_expt(update) if this_expt is not None and self.expt != this_expt: if (not self.expt.isnumeric()) and (self.data_path is not None): #Save data traces when expt ends folder_path = os.path.join(self.data_path, self.expt) np.save(os.path.join(folder_path, "processed_data_x"), self.canvas.data_x) np.save(os.path.join(folder_path, "processed_data_y"), self.canvas.data_y) self.save_fig(self.canvas.data_axes[0], self.canvas.fig, os.path.join(folder_path, 'fig_0.png')) self.save_fig(self.canvas.data_axes[1], self.canvas.fig, os.path.join(folder_path, 'fig_1.png')) print('Saved data in folder: ' + folder_path) if this_expt.isnumeric(): self.canvas.fig.suptitle(self.expt + " ended") self.expt = this_expt else: print(this_expt) self.canvas.reset_data() self.expt = this_expt self.data_path = this_path self.canvas.fig.suptitle(self.expt) self.canvas.lim_set = self.canvas.lim_default #Get current lims to prevent re-scaling lims = self.preserve_lim() preset = self.canvas.lim_set.copy() #!!Specify listeners for diff axes: #Comment/uncomment next lines to turn off/on pmt listeners: self.mode(update, preset) listeners.atom_number(update, self.canvas.data_axes[1], self.canvas.data_x[1], self.canvas.data_y[1], bad_points=self.canvas.bad_data, freq_domain=False) #Cavity single-tone traces: listeners.bare_cavity_single_tone(update, self.canvas.trace_axes[1], 'gnd') listeners.bare_cavity_single_tone(update, self.canvas.trace_axes[2], 'exc') #Cavity 2-tone probing clock operation: ''' try: returned, vrs_gnd = listeners.cavity_probe_two_tone(update, self.canvas.trace_axes[1]) self.canvas.lim_set[1] = returned or preset[1] print('fit one') returned, vrs_exc = listeners.cavity_probe_two_tone(update, self.canvas.trace_axes[2], 'exc') self.canvas.lim_set[2] = returned or preset[2] listeners.exc_frac_cavity(update, self.canvas.data_axes[1], self.canvas.data_x[1], self.canvas.data_y[1], vrs_gnd, vrs_exc, 'sequencer.clock_phase') except: print('cannot extract tones') ''' #Add back past lims to prevent rescaling self.enforce_lim(lims, preset) self.canvas.draw() #Different potential plotter configs def set_freq(self): def freq_update(update, preset): self.canvas.lim_set[0] = listeners.pmt_trace( update, self.canvas.trace_axes[0]) or preset[0] exc_called = listeners.exc_frac(update, self.canvas.data_axes[0], self.canvas.data_x[0], self.canvas.data_y[0]) return freq_update def set_time(self, time_name): def time_update(update, preset): self.canvas.lim_set[0] = listeners.pmt_trace( update, self.canvas.trace_axes[0]) or preset[0] exc_called = listeners.exc_frac(update, self.canvas.data_axes[0], self.canvas.data_x[0], self.canvas.data_y[0], time_domain=True, time_name=time_name) return time_update def set_phase(self): def phase_update(update, preset): self.canvas.lim_set[0] = listeners.pmt_trace( update, self.canvas.trace_axes[0]) or preset[0] exc_called = listeners.exc_frac(update, self.canvas.data_axes[0], self.canvas.data_x[0], self.canvas.data_y[0], time_domain=True, time_name='sequencer.clock_phase') return phase_update def set_shot(self): def shot_update(update, preset): bad_shot = self.canvas.bad_data[-1] self.canvas.lim_set[0] = listeners.pmt_trace( update, self.canvas.trace_axes[0]) or preset[0] exc_called = listeners.exc_frac(update, self.canvas.data_axes[0], self.canvas.data_x[0], self.canvas.data_y[0], freq_domain=False, n_avg=1, bad_shot=bad_shot) return shot_update #Add buttons to select config, fit methods def add_subplot_buttons(self): self.nav.addAction('Fit', self.do_fit) #Add fft client option to left cavity trace self.fft_left = QPushButton(self) self.fft_left.setText('FFT on click') self.fft_left.move(500, 380) self.fft_left.clicked.connect(self.show_fft) #Add dropdown for setting data x axis self.dropdown = QComboBox(self) self.labels = [ "Frequency", "Phase", "Dark time", "Pi time", "Shot num" ] self.fxns = [ self.set_freq(), self.set_phase(), self.set_time('sequencer.t_dark'), self.set_time('sequencer.t_pi'), self.set_shot() ] self.dropdown.addItems(self.labels) self.dropdown.currentIndexChanged.connect(self.select_mode) self.dropdown.move(870, 4) self.mode = self.fxns[0] #Add dropdown for setting analysis fxn self.fxn_drop = QComboBox(self) self.fit_labels = [ "Gaussian", "Inv. Gauss", "Phase fringe", "Local max", "Local min" ] self.fit_fxns = [ fits.do_gaussian_fit, fits.do_inverted_gaussian_fit, fits.do_phase_fit, fits.do_local_max, fits.do_local_min ] self.fxn_drop.addItems(self.fit_labels) self.analysis_script = self.fit_fxns[0] self.fxn_drop.move(1000, 4) self.fxn_drop.currentIndexChanged.connect(self.select_script) def select_mode(self): txt = self.dropdown.currentText() ix = self.dropdown.currentIndex() self.mode = self.fxns[ix] self.canvas.reset_data() print(txt) def select_script(self): ix = self.fxn_drop.currentIndex() self.analysis_script = self.fit_fxns[ix] def do_fit(self): self.analysis_script(self.canvas.data_axes[0], self.canvas.data_x[0], self.canvas.data_y[0]) def show_fft(self): self.mouse_listener = self.canvas.mpl_connect('button_press_event', self.process_click) def process_click(self, event): t_click = event.xdata self.canvas.mpl_disconnect(self.mouse_listener) self.FFTPlot = FFTPlotter(self.update, t_click) self.FFTPlot.show() def show_fft(self): self.mouse_listener = self.canvas.mpl_connect('button_press_event', self.process_click)
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()
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 MplWidget(QtWidgets.QWidget): """ Widget defined in Qt Designer. """ zoom_to_full_view = pyqtSignal() map_press = pyqtSignal(float, float) def __init__(self, parent = None): # initialization of Qt MainWindow widget QtWidgets.QWidget.__init__(self, parent) # set the canvas to the Matplotlib widget self.canvas = MplCanvas() self.ntb = NavigationToolbar(self.canvas, self) #self.ntb.removeAction(self.ntb.buttons[0]) self.ntb.clear() program_folder = os.path.join(os.path.dirname(__file__), "ims") a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "world.png")), 'Home', self.zoom2fullextent) a.setToolTip('Reset original view') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_left.png")), 'Back', self.ntb.back) a.setToolTip('Back to previous view') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_right.png")), 'Forward', self.ntb.forward) a.setToolTip('Forward to next view') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_out.png")), 'Pan', self.ntb.pan) a.setToolTip('Pan axes with left mouse, zoom with right') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "zoom.png")), 'Zoom', self.ntb.zoom) a.setToolTip('Zoom to rectangle') action_SetSrcPt = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "bullet_red.png")), 'Source point', self.pt_map) action_SetSrcPt.setToolTip('Set source point in map') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "camera.png")), 'Save', self.ntb.save_figure) a.setToolTip('Save map as image') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "help.png")), 'Help', self.openHelp) a.setToolTip('Help') a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "information.png")), 'About', self.helpAbout) a.setToolTip('About') # create a vertical box layout self.vbl = QtWidgets.QVBoxLayout() # add widgets to the vertical box self.vbl.addWidget(self.canvas) self.vbl.addWidget(self.ntb) # set the layout to the vertical box self.setLayout(self.vbl) def onclick(self, event): """ Emit a signal to induce the update of source point location. @param event: press event. @type event: Matplotlib event. """ global set_srcpt_count, cid set_srcpt_count += 1 if set_srcpt_count == 1: self.map_press.emit(event.xdata, event.ydata) self.canvas.fig.canvas.mpl_disconnect(cid) def pt_map(self): """ Connect the press event with the function for updating the source point location. """ global set_srcpt_count, cid set_srcpt_count = 0 cid = self.canvas.fig.canvas.mpl_connect('button_press_event', self.onclick) def zoom2fullextent(self): """ Emit the signal for updating the map view to the extent of the DEM, in alternative of the shapefile, or at the standard extent. """ self.zoom_to_full_view.emit() def openHelp(self): """ Open an Help HTML file after CADTOOLS module in QG """ help_path = os.path.join(os.path.dirname(__file__), 'help', 'help.html') webbrowser.open(help_path) def helpAbout(self): """ Visualize an About window. """ QtWidgets.QMessageBox.about(self, "About gSurf", """ <p>gSurf<br />License: GPL v. 3</p> <p>This application calculates the intersection between a plane and a DEM in an interactive way. The result is a set of points/lines that can be saved as shapefiles. </p> <p>Report any bug to <a href="mailto:[email protected]">[email protected]</a></p> """)
class MyApp(QMainWindow, Ui_MainWindow): def __init__(self): QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.setupUi(self) self.setWindowTitle('GeoProcessing') self.dirSelect_main.clicked.connect(self.select_output) self.process_main.clicked.connect(self.process) self.exportGeoTiff_main.clicked.connect(self.exportGeo) self.actionImport_main.triggered.connect(self.importDialog) self.actionSettings.triggered.connect(self.settingsDialog) self.actionAssemble_main.triggered.connect(self.assembleDialog) self.getStats_main.triggered.connect(self.makeStatistics) self.exportGeoTiff_main.setEnabled(False) self.loadParam() #Create the toolbar actionRotateR = QAction(QIcon(os.path.join(os.path.dirname(__file__), 'ressources/rotateR.png')), 'Rotate clockwise', self) actionRotateL = QAction(QIcon(os.path.join(os.path.dirname(__file__), 'ressources/rotateL.png')), 'Rotate counter clockwise', self) actionFlipUD = QAction(QIcon(os.path.join(os.path.dirname(__file__), 'ressources/flipud.png')), 'Flip vertical', self) actionFlipLR = QAction(QIcon(os.path.join(os.path.dirname(__file__), 'ressources/fliplr.png')), 'Flip horiziontal', self) #Icon from https://www.iconfinder.com/icons/281321/adjust_align_control_editor_manage_move_operate_rotate_icon#size=128 actionRotateR.triggered.connect(self.rotateR) actionRotateL.triggered.connect(self.rotateL) actionFlipUD.triggered.connect(self.flipud) actionFlipLR.triggered.connect(self.fliplr) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) # set button context menu policy self.canvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.canvas.customContextMenuRequested.connect(self.on_context_menu) # create context menu self.popMenu = QMenu(self) self.actionOpenEditor=QAction('Open grid in editor', self) self.actionEditPoint=QAction('Edit point', self) self.popMenu.addAction(self.actionOpenEditor) self.popMenu.addAction(self.actionEditPoint) self.actionOpenEditor.triggered.connect(self.openEditor) self.actionEditPoint.triggered.connect(self.editPoint) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.addAction(actionRotateL) self.toolbar.addAction(actionRotateR) self.toolbar.addAction(actionFlipUD) self.toolbar.addAction(actionFlipLR) self.canvasLayout.addWidget(self.toolbar) self.canvasLayout.addWidget(self.canvas) self.sizeTiles_lbl.setText("x:%s y:%s" %(self.tileSize[0], self.tileSize[0])) self.epsg_txt.setText("EPSG: %s" %(self.epsg)) self.stdClip_val.setText(str(self.stdClip_slider.value()/float(100))) self.stdClip_slider.valueChanged.connect(self.changeClipValue) self.stdClip_check.toggled.connect(self.clipChecked) self.survey=Survey(self.foldername, self.siteName) self.statusBar().showMessage('Ready') self.gridSize=(20,20) def clipChecked(self): if self.stdClip_check.isChecked(): self.stdClip_slider.setEnabled(True) self.stdClip_val.setEnabled(True) #Update view else: self.stdClip_slider.setEnabled(False) self.stdClip_val.setEnabled(False) def changeClipValue(self): self.stdClip_val.setText(str(self.stdClip_slider.value()/float(100))) def addInfo(self,l): self.infotxt+=l self.infoTxt_main.setText(self.infotxt) app.processEvents() def clearInfo(self): self.infotxt="" self.infoTxt_main.setText(self.infotxt) app.processEvents() def select_output(self): foldername = QFileDialog.getExistingDirectory(self, "Select output directory ",self.foldername) self.foldername=foldername #Here check that the directory is valid self.pathRaw=foldername +"/raw/" self.pathOutput=foldername +"/output/" infotxt="" if (os.path.isdir(self.pathRaw)==0): infotxt+="Raw folder not found\n" if (os.path.isdir(self.pathOutput)==0): infotxt+="Output folder not found\n" if (infotxt!=""): QMessageBox.about(self, "Invalid directory", infotxt) self.actionImport_main.setEnabled(False) self.actionAssemble_main.setEnabled(False) self.dir_main.setText("") else: self.dir_main.setText(foldername) self.actionImport_main.setEnabled(True) self.actionAssemble_main.setEnabled(True) self.siteName=foldername.split("/")[-1] self.siteName_main.setText(self.siteName) self.saveParam() def scanLastGridId(self): listCsv=os.listdir(self.pathRaw) last=0 for i in range (0,len(listCsv)): str=listCsv[i].split(".") if (str[1]=="csv"): a=str[0].split("_") gridId=a[1] if ("v" not in gridId) and ("n" not in gridId): if (int(gridId)>last): last=int(gridId)+1 return last def importDialog(self): self.dlgImport = ImportDialog() self.siteName=self.siteName_main.text() self.dlgImport.exportDir.setText(str(self.siteName)) self.init=self.scanLastGridId() self.dlgImport.firstGrid.setValue(self.init) self.dlgImport.go.clicked.connect(self.downloadRM85) self.dlgImport.show() def settingsDialog(self): self.dlgSettings = SettingsDialog() index = self.dlgSettings.epsg.findText(str(self.epsg), QtCore.Qt.MatchFixedString) if index >= 0: self.dlgSettings.epsg.setCurrentIndex(index) # self.dlgSettings.epsg.value(self.epsg) self.dlgSettings.go.clicked.connect(self.saveSettingsChanges) self.dlgSettings.show() def saveSettingsChanges(self): self.epsg=int(self.dlgSettings.epsg.currentText()) self.saveParam() self.epsg_txt.setText("EPSG: %s" %(self.epsg)) del self.dlgSettings def assembleDialog(self): self.siteName=str(self.siteName_main.text()) print (self.siteName) self.geoFile="0\t0\t0\t0\t0\n0\t0\t0\t0\t0\n0\t0\t0\t0\t0\n0\t0\t0\t0\t0\n" if os.path.isfile(self.foldername+"/output/geometry.txt"): f=open(self.foldername+"/output/geometry.txt", "r+") self.geoFile=f.read() f.close() else: print ("The geometry file was not found. A new one was initiated.") f=open(self.foldername+"/output/geometry.txt", "w+") f.write(self.geoFile) f.close() self.dlgAss = Assemble() self.dlgAss.buttonBox.accepted.connect(self.assembleSave) self.dlgAss.text.setLineWrapMode(0) self.dlgAss.text.appendPlainText(self.geoFile) self.dlgAss.show() def assembleSave(self): self.geoFile=self.dlgAss.text.toPlainText() f=open(self.foldername+"/output/geometry.txt", "w+") f.write(self.geoFile) f.close() def makeStatistics(self): import matplotlib.pyplot as plt first=1 files=os.listdir(self.pathRaw) for file in files: if ".csv" in file: grid=np.genfromtxt(self.pathRaw+file, delimiter=",") print (grid.shape) if first: all=grid first=0 else: all=np.concatenate((grid,all)) print (all.shape) self.min=all.min() self.max=all.max() self.std=all.std() self.mean=all.mean() hist, bin_edges = np.histogram(all) plt.hist(all, bins=100) plt.show() report=("Statistics\nmin=%.3f\nmax=%.3f\nStd=%.3f\nMean=%.3f" %(self.min,self.max,self.std,self.mean)) self.infoTxt_main.setText(report) def drawSelection(self): x,y=self.currentSelection if (x!=-1): self.select=self.ax.add_patch( patches.Rectangle( (x*self.tileSize[0], y*self.tileSize[1]), # (x,y) self.tileSize[0], # width self.tileSize[1], # height fill=False, edgecolor="red", linewidth=1 ) ) gridID=self.getGeometry()[y][x] print (x,y,gridID) self.select_gridID= plt.text(x*self.tileSize[0], y*self.tileSize[1]+self.tileSize[1],gridID) self.canvas.draw() def clearSelection(self): x,y=self.currentSelection if (x!=-1): try: self.select.remove() self.select_gridID.remove() self.canvas.draw() except: print ("No selection to clear") def onclickFig(self,event): print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (event.button, event.x, event.y, event.xdata, event.ydata)) selection=True if event.button==1: if selection: x=int(event.xdata/self.tileSize[0]) y=int(event.ydata/self.tileSize[1]) print("right Click at %s %s" %(x,y)) self.clearSelection() if self.index[y][x]!="0": self.currentSelection=(x,y) self.drawSelection() elif event.button==2: self.drag_view(event) elif event.button==3: x=int(event.xdata/self.tileSize[0]) y=int(event.ydata/self.tileSize[1]) self.clearSelection() if self.index[y][x]!="0": self.currentSelection=(x,y) self.drawSelection() point=QPoint(event.x, self.canvas.height()-event.y) self.popMenu.exec_(self.canvas.mapToGlobal(point)) def drag_view(self, event): print ("pan") 'on button press we will see if the mouse is over us and store some data' if event.inaxes != self.ax.axes: return cur_xlim = self.ax.get_xlim() cur_ylim = self.ax.get_ylim() self.press = cur_xlim, cur_ylim, event.xdata, event.ydata def on_motion(self, event): 'on motion we will move the rect if the mouse is over us' if self.press is None: return if event.inaxes != self.ax.axes: return cur_xlim, cur_ylim, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress #print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' % # (x0, xpress, event.xdata, dx, x0+dx)) self.ax.set_xlim([cur_xlim[0]-dx,cur_xlim[1]-dx]) self.ax.set_ylim([cur_ylim[0]-dy,cur_ylim[1]-dy]) app.processEvents() self.canvas.draw() def on_context_menu(self, point): # # show context menu # self.popMenu.exec_(self.canvas.mapToGlobal(point)) pass def on_release(self, event): if event.button==2: 'on release we reset the press data' self.press = None print ("realese button") def zoom_fun(self,event,base_scale = 1.5): dataXmax=self.survey.getGeometry().shape[1]*self.gridSize[1] dataYmax=self.survey.getGeometry().shape[0]*self.gridSize[0] # get the current x and y limits curr_xlim = self.ax.get_xlim() curr_ylim = self.ax.get_ylim() xdata = event.xdata # get event x location ydata = event.ydata # get event y location if event.button == 'up': # deal with zoom in scale_factor = 1/base_scale elif event.button == 'down': # deal with zoom out scale_factor = base_scale else: # deal with something that should never happen scale_factor = 1 print (event.button) # set new limits new_width = (curr_xlim[1]-curr_xlim[0])*scale_factor new_height= (curr_ylim[1]-curr_ylim[0])*scale_factor relx = (curr_xlim[1]-event.xdata)/(curr_xlim[1]-curr_xlim[0]) rely = (curr_ylim[1]-event.ydata)/(curr_ylim[1]-curr_ylim[0]) new_xlim=[event.xdata-new_width*(1-relx),event.xdata+new_width*(relx)] if new_xlim[0]<0: new_xlim[0]=0 if new_xlim[1]>dataXmax: new_xlim[1]=dataXmax self.ax.set_xlim(new_xlim) new_ylim=[event.ydata+new_width*(1-rely),event.ydata-new_width*(rely)] if new_ylim[0]<0: new_ylim[0]=0 if new_ylim[1]>dataYmax: new_ylim[1]=dataYmax self.ax.set_ylim(new_ylim) app.processEvents() self.canvas.draw() def rotateL(self): ii,i=self.currentSelection csv=np.genfromtxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), delimiter=",") csv=np.rot90(csv) np.savetxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), csv, delimiter=",", fmt='%.2f') self.process() self.currentSelection=(ii,i) self.drawSelection() def rotateR(self): ii,i=self.currentSelection csv=np.genfromtxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), delimiter=",") csv=np.rot90(csv,3) np.savetxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), csv, delimiter=",", fmt='%.2f') self.process() self.currentSelection=(ii,i) self.drawSelection() def fliplr(self): ii,i=self.currentSelection csv=np.genfromtxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), delimiter=",") csv=np.fliplr(csv) np.savetxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), csv, delimiter=",", fmt='%.2f') self.process() self.currentSelection=(ii,i) self.drawSelection() def flipud(self): ii,i=self.currentSelection csv=np.genfromtxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), delimiter=",") csv=np.flipud(csv) np.savetxt(str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv'), csv, delimiter=",", fmt='%.2f') self.process() self.currentSelection=(ii,i) self.drawSelection() def openEditor(self): ii,i=self.currentSelection file=str(self.pathRaw+self.siteName+'_'+self.index[i][ii]+'.csv') import subprocess print (file) subprocess.call(['notepad',file]) def editPoint(self): print ("the point") def downloadRM85(self): import serial rows=self.dlgImport.YSize.value() cols=self.dlgImport.XSize.value() try: ser = serial.Serial(str(self.dlgImport.com.currentText()), int(self.dlgImport.baud.currentText()), timeout=1) #Tried with and without the last 3 parameters, and also at 1Mbps, same happens. ser.flushInput() ser.flushOutput() except: connected=0 self.dlgImport.status.setText("No device detected. Check connection.") app.processEvents() else: self.dlgImport.status.setText("Ready. Press <<Dump>> on device.") connected=1 app.processEvents() if connected: started = 0 ii=int(self.dlgImport.firstGrid.value())-1 data_raw="" mode= self.dlgImport.mode.currentIndex() siteNameID=self.siteName while True: bytesToRead = ser.readline() if (bytesToRead!=""): self.dlgImport.status.setText("Loading...") app.processEvents() started=1 ii+=1 if (ii<10): countStr=str("0%s" %ii) else: countStr=ii matrix1=np.empty([rows,cols]) if mode==1: matrix50=np.empty([rows,2*cols]) samplesPerPoints=3 if mode ==0: samplesPerPoints=1 self.dlgImport.progressBar.setValue(0) for x in range (0,cols): line="" self.dlgImport.status.setText("Loading... Grid %s - Line %s" %(ii, x+1)) for y in range (0,rows): for pt in range (0,samplesPerPoints): while True: if (x==0 and y==0 and pt==0): line=str(bytesToRead) else: line=str(ser.readline()) reading=str(ser.readline()) if "01" in reading: gain=1 elif "11" in reading: gain=10 elif "21" in reading: gain=100 break if ("4095" in line): value="0" elif ("4094" in line): value="0.01" else: value=int(line.strip()) value=value/(2*float(gain)) value= round(value, 3)#convert to resistance values if (x%2!=0):#Zigzag mode if (pt==0): matrix1[19-y][x]=value elif(pt==1): matrix50[19-y][2*x+1]=value elif(pt==2): matrix50[19-y][2*x]=value else: if (pt==0): matrix1[y][x]=value elif(pt==1): matrix50[y][2*x]=value elif(pt==2): matrix50[y][2*x+1]=value self.dlgImport.progressBar.setValue((100/cols)*(x+1)) app.processEvents() ### Rotate if self.dlgImport.SWV.isChecked(): matrix1=np.flipud(matrix1) elif self.dlgImport.SWH.isChecked(): matrix1=np.rot90(matrix1) elif self.dlgImport.SEV.isChecked(): matrix1=np.rot90(matrix1,2) elif self.dlgImport.SEH.isChecked(): matrix1=np.rot90(matrix1,3) matrix1=np.flipud(matrix1) elif self.dlgImport.NWV.isChecked(): pass elif self.dlgImport.NWH.isChecked(): matrix1=np.rot90(matrix1) matrix1=np.flipud(matrix1) elif self.dlgImport.NEH.isChecked(): matrix1=np.rot90(matrix1,3) elif self.dlgImport.NEV.isChecked(): matrix1=np.fliplr(matrix1) #### Writing file. Checking whether the file already exists #### fname1='%s\%s_%s.csv' %(self.pathRaw,siteNameID,countStr) if (os.path.isfile(fname1)): msg=QMessageBox.about(self, "Error", "Error: Grid %s Already exists. Erase it first." %ii) break else: np.savetxt(fname1, matrix1, delimiter=",", fmt='%.2f') if mode==1: fname50='%s\grid50_%s.csv' %(self.pathRaw,countStr) if (os.path.isfile(fname1)): msg=QMessageBox.about(self, "Error", "Error: Grid %s Already exists. Erase it first." %ii) break else: matrix50=np.rot90(matrix50) np.savetxt(fname50, matrix50, delimiter=",", fmt='%.2f') app.processEvents() if (started==1 and bytesToRead==""): ser.flushInput() ser.flushOutput() ser.close() del ser self.dlgImport.status.setText("Done") self.dlgImport.go.setText("Done") self.dlgImport.close() break def saveParam(self): try: confFile=open(os.path.join(os.path.dirname(__file__), 'conf.txt'),'w') except: print ("Error opening the config file. Check you have the rights to write in the directory.") conftxt="SITEDIR="+ self.foldername + "\n" conftxt+="SITENAME="+ self.siteName + "\n" conftxt+="TILESIZEX=%s\n" %self.tileSize[0] conftxt+="TILESIZEY=%s\n" %self.tileSize[1] conftxt+="EPSG= %s\n" %self.epsg confFile.write(conftxt) def loadParam(self): confFile=open(os.path.join(os.path.dirname(__file__), 'conf.txt'),'r') # print "Error loading the config file. Default values will be used." ##Set some default values X=20 Y=20 self.foldername="" for line in confFile: if "SITEDIR" in line: foldername=line.split('=')[1].strip() self.foldername=foldername self.pathRaw=foldername +"/raw/" self.pathOutput=foldername +"/output/" if (os.path.isdir(self.pathRaw)==0 or os.path.isdir(self.pathOutput)==0): self.actionImport_main.setEnabled(False) self.actionAssemble_main.setEnabled(False) self.dir_main.setText("") print("no dir found") self.foldername="" else: self.dir_main.setText(foldername) self.actionImport_main.setEnabled(True) self.actionAssemble_main.setEnabled(True) self.dir_main.setText(self.foldername) if "TILESIZEX" in line: X=int(line.split('=')[1]) if "TILESIZEY" in line: Y=int(line.split('=')[1]) if "SITENAME" in line: self.siteName=line.split('=')[1].strip() self.siteName_main.setText(self.siteName) if "EPSG" in line: self.epsg=line.split('=')[1].strip() self.tileSize=(X,Y) def process(self): self.clearInfo() self.index=self.getGeometry() self.mainMatrix=self.makeMosaic() self.exportGeoTiff_main.setEnabled(True) def matrix2figure(self, matrix): self.figure.clear() if self.stdClip_check.isChecked(): matrix=self.stdClip(matrix, self.stdClip_slider.value()/float(100)) self.ax = self.figure.add_subplot(111) # discards the old graph #self.ax.hold(False) noData=0 masked_data = np.ma.masked_where(matrix == noData, matrix) print (self.vis_palette.currentText()) array = plt.imshow(masked_data, interpolation='none', cmap=str(self.vis_palette.currentText())) # cbar = plt.colorbar(mappable=array, orientation = 'horizontal', fraction=0.030, pad=0.07) # cbar.set_label('Resisitivity (Ohm/m)') plt.tight_layout() self.ax.grid(True) index=self.getGeometry() y=(index.shape[0]) x=(index.shape[1]) xmax=x*self.tileSize[0] ymax=y*self.tileSize[1] print(xmax,ymax) self.ax.xaxis.set_ticks(np.arange(0, xmax, 20)) self.ax.yaxis.set_ticks(np.arange(0, ymax, 20)) app.processEvents() self.canvas.draw() cid = self.canvas.mpl_connect('button_press_event', self.onclickFig) zo=self.canvas.mpl_connect('scroll_event',self.zoom_fun) cidMove=self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.press=None cidRelease= self.canvas.mpl_connect('button_release_event', self.on_release) self.currentSelection=(-1,-1) def exportGeoTiff(self, filename, mat): import os.path import gdal, osr mat=np.int16(mat) xSize=mat.shape[1] ySize=mat.shape[0] step=1 output_file=str(self.pathOutput+filename+'.tif') xCoord=0 yCoord=0 rotX=0 rotY=0 epsg=int(self.epsg) #epsg=32633 UTM nord 33 #epsg=3004 Monte Mario 2 print('Output coordintate system: %s' % epsg) geoTrans=[ xCoord, step , 0, yCoord , 0, step ] srs = osr.SpatialReference() srs.ImportFromEPSG(epsg) # Create gtif (uncomment when using gdal) # DataType= gdal.GDT_UInt16 driver = gdal.GetDriverByName("GTiff") dst_ds = driver.Create(output_file, xSize, ySize, 1, DataType) #top left x, w-e pixel resolution, rotation, top left y, rotation, n-s pixel resolution print (geoTrans) #leavev the geotransform to use worldfile ##dst_ds.SetGeoTransform( geoTrans ) dst_ds.SetProjection( srs.ExportToWkt() ) # write the band dst_ds.GetRasterBand(1).WriteArray(mat) band = dst_ds.GetRasterBand(1) band.SetNoDataValue(0) band.FlushCache() def exportTiff(self, filename, mat): import os.path import gdal, osr from libtiff import TIFF import arcpy mat=np.uint16(mat) xSize=mat.shape[1] ySize=mat.shape[0] step=1 output_file=str(self.pathOutput+filename+'.tif') tiff = TIFF.open(output_file, mode='w') tiff.write_image(mat) tiff.close() arcpy.SetRasterProperties_management(output_file, "GENERIC","#" , "0" , "#") def georeferencing(self): import arcpy if os.path.isfile(self.pathOutput+"gcp.txt"): print ("Georeferencing output from GCPs") files=os.listdir(self.pathOutput) for file in files: if ".tif" in file: try: arcpy.WarpFromFile_management( self.pathOutput+file, self.pathOutput+"georef_"+file, self.pathOutput+"gcp.txt", "POLYORDER1", "BILINEAR") except: print ("Error georeferencing "+file) else: print ("No GCP file found.") def normalize(self,mat): min=999999 max=0 for i in range (0,mat.shape[0]): for ii in range (0,mat.shape[1]): if (mat[i][ii]!=0 and mat[i][ii]<min): min=mat[i][ii] if (mat[i][ii]!=0 and mat[i][ii]>max): max=mat[i][ii] print(min, max) mat=((mat-min)/(max-min)) return mat def normalizeClip(self,mat,clip): dev=mat.std() mean=mat.mean() min=mean-clip*dev max=mean+clip*dev mat=((mat-min)/(max-min)) return mat def stdClip(self,mat,clip): dev=mat.std() mean=mat.mean() min=mean-clip*dev max=mean+clip*dev for i in range (0, mat.shape[0]): for ii in range (0,mat.shape [1]): if mat[i][ii]>max: mat[i][ii]=max if mat[i][ii]<min: mat[i][ii]=min return mat def getGeometry(self): #print (str(self.pathOutput)+'geometry.txt') index=np.genfromtxt(str(self.pathOutput)+'geometry.txt',dtype='str', delimiter="\t"); return index; def excludeOdds(self,mat): #Exclude weird values and 0's from the mean calculation mat=np.array(mat) mat=mat.flatten() mat=mat[np.nonzero(mat)] std=np.std(mat) mean=np.mean(mat) indexOdds=list() limMin=mean-2.5*std limMax=mean+2.5*std for i in range (0, len(mat)): if(mat[i] <limMin or mat[i]>limMax): indexOdds.append(i) mat=np.delete(mat,indexOdds) return mat; def delta_extrap(self,grid_2,adj_2): #determine the difference between adajcent edges of two grids grid_extrap=np.zeros(grid_2.shape[0]) adj_extrap=np.zeros(grid_2.shape[0]) grid_lin=grid_2[:,0] adj_lin=adj_2[:,1] delete=[] good=0 bad=0 for i in range (0,len(grid_extrap)): grid_extrap[i]=((grid_2[i][0]-grid_2[i][1])*0.5)+grid_2[i][0] adj_extrap[i]=((adj_2[i][1]-adj_2[i][0])*0.5)+adj_2[i][1] diff=np.zeros(len(grid_extrap)) for i in range (0,len(grid_extrap)): diff[i]=((grid_extrap[i]-adj_extrap[i])+(grid_lin[i]-adj_lin[i]))/2 mean_diff=np.mean(diff) std_diff=np.std(diff) for i in range (0,len(diff)): if (grid_2[i][0]==0 or grid_2[i][1]==0 or adj_2[i][0]==0 or adj_2[i][1]==0): delete=np.append(delete,i) if (diff[i]>mean_diff+2*std_diff or diff[i]<mean_diff-2*std_diff): delete=np.append(delete,i) diff=np.delete(diff,delete) if (len(diff)>2):#if the edge is not too small.... d=np.round(np.mean(diff),3) #the difference between the grids if (d!=0): w=np.power(len(diff),2)/(np.std(diff)) #The weighting factor good+=1 else: w=0 bad+=1 else: w=0 d=0 bad+=1 return d,w def edgeMatch2(self,matrix,sizeEdge): print ('edge matching...') edgeSize=sizeEdge #Split the whole matrix in tiles (grids of 20x20 in our case) xGrids=int(matrix.shape[1]/edgeSize) yGrids=int(matrix.shape[0]/edgeSize) n=int(xGrids*yGrids) print (n) print (int(n)) A=np.zeros([n,n]) x=np.zeros([n]) b=np.zeros([n]) minInit=np.min(matrix) if minInit<0: print ("warning: Negative values in the data. This may cause errors later.\n Consider despiking or correct these data.") matSorted=np.sort(matrix, axis=None) pos=1 for i in range (0,len(matSorted)): if matSorted[i]==0 and pos: print ("%s negative values" %(i)) print (matSorted[0:i]) pos=0 if matSorted[i]>0.01: minInit=matSorted[i] break i=0 maxInit=np.max(matrix) print (minInit,maxInit) range_init=maxInit-minInit bestW=0 reference=0 print ('%s rows x %s cols' %(yGrids,xGrids)) for x in range(0,xGrids): for y in range (0,yGrids): #for each grid we compute the mismatch d and the weight coefficient w with the four neighbours 1:W, 2:N, 3,E, 4S ymin=y*edgeSize ymax=(y+1)*edgeSize xmin=x*edgeSize xmax=(x+1)*edgeSize #Adjacent edge if (x>0): gridW=(matrix[ymin:ymax,xmin:xmin+2]) edgeW=(matrix[ymin:ymax,xmin-2:xmin]) di1,wi1=self.delta_extrap(gridW,edgeW) else: di1=0 wi1=0 if (y<yGrids-1): gridN=np.fliplr(np.rot90((matrix[ymax-2:ymax,xmin:xmax]))) edgeN=np.fliplr(np.rot90((matrix[ymax:ymax+2,xmin:xmax]))) di2,wi2=self.delta_extrap(gridN,edgeN) else: di2=0 wi2=0 if (x<xGrids-1): gridE=np.fliplr((matrix[ymin:ymax,xmax-2:xmax])) edgeE=np.fliplr((matrix[ymin:ymax,xmax:xmax+2])) di3,wi3=self.delta_extrap(gridE,edgeE) else: di3=0 wi3=0 if (y>0): gridS=np.flipud(np.rot90((matrix[ymin:ymin+2,xmin:xmax]))) edgeS=np.flipud(np.rot90((matrix[ymin-2:ymin,xmin:xmax]))) di4,wi4=self.delta_extrap(gridS,edgeS) else: di4=0 wi4=0 if (wi1!=0 and wi2!=0 and wi3!=0 and wi4!=0): fitness=wi1+wi2+wi3+wi4 if fitness>bestW: bestW=fitness bestGrid=(x,y) reference=1 #following the equation of Haigh 1995 A[i,i]=(wi1+wi2+wi3+wi4) #factor of xi if (x>0): #the left line has no W side A[i,i-yGrids]=-wi1 #factor of xi1 if (y<yGrids-1): #the top line has no N side A[i,i+1]=-wi2 #factor of xi2 if (x<xGrids-1): #the right line has no E side A[i,i+yGrids]=-wi3 #factor of xi3 if (y>0): #the bottom line has no S side A[i,i-1]=-wi4 #factor of xi4 b[i]=((di1*wi1)+(di2*wi2)+(di3*wi3)+(di4*wi4)) i+=1 print ('Resolving the equation of the meaning of life...') try: U,s,V = np.linalg.svd(A) # SVD decomposition of A # computing the inverse using pinv pinv = np.linalg.pinv(A) # computing the inverse using the SVD decomposition pinv_svd = np.dot(np.dot(V.T,np.linalg.inv(np.diag(s))),U.T) c = np.dot(U.T,b) # c = U^t*b w = np.linalg.solve(np.diag(s),c) # w = V^t*c xSVD = np.dot(V.T,w) # x = V*w #the function is described here: http://students.mimuw.edu.pl/~pbechler/numpy_doc/reference/generated/numpy.linalg.lstsq.html corr=np.flipud(np.rot90(np.reshape(xSVD,(xGrids,yGrids)),1)) #We reshape the coefficient to the size of the initial matrix except: errorMessage= 'Singular Matrix, edge matching equation could not be solved. Survey area is possibly too small.' print (errorMessage) QMessageBox.about(self, "Edge matching Error", errorMessage) return matrix else: #rescale to the reference grid: if reference: delta=corr[bestGrid[1]][bestGrid[0]] print ("reference grid at x:%s y:%s delta:%s" % (bestGrid[0],bestGrid[1],delta)) corr=corr-delta #apply the correction: matrix_out=np.zeros((matrix.shape[0],matrix.shape[1])) mask=np.zeros((matrix.shape[0],matrix.shape[1])) mask[matrix.nonzero()]=1 for x in range(0,xGrids): for y in range (0,yGrids): ymin=y*edgeSize ymax=(y+1)*edgeSize xmin=x*edgeSize xmax=(x+1)*edgeSize b=corr[y,x] matrix_out[ymin:ymax,xmin:xmax]=matrix[ymin:ymax,xmin:xmax]-b matrix_out= matrix_out * mask min=np.nanmin(matrix_out) max=np.nanmax(matrix_out) range_output=max-min print (range_output) print (min) print (max) if (min<0): matrix_out=matrix_out-min print("minimum:%s"%(np.nanmin(matrix_out[matrix_out>0.0]))) matrix_out=((matrix_out+minInit)/range_output)*maxInit matrix_mask= matrix_out * mask print("minimum:%s"%(np.nanmin(matrix_mask[matrix_out>0.0]))) print("maximum:%s"%(np.nanmax(matrix_mask))) return matrix_mask def deslope(self,matrix,sizeEdge): print ("deloping...") edgeSize=sizeEdge #Split the whole matrix in tiles (grids of 20x20 in our case) xGrids=int(matrix.shape[1]/edgeSize) yGrids=int(matrix.shape[0]/edgeSize) n=int(xGrids*yGrids) for x in range(0,xGrids): for y in range (0,yGrids): #for each grid we compute the mismatch d and the weight coefficient w with the four neighbours 1:W, 2:N, 3,E, 4S for a in range (0,(edgeSize-1)): xmin=x*edgeSize xmax=(x+1)*edgeSize yline=y*edgeSize+a lineBottom=matrix[yline,xmin:xmax] lineTop=matrix[yline+1,xmin:xmax] delta=np.mean(lineTop)-np.mean(lineBottom) matrix[yline+1,xmin:xmax]=lineTop-delta return matrix def gaussian_blur1d(self, in_array, size): #check validity try: if 0 in in_array.shape: raise Exception("Null array can't be processed!") except TypeError: raise Exception("Null array can't be processed!") # expand in_array to fit edge of kernel padded_array = np.pad(in_array, size, 'symmetric').astype(float) # build kernel x, y = np.mgrid[-size:size + 1, -size:size + 1] g = np.exp(-(x**2 / float(size) + y**2 / float(size))) g = (g / g.sum()).astype(float) # do the Gaussian blur out_array = fftconvolve(padded_array, g, mode='valid') return out_array.astype(in_array.dtype) def sharpening(self, in_array, alpha): try: if 0 in in_array.shape: raise Exception("Null array can't be processed!") except TypeError: raise Exception("Null array can't be processed!") f=in_array blurred_f = ndimage.gaussian_filter(f, 3) filter_blurred_f = ndimage.gaussian_filter(blurred_f, 1) alpha = 30 sharpened = blurred_f + alpha * (blurred_f - filter_blurred_f) return sharpened.astype(in_array.dtype) def makeMosaic(self): from PIL import Image self.addInfo("Load parameters\n") self.siteName=self.siteName_main.text() gridSize=self.gridSize index=self.getGeometry() zero=np.zeros(gridSize) x=index.shape[0] y=index.shape[1] self.addInfo("Assembling tiles\n") for i in range (0,x): for ii in range (0,y): if (index[i][ii]=='0'): A=zero else: try: A = np.genfromtxt(str(self.pathRaw+self.siteName+'_'+index[i][ii]+'.csv'), delimiter=",") except: print ('Warning: Grid '+index[i][ii]+' not found.\n'+str(self.pathRaw+self.siteName+'_'+index[i][ii]+'.csv')) A=zero if A.shape!=(20,20): print ("grid of odd dimensions: " + index[i][ii] ) print (A.shape) if (ii==0): line=A else: #print('--------Grid: %s - %s -------' %(i, ii)) grid=A #grid=edgeMatch(line,A,'horizontal') line=np.concatenate((line,grid),axis=1) if (i==0): mos=line else: #line=edgeMatch(mos,line,'vertical') mos=np.concatenate((mos,line),axis=0) if (self.despike_main.isChecked()): self.addInfo("Despiking...") mos=p.despike(mos) self.addInfo("Done\n") if (self.deslope_main.isChecked()): self.addInfo("Desloping...") mos= self.deslope(mos,20) self.addInfo("Done\n") if (self.edgeMatching_main.isChecked()): self.addInfo("Edge matching...") mos=self.edgeMatch2(mos,20) self.addInfo("Done\n") #mos=localVariation(mos) step=1 xtif=y*gridSize[0] ytif=x*gridSize[1] self.addInfo("Creating preview...") #mos,cdf = histeq(mos) print(np.min(mos),np.max(mos)) print('%s x %s' %(x,y)) hs_array = p.hillshade(mos,45, 315, 0.0025) self.makePreview(mos, hs_array, str(self.siteName)) self.addInfo("Finished\n") self.matrix2figure(mos) return mos def makePreview(self,array, hillshade_mat, filename): import matplotlib import matplotlib.mlab as mlab import matplotlib.pyplot as plt import matplotlib.cm as cm from PIL import Image import cv2 #import scipy.ndimage x=array.shape[0] y=array.shape[1] min= 99999 max=0 if self.stdClip_check.isChecked(): array=self.stdClip(array, self.stdClip_slider.value()/float(100)) for i in range (0,x): for ii in range (0,y): if (array[i][ii]!=0 and array[i][ii]<min): min=array[i][ii] if (array[i][ii]!=0 and array[i][ii]>max): max=array[i][ii] print('min: %.2f max: %.2f \nsize: %s x %s' %(min,max, x, y)) mask=np.empty([x, y], dtype='float64') for i in range (0,x): for ii in range (0,y): if (array[i][ii]!=0): mask[i][ii]=255 if (array[i][ii]==0): mask[i][ii]=0 if (min!=max and max!=0): array=(1-(array-min)/(max-min)) array = cv2.resize(array, (0,0), fx=5, fy=5) hillshade_mat = cv2.resize(hillshade_mat, (0,0), fx=5, fy=5) mask = cv2.resize(mask, (0,0), fx=5, fy=5, interpolation=cv2.INTER_NEAREST) #array= scipy.ndimage.median_filter(array, 4) values = Image.fromarray(np.uint8(cm.jet_r(array)*255)).convert('RGB') hs_array = Image.fromarray(np.uint8(hillshade_mat)).convert('RGB') new_img = Image.blend(values, hs_array, 0.3).convert('RGBA') mask = Image.fromarray(np.uint8(mask)).convert('L') new_img.putalpha(mask) new_img.save(str(self.pathOutput+filename)+'preview.png') # self.displayPreview() else: print('error in reading image') def exportGeo(self): print('Export image...') self.statusBar().showMessage('Export image...') self.exportGeoTiff(self.siteName, self.mainMatrix*100) print('Export hillshade...') self.statusBar().showMessage('Export hillshade...') hs_array = p.hillshade(self.mainMatrix*100,45, 315, 0.0025) self.exportGeoTiff(self.siteName+"_hs", hs_array) self.statusBar().showMessage('GeoTIFF exported')
class MplWindow(UI_MainWindow, MainWindow): def __init__(self): super(MplWindow, self).__init__() self.verbose = 1 self.setupUi(self) self.showMaximized() self.fig = Figure() self.graph_sequence = [] self.graph_sequence_comments = [] self._V6fromPHC = [] # self._log = '' # self.setRequiresRecomputing() self.setActiveGraph(GraphCouplerCurve(window=self)) self.init_widgets() self.init_mpl() # self.init_data() self.init_plot() #self.init_pick() self.dockLog.resize(self.dockLog.minimumWidth() * 2, 300) self.dockLog.updateGeometry() def setActiveGraph(self, G): self.graph = G self.updateParameter() if self.graph.isComputed(): self.setComputed() else: self.setRequiresRecomputing() def init_widgets(self): """Initialize the widgets, mainly connecting the relevant signals""" self.plainTextEdit.setReadOnly(True) # self.plainTextEdit.setCenterOnScroll(True) # self.plainTextEdit.setMaximumBlockCount(40) self.doubleSpinBoxLengths = { '12': self.doubleSpinBoxL12, '13': self.doubleSpinBoxL13, '14': self.doubleSpinBoxL14, '15': self.doubleSpinBoxL15, '16': self.doubleSpinBoxL16, '27': self.doubleSpinBoxL27, '37': self.doubleSpinBoxL37, '47': self.doubleSpinBoxL47, '57': self.doubleSpinBoxL57, '67': self.doubleSpinBoxL67, '23': self.doubleSpinBoxL23, '34': self.doubleSpinBoxL34, '45': self.doubleSpinBoxL45, '56': self.doubleSpinBoxL56, } self.update_graph2tabLengths() self.update_graph2phi() for e in self.doubleSpinBoxLengths: self.doubleSpinBoxLengths[e].valueChanged.connect( self.update_tabLengths2graph) self.update_graph2R26() self.doubleSpinBoxR26.valueChanged.connect(self.update_R26toGraph) self.doubleSpinBoxY2.valueChanged.connect(self.update_yV2toGraph) self.doubleSpinBoxPhi.valueChanged.connect(self.update_phi2graph) self.doubleSpinBoxTheta.valueChanged.connect(self.update_theta2graph) self.pushButtonPlot.clicked.connect(self.computeCouplerCurves) self.pushButtonPlot.setShortcut(QKeySequence("Ctrl+P")) self.checkBoxBranches = { 'orange': self.checkBoxOrange, 'red': self.checkBoxRed, 'green': self.checkBoxGreen, 'blue': self.checkBoxBlue } for c in self.checkBoxBranches: self.checkBoxBranches[c].stateChanged.connect(self.plotScene) self.checkBoxMirror.stateChanged.connect(self.plotScene) # self.spinBoxSamples.valueChanged.connect(self.updateSamples) self.comboBoxGraphFor.currentTextChanged.connect( self.updateDisplayedGraph) self.spinBoxParameter.valueChanged.connect(self.updateDisplayedGraph) self.spinBoxParameter.setWrapping(True) self.spinBoxParameterStep.valueChanged.connect( self.update_parameterStep) self.doc_tb = self.addToolBar("File") self.loadLengthsButton = QAction(QIcon.fromTheme("document-open"), "Load lengths", self) self.doc_tb.addAction(self.loadLengthsButton) self.loadLengthsButton.triggered.connect(self.loadLengths) action = self.menuFile.addAction('Load lengths') action.triggered.connect(self.loadLengths) self.saveLengthsButton = QAction(QIcon.fromTheme("document-save"), "Save lengths", self) self.doc_tb.addAction(self.saveLengthsButton) self.saveLengthsButton.triggered.connect(self.saveLengths) action = self.menuFile.addAction('Save lengths') action.triggered.connect(self.saveLengths) self.inOut_tb = self.addToolBar("Input/Output") self.insertLengthsButton = QAction(QIcon.fromTheme("insert-text"), "Insert lengths", self) self.inOut_tb.addAction(self.insertLengthsButton) self.insertLengthsButton.triggered.connect(self.insertLengths) action = self.menuInputOutput.addAction('Insert lengths') action.triggered.connect(self.insertLengths) self.insertLengthsByEmbeddingsButton = QAction( "Insert lengths by embedding", self) self.inOut_tb.addAction(self.insertLengthsByEmbeddingsButton) self.insertLengthsByEmbeddingsButton.triggered.connect( self.insertLengthsByEmbeddings) action = self.menuInputOutput.addAction('Insert lengths by embedding') action.triggered.connect(self.insertLengthsByEmbeddings) self.exportLengthsButton = QAction("Export lengths", self) self.inOut_tb.addAction(self.exportLengthsButton) self.exportLengthsButton.triggered.connect(self.exportLengths) action = self.menuInputOutput.addAction('Export lengths') action.triggered.connect(self.exportLengths) self.actionFullscreen.toggled.connect(self.fullScreen) self.updateParameter() self.buttonLoadSequence.clicked.connect(self.loadSequence) self.spinBoxImgInSeq.setMinimum(1) self.spinBoxImgInSeq.setMaximum(1) self.spinBoxImgInSeq.valueChanged.connect(self.plotGraphFromSequence) self.spinBoxSeqLength.setReadOnly(True) self.spinBoxSeqLength.setButtonSymbols(QAbstractSpinBox.NoButtons) self.noteToImgInSeq.setReadOnly(True) self.boxInfoImgInSeq.setVisible(False) self.buttonRunPHC.clicked.connect(self.runPHC) self.spinBoxNumberReal.setReadOnly(True) self.spinBoxNumberReal.setButtonSymbols(QAbstractSpinBox.NoButtons) self.buttonRotateVertices.clicked.connect(self.rotateVertices) # self.exportButton.clicked.connect(self.exportLengths) self.doubleSpinBox_x.valueChanged.connect(self.plotScene) self.doubleSpinBox_y.valueChanged.connect(self.plotScene) self.doubleSpinBox_z.valueChanged.connect(self.plotScene) dockWidgets = self.findChildren(QDockWidget) for dockWidget in dockWidgets: action = self.menuView.addAction(dockWidget.windowTitle()) action.setCheckable(True) action.setChecked(True) action.triggered.connect(dockWidget.setVisible) dockWidget.visibilityChanged.connect(action.setChecked) # dockWidget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) # dockWidget.updateGeometry() # self.dockLog.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) # self.dockLog.updateGeometry() self.buttonSamplingPhiTheta.clicked.connect(self.runSamplingPhiTheta) self._possibleParametrizedVertices = { '[2, 1, 6, 3, 7]': [2, 1, 6, 3, 7], '[2, 3, 1, 7, 6]': [2, 3, 1, 7, 6], '[2, 6, 1, 7, 3]': [2, 6, 1, 7, 3], '[2, 7, 6, 3, 1]': [2, 7, 6, 3, 1], '[3, 1, 2, 4, 7]': [3, 1, 2, 4, 7], '[3, 2, 1, 7, 4]': [3, 2, 1, 7, 4], '[3, 4, 1, 7, 2]': [3, 4, 1, 7, 2], '[3, 7, 2, 4, 1]': [3, 7, 2, 4, 1], '[4, 1, 3, 5, 7]': [4, 1, 3, 5, 7], '[4, 3, 1, 7, 5]': [4, 3, 1, 7, 5], '[4, 5, 1, 7, 3]': [4, 5, 1, 7, 3], '[4, 7, 3, 5, 1]': [4, 7, 3, 5, 1], '[5, 1, 4, 6, 7]': [5, 1, 4, 6, 7], '[5, 4, 1, 7, 6]': [5, 4, 1, 7, 6], '[5, 6, 1, 7, 4]': [5, 6, 1, 7, 4], '[5, 7, 4, 6, 1]': [5, 7, 4, 6, 1], '[6, 1, 5, 2, 7]': [6, 1, 5, 2, 7], '[6, 2, 1, 7, 5]': [6, 2, 1, 7, 5], '[6, 5, 1, 7, 2]': [6, 5, 1, 7, 2], '[6, 7, 5, 2, 1]': [6, 7, 5, 2, 1] } self.comboBoxParamVert.setInsertPolicy(6) for comb in self._possibleParametrizedVertices: self.comboBoxParamVert.addItem(comb) self.buttonFindMore.clicked.connect(self.findMoreEmbeddings) self.tabifyDockWidget(self.dockBranches, self.dockSceneShift) self.tabifyDockWidget(self.dockSceneShift, self.dockSequenceNavigation) def fullScreen(self, bool): if bool: self.showFullScreen() else: self.showMaximized() def loadLengths(self): options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "Load lengths", "./examples", "Python Objects (*.p);;All Files (*)", options=options) if fileName: try: self.printLog('Lengths loaded from: ' + fileName) lengths, R26 = pickle.load(open(fileName, 'rb')) lengths['26'] = R26 self.graph.setLengthsAndUpdateFixedTriangle(lengths) # self.graph.setR26(R26) blocked = self.doubleSpinBoxR26.blockSignals(True) self.update_graph2R26() self.update_graph2tabLengths() self.update_graph2phi() self.doubleSpinBoxR26.blockSignals(blocked) self.computeCouplerCurves() except Exception as e: self.showError('Some problem with loading: \n' + str(e)) def saveLengths(self): options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "Save lengths", "./examples", "Python Objects (*.p);;All Files (*)", options=options) if fileName: self.printLog('Lengths saved to: ' + fileName) pickle.dump( [self.graph._lengths, self.graph.getR26()], open(fileName, 'wb')) def insertLengths(self): text, ok = QInputDialog.getMultiLineText( self, 'Insert lengths', 'Insert lengths as dictionary or list of squares of lengths:') if ok: try: evaluated_expr = ast.literal_eval(str(text)) if not type(evaluated_expr) is dict: g = evaluated_expr lengths = { '12': np.sqrt(g[0]), '13': np.sqrt(g[1]), '14': np.sqrt(g[2]), '15': np.sqrt(g[3]), '16': np.sqrt(g[4]), '27': np.sqrt(g[5]), '37': np.sqrt(g[6]), '47': np.sqrt(g[7]), '57': np.sqrt(g[8]), '67': np.sqrt(g[9]), '23': np.sqrt(g[10]), '34': np.sqrt(g[11]), '45': np.sqrt(g[12]), '56': np.sqrt(g[13]), '26': np.sqrt(g[14]) } else: lengths = evaluated_expr if type(lengths) == dict: self.printLog('Inserted lengths: ') self.graph.setLengthsAndUpdateFixedTriangle(lengths) self.update_graph2R26() self.update_graph2tabLengths() self.update_graph2phi() self.computeCouplerCurves() else: self.showError( 'Input must be list containing dictionary of lengths and float R26 or list of squares of lengths' ) except Exception as e: self.showError('Problem with input: \n' + str(e)) def insertLengthsByEmbeddings(self): text, ok = QInputDialog.getMultiLineText( self, 'Insert embedding', 'Insert list of coordinates of v1,..., v7:') if ok: try: evaluated_expr = ast.literal_eval(str(text)) v1, v2, v3, v4, v5, v6, v7 = evaluated_expr lengths = { '12': self.graph.dist(v1, v2), '13': self.graph.dist(v1, v3), '14': self.graph.dist(v1, v4), '15': self.graph.dist(v1, v5), '16': self.graph.dist(v1, v6), '27': self.graph.dist(v2, v7), '37': self.graph.dist(v3, v7), '47': self.graph.dist(v4, v7), '57': self.graph.dist(v5, v7), '67': self.graph.dist(v6, v7), '23': self.graph.dist(v2, v3), '34': self.graph.dist(v3, v4), '45': self.graph.dist(v4, v5), '56': self.graph.dist(v5, v6), '26': self.graph.dist(v2, v6) } self.printLog('Inserted lengths: ') self.graph.setLengthsAndUpdateFixedTriangle(lengths) self.update_graph2R26() self.update_graph2tabLengths() self.update_graph2phi() self.computeCouplerCurves() except Exception as e: self.showError('Problem with input: \n' + str(e)) def showError(self, s): msg = QErrorMessage(self) msg.showMessage(s) self.printLog(s) def init_plot(self): """Initialize the plot of the axes""" self.fig.subplots_adjust(left=0.0, top=1, right=1, bottom=0.0) self._branches_plot = self.fig.add_subplot(111, projection='3d') self._branches_plot.axis(aspect='equal') self._branches_plot.auto_scale_xyz([-1, 1], [-1, 1], [-1, 1]) self._firstPlot = True def init_mpl(self): """Initialize the mpl canvas and connect the relevant parts""" self.canvas = FigureCanvas(self.fig) self.canvas.draw() self.figLayout.addWidget(self.canvas) NavigationToolbar.toolitems = [ t for t in NavigationToolbar.toolitems if t[0] in ("Save", ) ] #,"Pan","Zoom","Subplots")] self.img_tb = NavigationToolbar(self.canvas, self, coordinates=False) actions = self.img_tb.findChildren(QAction) for a in actions: if a.text() == 'Customize': self.img_tb.removeAction(a) break self.addToolBar(self.img_tb) self.buttonAxelVisualisation = QAction('Axel visualisation', self) self.img_tb.addAction(self.buttonAxelVisualisation) self.buttonAxelVisualisation.triggered.connect(self.axelVisualisation) action = self.menuInputOutput.addAction('Axel visualisation') action.triggered.connect(self.axelVisualisation) def updateParameter(self): N = self.graph.getSamples() self.spinBoxParameter.setMaximum(N) blocked = self.spinBoxParameter.blockSignals(True) self.spinBoxParameter.setValue(N / 2) self.spinBoxParameter.blockSignals(blocked) self.spinBoxParameterStep.setValue(N / 20) def update_parameterStep(self): self.spinBoxParameter.setSingleStep(self.spinBoxParameterStep.value()) def updateDisplayedGraph(self): if self.isComputed(): self.plotScene() else: self.showError('Recomputation of coupler curve needed!') def update_graph2tabLengths(self): self.printLog('Tab Lengths updated from graph', verbose=1) for e in self.doubleSpinBoxLengths: blocked = self.doubleSpinBoxLengths[e].blockSignals(True) self.doubleSpinBoxLengths[e].setValue(self.graph.getEdgeLength(e)) self.doubleSpinBoxLengths[e].blockSignals(blocked) self.printLog(e + ': ' + str(self.doubleSpinBoxLengths[e].value()), verbose=2) def update_tabLengths2graph(self): self.printLog('Graph updated from tab Lengths', verbose=1) lengths = { '12': self.doubleSpinBoxL12.value(), '13': self.doubleSpinBoxL13.value(), '14': self.doubleSpinBoxL14.value(), '15': self.doubleSpinBoxL15.value(), '16': self.doubleSpinBoxL16.value(), '27': self.doubleSpinBoxL27.value(), '37': self.doubleSpinBoxL37.value(), '47': self.doubleSpinBoxL47.value(), '57': self.doubleSpinBoxL57.value(), '67': self.doubleSpinBoxL67.value(), '23': self.doubleSpinBoxL23.value(), '34': self.doubleSpinBoxL34.value(), '45': self.doubleSpinBoxL45.value(), '56': self.doubleSpinBoxL56.value(), '26': self.doubleSpinBoxR26.value() } try: self.graph.setLengthsAndUpdateFixedTriangle(lengths) except TriangleInequalityError as e: self.showError( 'Please, change the lengths, triangle inequality is violated!!!\n' + str(e)) self.update_graph2yV2() self.update_graph2phi() self.setRequiresRecomputing() def update_phi2graph(self): self.graph.setPhiDegree(self.doubleSpinBoxPhi.value()) blocked = self.doubleSpinBoxY2.blockSignals(True) self.doubleSpinBoxY2.setValue(self.graph.getyV2()) self.doubleSpinBoxY2.blockSignals(blocked) self.update_yV2toGraph() def update_theta2graph(self): self.graph.setThetaDegree(self.doubleSpinBoxTheta.value()) blocked = self.doubleSpinBoxR26.blockSignals(True) self.doubleSpinBoxR26.setValue(self.graph.getR26()) self.doubleSpinBoxR26.blockSignals(blocked) self.update_R26toGraph() def update_R26toGraph(self): self.printLog('Graph updated from R26') self.graph.setR26(self.doubleSpinBoxR26.value()) self.labelRecomputePHC.setText( '<html><head/><body><p><span style=" color:#ff0000;">Recomputation needed</span></p></body></html>' ) self.update_graph2theta() if self.isComputed(): self.graph.computeIntersections() self.plotScene() else: self.showError('Recomputation of coupler curve needed!') def update_yV2toGraph(self): self.printLog('v2 changed') self.labelRecomputePHC.setText( '<html><head/><body><p><span style=" color:#ff0000;">Recomputation needed</span></p></body></html>' ) self.graph.setyV2(self.doubleSpinBoxY2.value()) self.update_graph2tabLengths() self.update_graph2phi() if self.isComputed(): self.graph.computeIntersections() self.plotScene() else: self.showError('Recomputation of coupler curve needed!') def update_graph2R26(self): blocked = self.doubleSpinBoxR26.blockSignals(True) self.doubleSpinBoxR26.setValue(self.graph.getR26()) self.doubleSpinBoxR26.blockSignals(blocked) self.update_graph2yV2() self.update_graph2theta() def update_graph2yV2(self): blocked = self.doubleSpinBoxY2.blockSignals(True) self.doubleSpinBoxY2.setValue(self.graph.getyV2()) self.doubleSpinBoxY2.blockSignals(blocked) self.update_graph2phi() def update_graph2phi(self): self.update_graph2theta() blocked = self.doubleSpinBoxPhi.blockSignals(True) self.doubleSpinBoxPhi.setValue(self.graph.getPhiDegree()) self.doubleSpinBoxPhi.blockSignals(blocked) def update_graph2theta(self): blocked = self.doubleSpinBoxTheta.blockSignals(True) self.doubleSpinBoxTheta.setValue(self.graph.getThetaDegree()) self.doubleSpinBoxTheta.blockSignals(blocked) def setRequiresRecomputing(self): self.graph.setRequiresRecomputing(propagate=False) self.labelComputed.setText( '<html><head/><body><p><span style=" color:#ff0000;">Recomputation needed</span></p></body></html>' ) self._V6fromPHC = [] self.labelRecomputePHC.setText( '<html><head/><body><p><span style=" color:#ff0000;">Recomputation needed</span></p></body></html>' ) def setComputed(self): self.graph.setComputed(propagate=False) self.labelComputed.setText('Ready') c_x, c_y, c_z = self.graph.getCenterOfGravity() self.doubleSpinBox_x.setValue(-c_x) self.doubleSpinBox_y.setValue(-c_y) self.doubleSpinBox_z.setValue(-c_z) def isComputed(self): return self.graph.isComputed() def computeCouplerCurves(self): N = self.spinBoxSamples.value() self.printLog('Computing coupler curve') self.pushButtonPlot.setEnabled(False) self.graph.computeCouplerCurve(N) self.updateParameter() self.updateDisplayedGraph() self.pushButtonPlot.setEnabled(True) def plotScene(self): c_x, c_y, c_z = self.doubleSpinBox_x.value( ), self.doubleSpinBox_y.value(), self.doubleSpinBox_z.value() def draw(points, style='black', line_width=2): self._branches_plot.plot([x + c_x for x, y, z in points], [y + c_y for x, y, z in points], [z + c_z for x, y, z in points], style, linewidth=line_width) if self.isComputed(): if not self._firstPlot: xlim = self._branches_plot.get_xlim() ylim = self._branches_plot.get_ylim() zlim = self._branches_plot.get_zlim() minbound = min([xlim[0], ylim[0], zlim[0]]) maxbound = max([xlim[1], ylim[1], zlim[1]]) else: minbound = self.graph.minbound maxbound = self.graph.maxbound self._firstPlot = False self._branches_plot.cla() if self.comboBoxGraphFor.currentText() == 'orange': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 0, 0) elif self.comboBoxGraphFor.currentText() == 'red': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 1, 0) elif self.comboBoxGraphFor.currentText() == 'green': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 0, 1) elif self.comboBoxGraphFor.currentText() == 'blue': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 1, 1) else: pos = None allOrange = True for c in self.checkBoxBranches: if self.checkBoxBranches[c].checkState(): for part in self.graph.getBranch(c): if allOrange: draw(part, 'darkorange', line_width=2) else: draw(part, c, line_width=2) if self.checkBoxMirror.checkState(): for c in self.checkBoxBranches: if self.checkBoxBranches[c].checkState(): for part in self.graph.getMirrorBranch(c): if allOrange: draw(part, 'darkorange', line_width=2) else: draw(part, 'dark' + c, line_width=2) draw(self.graph.intersections_mirror, 'ro') draw(self.graph.intersections, 'ro') if pos: v1, v2, v3, v4, v5, v6, v7 = pos draw([v2, v3, v7, v2, v1, v3, v4, v5, v1, v4, v7, v6, v5, v7]) draw([v1, v6]) # draw([v1, v2, v3], 'ko') draw(pos, 'ko') draw([v6, v6], 'y^') for i, v in enumerate(pos): self._branches_plot.text(v[0] + 0.1 + c_x, v[1] + c_y, v[2] + c_z, 'v' + str(i + 1), size=20) self._branches_plot.set_xlim3d(minbound, maxbound) self._branches_plot.set_ylim3d(minbound, maxbound) self._branches_plot.set_zlim3d(minbound, maxbound) self._branches_plot.get_proj() else: self.showError('Recomputation of coupler curve needed!') if self._V6fromPHC: draw(self._V6fromPHC, 'go') # self._branches_plot.auto_scale_xyz([minbound, maxbound], [minbound, maxbound], [minbound, maxbound]) # x, y, z = self.graph.getSphere(100) # self._branches_plot.plot_surface(x, y, z, alpha=0.2, color='grey', shade=True, linewidth=0) self._branches_plot.set_axis_off() self.canvas.draw() def printLog(self, s, verbose=0, newLine=True): # self._log += s +'\n' # self.plainTextEdit.setPlainText(self._log) if verbose <= self.verbose: if newLine: print s self.plainTextEdit.appendPlainText(str(s)) else: print s, self.plainTextEdit.insertPlainText(str(s)) self.plainTextEdit.moveCursor(QTextCursor.End) self.plainTextEdit.ensureCursorVisible() QApplication.processEvents() def setGraphSequence(self, seq, seq_comments): self.graph_sequence = seq self.graph_sequence_comments = seq_comments self.spinBoxSeqLength.setValue(len(seq)) self.spinBoxImgInSeq.setMinimum(1) self.spinBoxImgInSeq.setMaximum(len(self.graph_sequence)) self.noteToImgInSeq.setPlainText( self.graph_sequence_comments[self.spinBoxImgInSeq.value() - 1]) self.boxInfoImgInSeq.setVisible(True) self.pushButtonPlot.setEnabled(False) N = self.spinBoxSamples.value() for graph in self.graph_sequence: graph.computeCouplerCurve(N) self.plotGraphFromSequence() self.pushButtonPlot.setEnabled(True) def loadSequence(self): options = QFileDialog.Options() fileName, _ = QFileDialog.getOpenFileName( self, "Load sequence", "./sequences", "Python Objects (*.p);;All Files (*)", options=options) if fileName: try: self.printLog('Sequence loaded from: ' + fileName) seq = pickle.load(open(fileName, 'rb')) graph_sequence = [] graph_sequence_comments = [] if type(seq[0]) == list: for g in seq: lengths = { '12': np.sqrt(g[0]), '13': np.sqrt(g[1]), '14': np.sqrt(g[2]), '15': np.sqrt(g[3]), '16': np.sqrt(g[4]), '27': np.sqrt(g[7]), '37': np.sqrt(g[9]), '47': np.sqrt(g[11]), '57': np.sqrt(g[13]), '67': np.sqrt(g[14]), '23': np.sqrt(g[5]), '34': np.sqrt(g[8]), '45': np.sqrt(g[10]), '56': np.sqrt(g[12]), '26': np.sqrt(g[6]) } graph_sequence.append( GraphCouplerCurve(lengths=lengths, window=self)) try: graph_sequence_comments.append(str(g[15])) except: graph_sequence_comments.append(str(' ')) else: self.printLog(seq) for len_seq in seq[1]: for lengths in len_seq[1]: graph_sequence.append( GraphCouplerCurve(lengths=lengths, window=self)) graph_sequence_comments.append(str(' ')) self.setGraphSequence(graph_sequence, graph_sequence_comments) except Exception as e: self.showError('Some problem with loading: \n' + str(e)) def plotGraphFromSequence(self): if self.graph_sequence: self.setActiveGraph( self.graph_sequence[self.spinBoxImgInSeq.value() - 1]) self._V6fromPHC = [] self.noteToImgInSeq.setPlainText( self.graph_sequence_comments[self.spinBoxImgInSeq.value() - 1]) self.update_graph2tabLengths() self.update_graph2R26() self.updateParameter() self.updateDisplayedGraph() self.labelRecomputePHC.setText( '<html><head/><body><p><span style=" color:#ff0000;">Recomputation needed</span></p></body></html>' ) def runPHC(self): self.buttonRunPHC.setEnabled(False) self._V6fromPHC = self.graph.getSolutionsForV6(usePrev=False) num_sol = len(self._V6fromPHC) self.printLog('Number of real solutions by PHC:') self.printLog(str(num_sol)) self.spinBoxNumberReal.setValue(num_sol) self.plotScene() self.labelRecomputePHC.setText('Green points OK') self.buttonRunPHC.setEnabled(True) return num_sol def rotateVertices(self): self.buttonRotateVertices.setEnabled(False) self.printLog('Rotating labeling of vertices:') rotated_lengths = { '12': self.graph.getEdgeLength('13'), '13': self.graph.getEdgeLength('14'), '14': self.graph.getEdgeLength('15'), '15': self.graph.getEdgeLength('16'), '16': self.graph.getEdgeLength('12'), '27': self.graph.getEdgeLength('37'), '37': self.graph.getEdgeLength('47'), '47': self.graph.getEdgeLength('57'), '57': self.graph.getEdgeLength('67'), '67': self.graph.getEdgeLength('27'), '23': self.graph.getEdgeLength('34'), '34': self.graph.getEdgeLength('45'), '45': self.graph.getEdgeLength('56'), '56': self.graph.getEdgeLength('26'), '26': self.graph.getEdgeLength('23') } self.graph.setLengthsAndUpdateFixedTriangle(rotated_lengths) blocked = self.doubleSpinBoxR26.blockSignals(True) self.update_graph2R26() self.update_graph2tabLengths() self.doubleSpinBoxR26.blockSignals(blocked) self.computeCouplerCurves() self.buttonRotateVertices.setEnabled(True) def showDialog(self, texts): dialog = QDialog(self) dialog.textBrowser = QPlainTextEdit(dialog) dialog.verticalLayout = QVBoxLayout(dialog) dialog.verticalLayout.addWidget(dialog.textBrowser) dialog.setMinimumSize(600, 300) for s in texts: dialog.textBrowser.appendPlainText(str(s)) dialog.show() def exportLengths(self): len_vect = [] for e in ['12', '13', '14', '15', '16', '23']: len_vect.append(self.graph.getEdgeLength(e)) len_vect.append(self.graph.getR26()) for e in ['27', '34', '37', '45', '47', '56', '57', '67']: len_vect.append(self.graph.getEdgeLength(e)) squared = True if squared: tmp = copy.copy(len_vect) len_vect = [] for r in tmp: len_vect.append(r**2) dialog = QDialog(self) dialog.textBrowser = QPlainTextEdit(dialog) dialog.textBrowser.appendPlainText(str(len_vect)) len_dict = copy.deepcopy(self.graph._lengths) len_dict[(2, 6)] = self.graph.getR26() dialog.textBrowser.appendPlainText(str(len_dict)) dialog.verticalLayout = QVBoxLayout(dialog) dialog.verticalLayout.addWidget(dialog.textBrowser) dialog.setMinimumSize(600, 300) dialog.exec_() def axelVisualisation(self): # #colors blue = [0, 0, 255] cyan = [0, 255, 255] green = [0, 255, 0] magenta = [255, 0, 255] dark_yellow = [100, 100, 0] yellow = [255, 255, 0] dark_green = [0, 190, 0] red = [255, 0, 0] black = [0, 0, 0] colors = { 'orange': yellow, 'red': red, 'green': green, 'blue': blue, 'darkorange': dark_yellow, 'darkred': cyan, 'darkgreen': dark_green, 'darkblue': magenta } if self.isComputed(): v = VisualizationAxel("/home/jan/Programs/miniconda3/bin/axel-s") if self.comboBoxGraphFor.currentText() == 'orange': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 0, 0) elif self.comboBoxGraphFor.currentText() == 'red': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 1, 0) elif self.comboBoxGraphFor.currentText() == 'green': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 0, 1) elif self.comboBoxGraphFor.currentText() == 'blue': pos = self.graph.getPositionsOfVertices( self.spinBoxParameter.value() - 1, 0, 1, 1) else: pos = None if pos: v1, v2, v3, v4, v5, v6, v7 = pos v.add_polyline( [v2, v3, v7, v2, v1, v3, v4, v5, v1, v4, v7, v6, v5, v7], color=black) v.add_polyline([v1, v6], color=black) v.add_points([v1, v2, v3], color=black, size=1.5) v.add_points(self.graph.intersections, color=red, size=1.0) for c in self.checkBoxBranches: if self.checkBoxBranches[c].checkState(): for part in self.graph.getBranch(c): v.add_polyline(part, color=colors[c]) if self.checkBoxMirror.checkState(): v.add_points(self.graph.intersections_mirror, color=red, size=1.0) for c in self.checkBoxBranches: if self.checkBoxBranches[c].checkState(): for part in self.graph.getMirrorBranch(c): v.add_polyline(part, color=colors['dark' + c]) if self._V6fromPHC: v.add_points(self._V6fromPHC, color=dark_green, size=1.0) v.show() else: self.showError('Recomputation of coupler curve needed!') def runSamplingPhiTheta(self): self.computeCouplerCurves() n = 0 first = True while n < 48 and (not self.interrupt.checkState() or first): first = False self.printLog('Sampling phi and theta') alg = AlgRealEmbeddings( 'Max7vertices', num_phi=self.spinBoxSamplesPhi.value(), num_theta=self.spinBoxSamplesTheta.value(), choice_from_clusters='closestToAverageLength', window=self) alg.runSamplingPhiTheta( self.graph.getLengths(), self._possibleParametrizedVertices[ self.comboBoxParamVert.currentText()]) self.printLog('Sampling finished, see sequence') if not self.interrupt.checkState(): self.printLog('Rotating:') self.rotateVertices() def findMoreEmbeddings(self): self.computeCouplerCurves() self.printLog('Searching more embeddings:') alg = AlgRealEmbeddings('Max7vertices', window=self, num_phi=self.spinBoxSamplesPhi.value(), num_theta=self.spinBoxSamplesTheta.value(), name=self.lineEditName.text()) alg.findMoreEmbeddings(self.graph.getLengths()) if not self.interrupt.checkState(): self.printLog('Rotating:') self.rotateVertices() def showClusters(self, clusters, centers): pass newDialog = QDialog(self) newDialog.figure = Figure() newDialog.canvas = FigureCanvas(newDialog.figure) layout = QVBoxLayout() layout.addWidget(newDialog.canvas) newDialog.setLayout(layout) ax = newDialog.figure.add_subplot(111) for cluster in clusters: ax.plot([x for x, y in cluster], [y for x, y in cluster], 'o') ax.plot([x for x, y in centers], [y for x, y in centers], 'ro') newDialog.canvas.draw() newDialog.show()