class matplotlibWidget(QWidget): def __init__(self, parent = None): QWidget.__init__(self, parent) self.canvas = MplCanvas() self.gl = QGridLayout() alignment = Qt.Alignment() self.gl.addWidget(self.canvas,0,0,-1,-1,alignment) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # Just some button self.button1 = QPushButton('Zoom') self.button1.clicked.connect(self.zoom) self.button2 = QPushButton('Pan') self.button2.clicked.connect(self.pan) self.button3 = QPushButton('Home') self.button3.clicked.connect(self.home) self.gl.addWidget(self.toolbar,1,0,alignment) self.gl.addWidget(self.button1,1,1,alignment) self.gl.addWidget(self.button2,1,2,alignment) self.gl.addWidget(self.button3,1,3,alignment) self.setLayout(self.gl) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan()
class MyMplCanvas(FigureCanvas): """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" def __init__(self, parent=None, width=5, height=4, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) self.axes = fig.add_subplot(111) # We want the axes cleared every time plot() is called self.axes.hold(False) self.compute_initial_figure() # FigureCanvas.__init__(self, fig) self.setParent(parent) self.setStyleSheet("{background-color:transparent;border:none;}") """FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)""" FigureCanvas.updateGeometry(self) self.tootlbar = NavigationToolbar(self, parent) self.tootlbar.hide() self.fid = 0 self.data = [] self.index = [] def compute_initial_figure(self): pass
class BoxplotChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Boxplot Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() def show_dataset(self, dataset: typing.Iterable[typing.Iterable[float]], xlabels, ylabel, title=""): self.axes.clear() self.axes.boxplot(dataset, labels=xlabels) self.axes.set_ylabel(ylabel) self.axes.set_title(title) self.figure.tight_layout() self.canvas.draw()
class HierarchicalChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Hierarchical Clustering Chart")) self.figure = plt.figure(figsize=(6, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() def show_result(self, linkage_matrix: np.ndarray, p=100, **kwargs): self.axes.clear() res = dendrogram(linkage_matrix, no_labels=False, p=p, truncate_mode='lastp', show_contracted=True, ax=self.axes) self.axes.set_title( f"{self.tr('Hierarchical clustering chart')} (p={p})") self.axes.set_xlabel(self.tr("Sample count/index")) self.axes.set_ylabel(self.tr("Distance")) self.figure.tight_layout() self.canvas.draw() return res
class SliceWidget(FigureCanvas): def __init__(self, parent=None, dpi=100): # Create figure and axes, the axes should cover the entire figure size figure = Figure(dpi=dpi, frameon=False) self.axes = figure.add_axes((0, 0, 1, 1), facecolor='black') # Hide the x and y axis, we just want to see the image self.axes.get_xaxis().set_visible(True) self.axes.get_yaxis().set_visible(True) # Initialize the parent FigureCanvas FigureCanvas.__init__(self, figure) self.setParent(parent) # Set background of the widget to be close to black # The color is not made actually black so that the user can distinguish the image bounds from the figure bounds self.setStyleSheet('background-color: #FFFFFF;') # Set widget to have strong focus to receive key press events self.setFocusPolicy(Qt.StrongFocus) # Create navigation toolbar and hide it # We don't want the user to see the toolbar but we are making our own in the user interface that will call # functions from the toolbar self.toolbar = NavigationToolbar(self, self) self.toolbar.hide() # Update size policy and geometry FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self)
class MplWidget(MyBaseWidget): def __init__(self, dpi=30, parent=None): super(MplWidget, self).__init__(parent) self.sc = MplCanvas(self, width=3, height=4, dpi=dpi) # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second. self.toolbar = NavigationToolbar(self.sc, self) self.toolbar.hide() layout = QtWidgets.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.toolbar) layout.addWidget(self.sc) # self.setStyleSheet("border: 1px solid red") # pal = self.palette() # pal.setColor(QPalette.Background, QtCore.Qt.blue) # self.setAutoFillBackground(True) # self.setPalette(pal) #self.setLineWidth(0) # self.setContentsMargins(0,0,0,0) #self.setFrameStyle(QtWidgets.QFrame.Plain) self.setLayout(layout) self.sc.mpl_connect('button_press_event', self.onclick) # # The object attributes self.selected_stuff = None self.x = None self.y = None self.ploted_stuff = None def onclick(self, event): print('Click on matplotlib canvas..', event) def size(self) -> QtCore.QSize: return QtCore.QSize(100, 100) def plot(self, x, y, *args, **kwargs): self.x = x self.y = y self.ploted_stuff = self.sc.axes.plot(x, y, *args, **kwargs) self.sc.draw() def select_indices(self, idx): if self.selected_stuff is not None: for s in self.selected_stuff: self.selected_stuff.pop().remove() if not self.ploted_stuff: return self.selected_stuff = self.sc.axes.plot(self.x.iloc[idx], self.y.iloc[idx], 'ro') self.sc.draw() def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent) -> None: print('Double click on this one.')
class LocalizationPlot(FigureCanvas): def __init__(self, widget): self.widget = widget self.fig = Figure(figsize=(200, 100), dpi=100) super(LocalizationPlot, self).__init__(self.fig) self.axes = self.fig.add_subplot(111) self.axes.set(title="Localization") self.mlp_bar = NavigationToolbar(self, self.widget) self.mlp_bar.hide() self.mpl_connect("button_press_event", self.mlp_bar.pan()) self.mpl_connect("scroll_event", self.zoom) self.draw() # We only want the latest 80 plot # update and cancel the previous one def update_plot(self, data): xs = [] ys = [] for i in range(len(data)): xs.append(data[i][0]) ys.append(data[i][1]) self.axes.cla() self.axes.set(xlabel='x Axis (m)', ylabel='y Axis (m)') # # Update the right label for i in range(len(xs)): self.axes.plot(xs[i], ys[i], '.', color='#00BFFF') self.axes.set_xlim([0, 1]) self.axes.set_ylim([0, 1]) self.draw() def zoom(self, event): self.ran = [ self.axes.get_xlim()[0], self.axes.get_xlim()[1], self.axes.get_ylim()[0], self.axes.get_ylim()[1] ] if event.button == "up": self.ran[0] += 5 self.ran[1] -= 5 self.ran[2] += 5 self.ran[3] -= 5 self.axes.set_xlim(self.ran[0], self.ran[1]) self.axes.set_ylim(self.ran[2], self.ran[3]) else: self.ran[0] -= 5 self.ran[1] += 5 self.ran[2] -= 5 self.ran[3] += 5 self.axes.set_xlim(self.ran[0], self.ran[1]) self.axes.set_ylim(self.ran[2], self.ran[3]) self.draw() def getrange(self): return self.ran
class PlotWidget(QtWidgets.QWidget): def __init__(self, parent, plot_name): super().__init__(parent) self.plot_name = plot_name self.plot_manager = PlotManager.instance self.plot_thread = None self.figure = Figure(figsize=(18, 12)) # self.figure.subplots_adjust(left=0.1, right=0.9, # bottom=0.1, top=0.9, # hspace=0.3, wspace=0.3) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) main_layout = QtWidgets.QVBoxLayout(self) main_layout.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) main_layout.addWidget(self.toolbar) main_layout.addWidget(self.canvas) def start_plotting(self): self.toolbar.hide() self.plot_thread = PlotCreationThread(self, self.figure) self.plot_thread.finished.connect(self.on_plot_thread_is_done) debug_mode = False # set to 'True' in order to debug plot creation with embed if debug_mode: # synchronous plotting (runs in main thread and thus allows debugging) self.plot_thread.run() else: # asynchronous plotting (default): self.plot_thread.start( ) # start will start thread (and run), but main thread will continue immediately def is_busy_plotting(self): return self.plot_thread is not None @QtCore.pyqtSlot() def on_plot_thread_is_done(self): self.plot_thread = None self.toolbar.show() self.plot_manager.add_plot(self) def clear_figure(self): self.figure.clear() def refresh_canvas(self): self.canvas.draw() def closeEvent(self, close_event): self.plot_manager.remove_plot(self) super().closeEvent(close_event)
class EMMASummaryChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("EMMA Summary Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) self.supported_distances = ("1-norm", "2-norm", "3-norm", "4-norm", "MSE", "log10MSE", "cosine", "angular") self.distance_label = QLabel(self.tr("Distance")) self.distance_combo_box = QComboBox() self.distance_combo_box.addItems(self.supported_distances) self.distance_combo_box.setCurrentText("log10MSE") self.distance_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.distance_label, 2, 0) self.main_layout.addWidget(self.distance_combo_box, 2, 1) if not toolbar: self.toolbar.hide() self.results = [] @property def distance(self) -> str: return self.distance_combo_box.currentText() def update_chart(self): self.show_distances(self.results) def show_distances(self, results: typing.List[EMMAResult], title=""): self.results = results n_members_list = [result.n_members for result in results] distances = [result.get_distance(self.distance) for result in results] self.axes.clear() self.axes.plot(n_members_list, distances, c="black", linewidth=2.5, marker=".", ms=8, mfc="black", mew=0.0) self.axes.set_xlabel(self.tr("$N_{iterations}$")) self.axes.set_ylabel(self.tr("Distance")) self.axes.set_title(title) self.figure.tight_layout() self.canvas.draw()
class Window(QtWidgets.QDialog): def __init__(self, data, parent=None): super(Window, self).__init__(parent) self.data = data self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() self.button = QtWidgets.QPushButton('Plot') self.button.clicked.connect(self.plot) self.button1 = QtWidgets.QPushButton('Zoom') self.button1.clicked.connect(self.zoom) self.button2 = QtWidgets.QPushButton('Pan') self.button2.clicked.connect(self.pan) self.button3 = QtWidgets.QPushButton('Home') self.button3.clicked.connect(self.home) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) layout.addWidget(self.button) layout.addWidget(self.button1) layout.addWidget(self.button2) layout.addWidget(self.button3) self.setLayout(layout) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def plot(self): # PlotCanvas(self.data) data = self.data ax = self.figure.add_subplot(111) ax.hold(False) ax.set_title("Basic Distribution") ax.plot(data, 'r*-') self.canvas.draw()
class Window(QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # Just some button self.button = QPushButton('Plot') self.button.clicked.connect(self.plot) self.button1 = QPushButton('Zoom') self.button1.clicked.connect(self.zoom) self.button2 = QPushButton('Pan') self.button2.clicked.connect(self.pan) self.button3 = QPushButton('Home') self.button3.clicked.connect(self.home) # set the layout layout = QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) layout.addWidget(self.button) layout.addWidget(self.button1) layout.addWidget(self.button2) layout.addWidget(self.button3) self.setLayout(layout) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def plot(self): ''' plot some random stuff ''' data = [random.random() for i in range(25)] ax = self.figure.add_subplot(111) ax.hold(False) ax.plot(data, '*-') self.canvas.draw()
class MatplotlibWidget(Canvas): def __init__(self, parent=None): figure = Figure(figsize=(4, 3)) self.ax = figure.add_subplot(111) Canvas.__init__(self, figure) c = Canvas(Figure()) self.setParent(parent) Canvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) Canvas.updateGeometry(self) self.figure = figure self.ax.grid() self.tb = NavTB(self.figure.canvas, parent) self.tb.hide() self.marker = itertools.cycle(('s', 'v', 'd', 'o', '*', '^', '8'))
class MatplotlibWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) plt.style.use('dark_background') self.canvas = FigureCanvas(Figure()) self.toolbar = NavigationToolbar(self.canvas, self, coordinates=False) self.toolbar.hide() vertical_layout = QVBoxLayout() vertical_layout.addWidget(self.canvas) # vertical_layout.addWidget(self.toolbar) self.canvas.axes = self.canvas.figure.add_subplot(111) self.setLayout(vertical_layout)
class MplWidget(QtGui.QWidget): """Widget defined in Qt Designer""" def __init__(self, parent=None, height=300, width=300): # initialization of Qt MainWindow widget QtGui.QWidget.__init__(self, parent) # set the canvas to the Matplotlib widget self.canvas = MplCanvas() # create a vertical box layout self.vbl = QtGui.QVBoxLayout() self.vbl.setSpacing(0) self.ntb = NavigationToolbar(self.canvas, parent) self.ntb.hide() self.vbl.addWidget(self.ntb) # add mpl widget to vertical box self.vbl.addWidget(self.canvas) # set the layout to the vertical box self.setLayout(self.vbl) self.setMinimumHeight(height) self.setMinimumWidth(width)
class PCAResultChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("PCA Result Chart")) self.figure = plt.figure(figsize=(6, 4)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() def show_result(self, dataset: GrainSizeDataset, transformed: np.ndarray, pca: PCA): self.axes.clear() # X = dataset.X components = pca.components_ # X_hat = transformed @ components # MAD = np.abs(X_hat - X) n_samples, n_components = transformed.shape n_components_, n_classes = components.shape sample_indexes = np.linspace(1, n_samples, n_samples) assert n_components == n_components_ for i in range(n_components): self.axes.plot( sample_indexes, transformed[:, i], label=f"PC{i+1} ({pca.explained_variance_ratio_[i]:0.2%})") self.axes.set_xlabel(self.tr("Sample index")) self.axes.set_ylabel(self.tr("Transformed value")) self.axes.set_title(self.tr("Varations of PCs")) if n_components < 10: self.axes.legend(loc="upper left") self.figure.tight_layout() self.canvas.draw()
class DistanceCurveChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Distance History")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() def show_distance_series(self, series: typing.Iterable[float], title=""): self.axes.clear() self.axes.plot(series) self.axes.set_xlabel(self.tr("Iteration index")) self.axes.set_ylabel(self.tr("Distance")) self.axes.set_title(title) self.figure.tight_layout() self.canvas.draw()
class Form(QMainWindow, ProgressCallback): def __init__(self, ui_file, parent=None): super(Form, self).__init__(parent) ui_file = QFile(ui_file) ui_file.open(QFile.ReadOnly) self.communicate = Communicate() self.communicate.update_progress.connect(self.emit_progress) self.communicate.bundle_result.connect(self.bundle_result) self.path = None self.graph = None self.algorithm = None self.thread: Optional[Thread] = None self.stopped = False self.parameters = AntBundleParameterSettings().getValues() loader = QUiLoader() self.ui = loader.load(ui_file) ui_file.close() # Menu bar self.ui.menuFileOpen.triggered.connect(self.openFile) self.ui.btnSettings.clicked.connect(self.settings) self.ui.btnRun.clicked.connect(lambda: self.run(open_file=True)) self.ui.btnStop.clicked.connect(self.stop) # Matplotlib widget content = self.ui.widget self.canvas = FigureCanvas(Figure((6, 7))) self.figure = self.canvas.figure self.ax = self.canvas.figure.subplots() self.ax.axes.xaxis.set_visible(False) self.ax.axes.yaxis.set_visible(False) self.ax.text(0.5, 0.5, 'No graph loaded', ha='center', va='center') self.ntb = NavigationToolbar(self.canvas, self) self.ntb.hide() content_box = QVBoxLayout() content_box.addWidget(self.canvas) content_box.addWidget(self.ntb) content.setLayout(content_box) self.info: QLabel = self.ui.lblInfo self.canvas.draw() self.ui.show() def settings(self): dlg = AntBundleParameterSettings(self.parameters) if dlg.exec_() == QDialog.Accepted: self.parameters = dlg.getValues() def create_bundler(self): return AntBundleAlgorithm( self.graph, self.create_interpolator(), self.parameters.runs, self.parameters.numberOfSegments, self.parameters.decreaseByConstant, self.parameters.decreaseFactor, self.parameters.randomness, self.parameters.threshold, self.parameters.updateDistance, self.parameters.pathExponent) def create_interpolator(self): return BSplineInterpolate( max_degree=self.parameters.splineInterpolationDegree) @Slot(float, float, str) def emit_progress(self, overall, subtask_progress, subtask_name): self.ui.progressBarOverall.setValue(int(overall * 100)) self.ui.progressBarSub.setValue(int(subtask_progress * 100)) self.ui.lblSubtask.setText(subtask_name) def progress(self, overall, subtask_progress, subtask_name): # ask ui thread to handle progress update self.communicate.update_progress.emit(overall, subtask_progress, subtask_name) @Slot(BundledGraph, PheromoneField, AntBundleParameters) def bundle_result(self, bg: BundledGraph, pf: PheromoneField, parameters: AntBundleParameters): dlg = AntBundleOutputWindow(bg, pf, parameters, parent=self) dlg.show() self.ui.btnRun.setEnabled(True) self.ui.btnSettings.setEnabled(True) def bundle(self): parameters = AntBundleParameters(**self.parameters.__dict__) self.algorithm = self.create_bundler() self.algorithm.progress_callback = self bundled_graph = self.algorithm.bundle() if not self.stopped: self.communicate.bundle_result.emit(bundled_graph, self.algorithm.field, parameters) self.communicate.update_progress.emit(0, 0, "Waiting...") def run(self, open_file=True): if self.path and self.graph: self.stopped = False self.ui.btnRun.setEnabled(False) self.ui.btnSettings.setEnabled(False) self.thread = Thread(target=self.bundle) self.thread.start() elif open_file: self.openFile() self.run(open_file=False) def stop(self): if self.thread.is_alive() and self.algorithm: if easygui.ynbox('Do you want to terminate?', 'Stop request', ['Yes', 'No']): self.stopped = True self.algorithm.stop() self.ui.btnRun.setEnabled(True) self.ui.btnSettings.setEnabled(True) def draw(self): self.ntb.show() self.ax.cla() nx.draw_networkx(self.graph, GraphUtils.createPositionDict(self.graph), ax=self.ax, with_labels=False, node_size=100) self.ax.set_aspect('equal') self.canvas.draw() def openFile(self): self.path = easygui.fileopenbox() if not self.path: return try: g = nx.read_graphml(self.path) except (nx.NetworkXError, ElementTree.ParseError) as e: error_dialog = QtWidgets.QErrorMessage() error_dialog.showMessage(str(e)) error_dialog.exec_() return g, success = GraphUtils.sanitize(g) if success: self.graph = g self.setGraphInfo() self.draw() else: self.path = None def setGraphInfo(self): if not self.graph: return max_x, max_y, _ = GraphUtils.getGraphFieldShape(self.graph) node_count = len(self.graph.nodes) self.info.setText( f"#nodes: {node_count}\tdimensions: ({max_x}, {max_y})")
class window_graph(QDialog): def __init__(self,data,Fs,status,methodValues=0,deletedEpochs=0,totalepochs=0): QDialog.__init__(self); loadUi('graph.ui',self); self.setWindowIcon(QtGui.QIcon("logo.png")) self.mySignal=data self.myFs=Fs self.status=status self.methodSignal=methodValues self.deletedEpochs=deletedEpochs self.numEpochs=totalepochs self.channels, self.points = shape(self.mySignal) #self.time_array=arange(0,self.points/self.myFs,1/self.myFs) self.button_zoom.clicked.connect(self.zoom) self.button_pan.clicked.connect(self.pan) self.button_home.clicked.connect(self.home) self.figure = Figure(figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.axes=self.figure.add_subplot(111) self.toolbar=NavigationToolbar(self.canvas,self) self.toolbar.hide() layout=QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) self.field_graph.setLayout(layout) self.axes.clear() self.canvas.draw() self.plot() def plot(self): legends=['Ch 1','Ch 2','Ch 3','Ch 4','Ch 5','Ch 6','Ch 7','Ch 8'] if self.status==1: for c in range(self.channels): self.axes.plot(self.mySignal[c,::]) self.axes.legend(legends) self.axes.set_title('Señal original') if self.status==2: r=[] for c in range(self.channels): r.append(c*150) self.axes.plot(self.mySignal[c,::]+c*150) self.axes.set_yticklabels(legends) self.axes.set_yticks(r) self.axes.set_title('Señal filtrada') if self.status==3: r=[] for c in range(self.channels): r.append(c*150) self.axes.plot(self.mySignal[c,::]+c*150) self.axes.plot(self.methodSignal[c,::]+c*150,'r') self.axes.set_yticklabels(legends) self.axes.set_yticks(r) self.axes.set_title('Eliminación de épocas') msg = QMessageBox(self) msg.setIcon(QMessageBox.Information) msg.setText("Se eliminaron las épocas : "+' '.join(str(ep) for ep in self.deletedEpochs)) msg.setWindowTitle("Épocas eliminadas") msg.setInformativeText("Épocas iniciales: "+str(self.numEpochs)+'\tÉpocas restantes: '+str(self.numEpochs-len(self.deletedEpochs))) msg.show(); self.axes.set_ylabel('Voltaje [uV]') self.axes.set_xlabel('Muestras') self.axes.grid(True) self.axes.set_xlim([0,4000]) self.canvas.draw() def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan()
class Window(QMainWindow): def __init__(self, camera=None, calibration=None): #Inherit properies from QMainWindow super().__init__() #Setting geometry attributes of MainWindow self.left = 10 self.top = 10 self.width = 720 self.height = 480 self.project_num = 0 self.project_name = "Calib_" self.project_list = [] #Design GUI elements self.initUI() #Declare Camera, calibration instances self.camera = camera self.calib = calibration self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) #GUI elements design def initUI(self): # Matplotlib figure to draw image frame self.figure = plt.figure() # Window canvas to host matplotlib figure self.canvas = FigureCanvas(self.figure) # ToolBar to navigate image frame self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() #Setting Title and StatusBar self.setWindowTitle('Ứng dụng kiểm chuẩn máy ảnh số phổ thông') self.statusBar().showMessage("Trạng thái tác vụ: ") #Setting main menu mainMenu = self.menuBar() fileMenu = mainMenu.addMenu('Thao tác tệp tin') toolsMenu = mainMenu.addMenu('Xử lý dữ liệu') viewMenu = mainMenu.addMenu('Xem thông tin') editMenu = mainMenu.addMenu('Biên tập') searchMenu = mainMenu.addMenu('Tìm kiếm') helpMenu = mainMenu.addMenu('Trợ giúp') #Setting fileMenu fileMenu.addAction('Thiết lập dự án mới', self.on_file_setnewproject) fileMenu.addAction('Mở kho ảnh', self.on_open_imagestore) #Setting ToolsMenu toolsMenu.addAction('Tiền xử lý ảnh', self.on_preprocessing_image) toolsMenu.addAction('Kiểm chuẩn máy ảnh', self.on_calibrate_image) #Setting viewMenu viewMenu.addAction('Thông tin dự án', self.on_view_project_info) viewMenu.addAction('Dữ liệu kiểm chuẩn', self.on_view_calib_info) # Create central Widget self.central_widget = QWidget() # Just some button self.button = QPushButton('Chụp ảnh', self.central_widget) self.button1 = QPushButton('Phóng ảnh', self.central_widget) self.button2 = QPushButton('Điều hướng ảnh', self.central_widget) self.button3 = QPushButton('Về gốc', self.central_widget) #Put Buttons to HBoxLayout hBox = QHBoxLayout() hBox.addWidget(self.button) hBox.addWidget(self.button1) hBox.addWidget(self.button2) hBox.addWidget(self.button3) # set QVBoxLayout as the layout self.layout = QVBoxLayout(self.central_widget) self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas) #Add hBoxLayout to VBoxLayout self.layout.addLayout(hBox) self.textedit = QTextEdit() font = QtGui.QFont() font.setPointSize(9) self.textedit.setFont(font) self.layout.addWidget(self.textedit) #Set central widget self.setCentralWidget(self.central_widget) # Events handling self.button.clicked.connect(self.plot) self.button1.clicked.connect(self.zoom) self.button2.clicked.connect(self.pan) self.button3.clicked.connect(self.home) #Events processing methods # Open image store @QtCore.pyqtSlot() def on_open_imagestore(self): #extract file name in a directory self.statusBar().showMessage( "Chương trình đang xử lý tác vụ: Mở kho ảnh") filepath = QFileDialog().getExistingDirectory( self, "Chọn đường dẫn tới kho ảnh") if filepath: images = Path(filepath).glob('*.jpg') imagelist = [str(image) for image in images] self.statusBar().showMessage(str("Kho ảnh nằm tại: ") + filepath) # check filename list and display first image if len(imagelist) > 2: #self.calib.imagelist = imagelist if self.project_num == 0: self.project_num += 1 self.calib.imagelist = imagelist self.calib.project_name = str("Calib_") + str( self.project_num) else: self.calib.project_name = str("Calib_") + str( self.project_num) self.project_list.append(self.calib) self.project_num += 1 newcalib = Calibration() newcalib.project_num = self.project_num newcalib.imagelist = imagelist self.calib = newcalib img = cv2.imread(imagelist[0]) pic = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(pic) self.canvas.draw() else: self.statusBar().showMessage( "Không có ảnh trong thư mục được chọn!") else: self.statusBar().showMessage(filepath) #Setting info for new project @QtCore.pyqtSlot() def on_file_setnewproject(self): self.statusBar().showMessage( "Chương trình đang xử lý tác vụ: Thiết lập tham số cho dự án mới") #Preprocessing images of current project @QtCore.pyqtSlot() def on_preprocessing_image(self): if len(self.calib.imagelist) > 0: self.calib.refresh() self.statusBar().showMessage( "Chương trình đang xử lý tác vụ: Tiền xử lý ảnh - ") #self.textedit.clear() self.textedit.append("") self.textedit.append("***********************************") #self.textedit.append("") self.textedit.append( str("Tiền xử lý ảnh dự án Calib_") + str(self.project_num) + str(":")) self.thread_image_preprocessing = Calib_Preprocess_Thread( self.calib) self.thread_image_preprocessing.sig1.connect( self.on_image_preprocessing_display) self.thread_image_preprocessing.sig2.connect( self.on_image_preprocessed_display) self.thread_image_preprocessing.sig3.connect( self.on_image_preprocessed_result) self.thread_image_preprocessing.start() #Calibrate current project @QtCore.pyqtSlot() def on_calibrate_image(self): if self.calib.preprocessing_done == True: # Calibrate if calib has not done if self.calib.calibrated == False: self.thread_calib = Calib_Calibrate_Thread(self.calib) self.thread_calib.sig1.connect(self.on_calibtation_processed) self.thread_calib.start() @QtCore.pyqtSlot() def on_view_project_info(self): pass @QtCore.pyqtSlot() def on_view_calib_info(self): calib_list = [] if len(self.project_list) > 0: for calib in self.project_list: clone_calib = calib.clone() calib_list.append(clone_calib) if self.calib.calibrated == True: clone_calib = self.calib.clone() clone_calib.project_name = str("Calib_") + str(self.project_num) calib_list.append(clone_calib) if len(calib_list) > 0: self.calib_info = Total_Calib_Info(calib_list) self.calib_info.show() else: msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText("Chưa có dự án kiểm chuẩn được thực hiện hoàn chỉnh!") msg.setWindowTitle("Thông báo") msg.exec_() def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom(0.5) def pan(self): self.toolbar.pan() def plot(self): #Moving Camera class to a thread and emit signal from that thread self.thread1 = Frame_Capture_Thread(self.camera) self.thread1.sig1.connect(self.on_frame) self.thread1.start() def on_frame(self, frame): plt.imshow(frame) self.canvas.draw() #Update current image name being processed: fname is image name obtained from signal sig1 of Calib_Preprocess_Thread def on_image_preprocessing_display(self, fname): filename = fname.split('\\')[-1] filename_message = str( "Chương trình đang xử lý tác vụ: Tiền xử lý ảnh - đang xử lý file ảnh: " ) + filename self.statusBar().showMessage(filename_message) self.textedit.append( str("Tiền xử lý ảnh - đang xử lý file ảnh: ") + str(filename) + str("...")) #Update last processed image on display : pic is image data obtained from signal sig2 of Calib_Preprocess_Thread def on_image_preprocessed_display(self, pic): plt.imshow(pic) self.canvas.draw() #Update preprocessing result - def on_image_preprocessed_result(self, result): redColor = QtGui.QColor(255, 0, 0) blackColor = QtGui.QColor(0, 0, 0) ret = result[0] fname = result[1] filename = fname.split('\\')[-1] if ret == True: self.textedit.setTextColor(blackColor) msg = str("Xử lý xong ảnh ") + filename + str( " - ảnh hợp lệ cho kiểm chuẩn!") self.textedit.append(msg) else: self.textedit.setTextColor(redColor) msg = str("Xử lý xong ảnh ") + filename + str( " - ảnh không hợp lệ cho kiểm chuẩn!") self.textedit.append(msg) self.textedit.setTextColor(blackColor) if self.calib.preprocessing_done == True: msg = str("Tổng số ảnh tiền xử lý: ") + str( self.calib.processed_count) self.textedit.append(msg) msg = str("Số ảnh hợp lệ cho kiểm chuẩn: ") + str( self.calib.validated_count) self.textedit.append(msg) msg = str("Số ảnh không hợp lệ cho kiểm chuẩn: ") + str( self.calib.processed_count - self.calib.validated_count) self.textedit.append(msg) self.statusBar().showMessage( "Hoàn thành tác vụ tiền xử lý ảnh - dự án Calib_" + str(self.project_num)) def on_calibtation_processed(self): self.calib.project_name = str("Calib_") + str(self.project_num) self.calib_info_window = Calib_Info(self.calib) self.calib_info_window.show()
class SSUTypicalComponentChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("SSU Typical Component Chart")) self.figure = plt.figure(figsize=(6, 3)) self.clustering_axes = self.figure.add_subplot(1, 2, 1) self.component_axes = self.figure.add_subplot(1, 2, 2) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 4) self.main_layout.addWidget(self.canvas, 1, 0, 1, 4) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.AXIS_LIST = [ self.tr("Mean [φ]"), self.tr("Standard deviation [φ]"), self.tr("Skewness"), self.tr("Kurtosis") ] self.x_axis_label = QLabel(self.tr("X Axis")) self.x_axis_combo_box = QComboBox() self.x_axis_combo_box.addItems(self.AXIS_LIST) self.y_axis_label = QLabel(self.tr("Y Axis")) self.y_axis_combo_box = QComboBox() self.y_axis_combo_box.addItems(self.AXIS_LIST) self.y_axis_combo_box.setCurrentIndex(1) self.main_layout.addWidget(self.x_axis_label, 2, 0) self.main_layout.addWidget(self.x_axis_combo_box, 2, 1) self.main_layout.addWidget(self.y_axis_label, 3, 0) self.main_layout.addWidget(self.y_axis_combo_box, 3, 1) self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems( [name for key, name in self.supported_scales]) self.main_layout.addWidget(self.scale_label, 2, 2) self.main_layout.addWidget(self.scale_combo_box, 2, 3) self.min_samples_label = QLabel(self.tr("Minimum Samples")) self.min_samples_input = QDoubleSpinBox() self.min_samples_input.setRange(0.0001, 0.9999) self.min_samples_input.setDecimals(4) self.min_samples_input.setSingleStep(0.001) self.min_samples_input.setValue(0.03) self.min_cluster_size_label = QLabel(self.tr("Minimum Cluster Size")) self.min_cluster_size_input = QDoubleSpinBox() self.min_cluster_size_input.setRange(0.0001, 0.9999) self.min_cluster_size_input.setDecimals(4) self.min_cluster_size_input.setSingleStep(0.001) self.min_cluster_size_input.setValue(0.1) self.xi_label = QLabel(self.tr("xi")) self.xi_input = QDoubleSpinBox() self.xi_input.setRange(0.0001, 0.9999) self.xi_input.setDecimals(4) self.xi_input.setSingleStep(0.001) self.xi_input.setValue(0.05) self.main_layout.addWidget(self.min_samples_label, 3, 2) self.main_layout.addWidget(self.min_samples_input, 3, 3) self.main_layout.addWidget(self.min_cluster_size_label, 4, 0) self.main_layout.addWidget(self.min_cluster_size_input, 4, 1) self.main_layout.addWidget(self.xi_label, 4, 2) self.main_layout.addWidget(self.xi_input, 4, 3) self.update_chart_button = QPushButton(self.tr("Update Chart")) self.update_chart_button.clicked.connect(self.update_chart) self.save_typical_button = QPushButton(self.tr("Save Typical")) self.save_typical_button.setEnabled(False) self.save_typical_button.clicked.connect(self.on_save_clicked) self.main_layout.addWidget(self.update_chart_button, 5, 0, 1, 2) self.main_layout.addWidget(self.save_typical_button, 5, 2, 1, 2) self.last_results = None # type: list[SSUResult] self.data_to_clustering = None self.stacked_components = None self.normal_msg = QMessageBox(self) self.file_dialog = QFileDialog(parent=self) @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False def show_message(self, title: str, message: str): self.normal_msg.setWindowTitle(title) self.normal_msg.setText(message) self.normal_msg.exec_() def show_info(self, message: str): self.show_message(self.tr("Info"), message) def show_warning(self, message: str): self.show_message(self.tr("Warning"), message) def show_error(self, message: str): self.show_message(self.tr("Error"), message) def update_chart(self): if self.last_results is None: return x = self.transfer(self.last_results[0].classes_φ) self.save_typical_button.setEnabled(True) cluster = OPTICS(min_samples=self.min_samples_input.value(), min_cluster_size=self.min_cluster_size_input.value(), xi=self.xi_input.value()) flags = cluster.fit_predict(self.data_to_clustering) cmap = plt.get_cmap() self.clustering_axes.clear() flag_set = set(flags) for flag in flag_set: key = np.equal(flags, flag) if flag == -1: c = "#7a7374" label = self.tr("Not clustered") else: c = cmap(flag) label = self.tr("EM{0}").format(flag + 1) self.clustering_axes.plot(self.data_to_clustering[key] [:, self.x_axis_combo_box.currentIndex()], self.data_to_clustering[key] [:, self.y_axis_combo_box.currentIndex()], c="#ffffff00", marker=".", ms=8, mfc=c, mew=0.0, zorder=flag, label=label) if len(flag_set) < 6: self.clustering_axes.legend(loc="upper left") self.clustering_axes.set_xlabel(self.x_axis_combo_box.currentText()) self.clustering_axes.set_ylabel(self.y_axis_combo_box.currentText()) self.clustering_axes.set_title(self.tr("Clustering of end-members")) self.component_axes.clear() if self.xlog: self.component_axes.set_xscale("log") for flag in flag_set: if flag == -1: c = "#7a7374" else: c = cmap(flag) key = np.equal(flags, flag) for distribution in self.stacked_components[key]: self.component_axes.plot(x, distribution, c=c, zorder=flag) if flag != -1: typical = np.mean(self.stacked_components[key], axis=0) self.component_axes.plot(x, typical, c="black", zorder=1e10, ls="--", linewidth=1) self.component_axes.set_title(self.tr("Typical end-members")) self.component_axes.set_xlabel(self.xlabel) self.component_axes.set_ylabel(self.ylabel) self.component_axes.set_xlim(x[0], x[-1]) self.component_axes.set_ylim(0, None) self.figure.tight_layout() self.canvas.draw() def show_typical(self, results: typing.Iterable[GrainSizeSample]): if len(results) == 0: return keys_to_clustering = ["mean", "std", "skewness", "kurtosis"] data_to_clustering = [] stacked_components = [] for result in results: for component in result.components: has_nan = False for key in keys_to_clustering: if np.isnan( component.logarithmic_moments[key]) or np.isinf( component.logarithmic_moments[key]): has_nan = True break if has_nan: continue data_to_clustering.append([ component.logarithmic_moments[key] for key in keys_to_clustering ]) stacked_components.append(component.distribution) # convert to numpy array data_to_clustering = np.array(data_to_clustering) stacked_components = np.array(stacked_components) self.last_results = results self.data_to_clustering = data_to_clustering self.stacked_components = stacked_components self.update_chart() def save_typical(self, filename): assert self.last_results is not None if len(self.last_results) == 0: return cluster = OPTICS(min_samples=self.min_samples_input.value(), min_cluster_size=self.min_cluster_size_input.value(), xi=self.xi_input.value()) classes_μm = self.last_results[0].classes_μm flags = cluster.fit_predict(self.data_to_clustering) flag_set = set(flags) typicals = [] for flag in flag_set: if flag != -1: key = np.equal(flags, flag) typical = np.mean(self.stacked_components[key], axis=0) typicals.append(typical) wb = openpyxl.Workbook() prepare_styles(wb) ws = wb.active ws.title = self.tr("README") description = \ """ This Excel file was generated by QGrain ({0}). Please cite: Liu, Y., Liu, X., Sun, Y., 2021. QGrain: An open-source and easy-to-use software for the comprehensive analysis of grain size distributions. Sedimentary Geology 423, 105980. https://doi.org/10.1016/j.sedgeo.2021.105980 It contanins 2 + N_clusters sheets: 1. The first sheet is the sum distributions of all component clusters. 2. The second sheet is used to put the component distributions that not in any cluster. 3. The left sheet is the component distributions of each cluster, separately. The clustering algorithm is OPTICS, implemented by scikit-learn. https://scikit-learn.org/stable/modules/generated/sklearn.cluster.OPTICS.html Clustering algorithm details min_samples={1} min_cluster_size={2} xi={3} others=default """.format(QGRAIN_VERSION, self.min_samples_input.value(), self.min_cluster_size_input.value(), self.xi_input.value()) def write(row, col, value, style="normal_light"): cell = ws.cell(row + 1, col + 1, value=value) cell.style = style lines_of_desc = description.split("\n") for row, line in enumerate(lines_of_desc): write(row, 0, line, style="description") ws.column_dimensions[column_to_char(0)].width = 200 ws = wb.create_sheet(self.tr("Typical Components")) write(0, 0, self.tr("Typical Component"), style="header") ws.column_dimensions[column_to_char(0)].width = 16 for col, value in enumerate(classes_μm, 1): write(0, col, value, style="header") ws.column_dimensions[column_to_char(col)].width = 10 for row, distribution in enumerate(typicals, 1): if row % 2 == 0: style = "normal_dark" else: style = "normal_light" write(row, 0, self.tr("Component{0}").format(row), style=style) for col, value in enumerate(distribution, 1): write(row, col, value, style=style) QCoreApplication.processEvents() for flag in flag_set: if flag == -1: ws = wb.create_sheet(self.tr("Not Clustered"), 2) else: ws = wb.create_sheet(self.tr("Cluster{0}").format(flag + 1)) write(0, 0, self.tr("Index"), style="header") ws.column_dimensions[column_to_char(0)].width = 16 for col, value in enumerate(classes_μm, 1): write(0, col, value, style="header") ws.column_dimensions[column_to_char(col)].width = 10 key = np.equal(flags, flag) for row, component in enumerate(self.stacked_components[key], 1): if row % 2 == 0: style = "normal_dark" else: style = "normal_light" write(row, 0, str(row), style=style) for col, value in enumerate(component, 1): write(row, col, value, style=style) QCoreApplication.processEvents() wb.save(filename) wb.close() def on_save_clicked(self): if len(self.last_results) == 0: self.show_warning(self.tr("There is not an SSU result.")) return filename, _ = self.file_dialog.getSaveFileName( self, self. tr("Choose a filename to save the typical components of SSU results" ), None, f"{self.tr('Microsoft Excel')} (*.xlsx)") if filename is None or filename == "": return try: self.save_typical(filename) self.show_info(self.tr("The typical components have been saved.")) except Exception as e: self.show_error( self.tr("Error raised while saving it to Excel file.\n {0}" ).format(e.__str__())) return
class Window(QWidget): #Creates an init method. Constructor def __init__(self): #Super returns the parent object of the class. #Calls the constructor of the QWidget super(Window, self).__init__() self.initUI() def initUI(self): #Titles and subtitles test self.titleTest = QLabel('<b>TEST</b>') self.titleTest.setFont(QFont('SansSerif', 14)) self.subtitleTest = QLabel('Test selection:') #Dropdown test directorytestFiles = sorted(os.listdir()) self.dropDownTest = QComboBox() for index in directorytestFiles: if index.find('.py') == -1: self.dropDownTest.addItem(index) self.dropDownTest.activated[str].connect( self.open_file) #Connects the user's choice in the dropdown menu self.dropDownTest.setToolTip('List over Testfiles') #Textbox self.text = QTextEdit(self) self.text.hide() #SaveButton self.saveButton = QPushButton('Save') self.saveButton.setToolTip('Press to save the changes') self.saveButton.clicked.connect(self.save_text) #RunButton self.runButton = QPushButton('Run') self.runButton.setToolTip('Press to run the testfile') self.runButton.clicked.connect(self.run_test) #Titles and subtitles analyse self.titleAnalyse = QLabel('<b>ANALYSIS</b>') self.titleAnalyse.setFont(QFont('SansSerif', 14)) self.subtitleAnalyse = QLabel('Load test data:') #FFT button self.fftButton = QCheckBox('FFT') self.fftButton.setToolTip('Fast Fourier Transform') #Dropdown analyse self.dropDownAnalyse = QComboBox() #Adds the files in the directory to a list directoryFiles = sorted(os.listdir()) #Adds the text files in the directory to the drop down menu in alphabetical order for index in directoryFiles: #Ensures that only text files are displayed as options if index.find('.txt') != -1: self.dropDownAnalyse.addItem(index) self.dropDownAnalyse.activated[str].connect(self.plot) #For the graph. The figure is where the data is plotted and the canvas is where the figure is displayed. self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() #Layout #Creates a vertical box layout v1Layout = QVBoxLayout() #Creates horisontal box layouts h1Layout = QHBoxLayout() h2Layout = QHBoxLayout() h3Layout = QHBoxLayout() #Adds the widget for the test title to the vertical box layout v1Layout.addWidget(self.titleTest) #Adds widgets to the first horisontal layout h1Layout.addWidget(self.subtitleTest) h1Layout.addWidget(self.dropDownTest) #Pushes the widgets to the left corner h1Layout.addStretch(1) #Adds the first horisontal layout to the vertical layout v1Layout.addLayout(h1Layout) v1Layout.addWidget(self.text) h2Layout.addWidget(self.saveButton) h2Layout.addWidget(self.runButton) h2Layout.addStretch(1) v1Layout.addLayout(h2Layout) v1Layout.addWidget(self.titleAnalyse) h3Layout.addWidget(self.subtitleAnalyse) h3Layout.addWidget(self.dropDownAnalyse) h3Layout.addWidget(self.fftButton) h3Layout.addStretch(1) v1Layout.addLayout(h3Layout) #Layout for the graph v1Layout.addWidget(self.toolbar) v1Layout.addWidget(self.canvas) #Sets the font for the Tooltip elements QToolTip.setFont(QFont('SansSerif', 10)) self.setLayout(v1Layout) self.setWindowTitle('MIDAS') #Shows the window self.show() #Function for dropdownbox def open_file(self, fileName): textFile = open(fileName).read() self.text.setText(textFile) self.text.show() #Lets the user save a document in a chosen folder def save_text(self): #Accesses the file dialog filename = QFileDialog.getSaveFileName(self, 'Save file', os.getenv('HOME')) with open(filename[0], 'w') as f: my_text = self.text.toPlainText() f.write(my_text) def run_test(self): pass #Plots the data (of the file passed as an argument) as a graph def plot(self, fileName): self.figure.clear() #Creates lists for the x and y coordinates xList = [] yList = [] #Opens and reads the file with the name passed as an argument to the function text_file = open(fileName, "r") lines = text_file.readlines() #Adds the data for the x and y coordinates to the corresponding lists for line in lines: x, y = line.split(',') x = float(x) y = float(y) xList.append(x) yList.append(y) text_file.close() #Sets the label of the y axis plt.ylabel('Voltage(V)') #Plots line at y = 0 plt.axhline(0, color='black', linewidth=0.5) self.fftButton.stateChanged.connect(self.fft_function) #Sets the label of the x axis and plots either the "regular" graph or the graph after FFT if self.fftButton.isChecked(): plt.xlabel('Frequency (Hz)') plt.plot(xList, fft(yList)) else: plt.xlabel('Time (s)') plt.plot(xList, yList) plt.tight_layout() self.canvas.draw() def fft_function(self, state): pass """
class SliceWidget(FigureCanvas): def __init__(self, parent=None, dpi=100): # Create figure and axes, the axes should cover the entire figure size figure = Figure(dpi=dpi, frameon=False) self.axes = figure.add_axes((0, 0, 1, 1), facecolor='black') # Hide the x and y axis, we just want to see the image self.axes.get_xaxis().set_visible(False) self.axes.get_yaxis().set_visible(False) # Initialize the parent FigureCanvas FigureCanvas.__init__(self, figure) self.setParent(parent) # Set background of the widget to be close to black # The color is not made actually black so that the user can distinguish the image bounds from the figure bounds self.setStyleSheet('background-color: #222222;') # Set widget to have strong focus to receive key press events self.setFocusPolicy(Qt.StrongFocus) # Create navigation toolbar and hide it # We don't want the user to see the toolbar but we are making our own in the user interface that will call # functions from the toolbar self.toolbar = NavigationToolbar(self, self) self.toolbar.hide() # Update size policy and geometry FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self.isLPS = False self.isLAS = False self.image = None self.sliceNumber = 0 self.diaphragmAxial = None self.umbilicisInferior = None self.umbilicisSuperior = None self.umbilicisLeft = None self.umbilicisRight = None self.umbilicisCoronal = None self.CATLine = None self.leftArmBounds = None self.rightArmBounds = None def updateFigure(self): # Clear the axes self.axes.cla() # Draw the image if it is set if self.image is not None: image = self.image[self.sliceNumber, :, :] # For viewing, we use right-anterior-superior (RAS) system. This is the same system that PATS uses. # For LPS volumes, we reverse the x/y axes to go from LPS to RAS. # Also, TTU data is in LAS which is odd but handle that too if self.isLPS: image = image[::-1, ::-1] elif self.isLAS: image = image[:, ::-1] self.axes.imshow(image, cmap='gray', origin='lower') # Draw rectangle on the diaphragm slice if self.sliceNumber == self.diaphragmAxial: self.axes.add_patch(patches.Rectangle((0, 0), 20, 20, color='purple')) # Draw a line where the umbilicis is set to be if self.umbilicisInferior is not None and self.umbilicisSuperior is not None and \ self.umbilicisCoronal is not None and self.umbilicisLeft is not None and \ self.umbilicisRight is not None: if self.umbilicisInferior <= self.sliceNumber <= self.umbilicisSuperior: x = self.umbilicisLeft y = self.umbilicisCoronal width = self.umbilicisRight - x height = 1 self.axes.add_patch(patches.Rectangle((x, y), width, height, color='orange')) # Draw lines for the CAT bounding box configuration if self.CATLine is not None and len(self.CATLine) > 1: startIndex = next((i for i, x in enumerate(self.CATLine) if min(x) != -1), None) CATLine = self.CATLine[startIndex:] if len(CATLine) > 1 and CATLine[0][0] <= self.sliceNumber <= CATLine[-1][0]: posterior = int(np.round(np.interp(self.sliceNumber, np.array([i[0] for i in CATLine]), np.array([i[1] for i in CATLine])))) anterior = int(np.round(np.interp(self.sliceNumber, np.array([i[0] for i in CATLine]), np.array([i[2] for i in CATLine])))) x = self.image.shape[2] // 2.5 y = posterior width = 75 height = 1 self.axes.add_patch(patches.Rectangle((x, y), width, height, color='red')) y = anterior self.axes.add_patch(patches.Rectangle((x, y), width, height, color='red')) # Draw a line for the left arm bounds at the current slice # Only draw if current slice is between smallest and largest bounds if self.leftArmBounds is not None and len(self.leftArmBounds) > 0 and \ (self.leftArmBounds[0][-1] <= self.sliceNumber <= self.leftArmBounds[-1][-1]): # Get a list of slice numbers for the left arm bounds xp = np.array([i[4] for i in self.leftArmBounds]) # Get list of x/y coordinates at the slice numbers x1p, y1p = np.array([i[0] for i in self.leftArmBounds]), np.array([i[1] for i in self.leftArmBounds]) x2p, y2p = np.array([i[2] for i in self.leftArmBounds]), np.array([i[3] for i in self.leftArmBounds]) # Interpolate for given slice between the bounds, round and convert to an integer x1, y1 = int(np.round(np.interp(self.sliceNumber, xp, x1p))), \ int(np.round(np.interp(self.sliceNumber, xp, y1p))) x2, y2 = int(np.round(np.interp(self.sliceNumber, xp, x2p))), \ int(np.round(np.interp(self.sliceNumber, xp, y2p))) self.axes.plot([x1, x2], [y1, y2], 'g', lw=2.0) # Draw a line for the right arm bounds at the current slice # Only draw if current slice is between smallest and largest bounds if self.rightArmBounds is not None and len(self.rightArmBounds) > 0 and \ (self.rightArmBounds[0][-1] <= self.sliceNumber <= self.rightArmBounds[-1][-1]): # Get a list of slice numbers for the left arm bounds xp = np.array([i[4] for i in self.rightArmBounds]) # Get list of x/y coordinates at the slice numbers x1p, y1p = np.array([i[0] for i in self.rightArmBounds]), np.array([i[1] for i in self.rightArmBounds]) x2p, y2p = np.array([i[2] for i in self.rightArmBounds]), np.array([i[3] for i in self.rightArmBounds]) # Interpolate for given slice between the bounds, round and convert to an integer x1, y1 = int(np.round(np.interp(self.sliceNumber, xp, x1p))), \ int(np.round(np.interp(self.sliceNumber, xp, y1p))) x2, y2 = int(np.round(np.interp(self.sliceNumber, xp, x2p))), \ int(np.round(np.interp(self.sliceNumber, xp, y2p))) self.axes.plot([x1, x2], [y1, y2], 'g', lw=1.5) # Draw the figure now self.draw()
class MixedDistributionChart(QDialog): def __init__(self, parent=None, show_mode=True, toolbar=False, use_animation=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Mixed Distribution Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems([name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.scale_label, 2, 0) self.main_layout.addWidget(self.scale_combo_box, 2, 1) self.interval_label = QLabel(self.tr("Interval [ms]")) self.interval_input = QSpinBox() self.interval_input.setRange(0, 10000) self.interval_input.setValue(30) self.interval_input.valueChanged.connect(self.update_animation) self.main_layout.addWidget(self.interval_label, 3, 0) self.main_layout.addWidget(self.interval_input, 3, 1) self.repeat_check_box = QCheckBox(self.tr("Repeat")) self.repeat_check_box.setChecked(False) self.repeat_check_box.stateChanged.connect(self.update_animation) self.save_button = QPushButton(self.tr("Save")) self.save_button.clicked.connect(self.save_animation) self.main_layout.addWidget(self.repeat_check_box, 4, 0) self.main_layout.addWidget(self.save_button, 4, 1) self.show_mode = show_mode self.animation = None self.last_model = None self.last_result = None if not use_animation: self.interval_label.setVisible(False) self.interval_input.setVisible(False) self.repeat_check_box.setVisible(False) self.save_button.setVisible(False) self.normal_msg = QMessageBox(parent=self) self.file_dialog = QFileDialog(parent=self) @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False @property def interval(self) -> float: return self.interval_input.value() @property def repeat(self) -> bool: return self.repeat_check_box.isChecked() def show_demo(self): demo_model = get_demo_view_model() self.show_model(demo_model) def update_chart(self): if self.last_model is not None: self.show_model(self.last_model) elif self.last_result is not None: self.show_result(self.last_result) def update_animation(self): if self.last_result is not None: self.show_result(self.last_result) def show_model(self, model: SSUViewModel, quick=False): if self.animation is not None: self.animation._stop() self.animation = None if not quick: self.last_result = None self.last_model = model self.interval_label.setEnabled(False) self.interval_input.setEnabled(False) self.repeat_check_box.setEnabled(False) self.save_button.setEnabled(False) self.axes.clear() x = self.transfer(model.classes_φ) if self.xlog: self.axes.set_xscale("log") self.axes.set_title(model.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, model.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0, label=self.tr("Target"))[0] # scatter can not be modified from the tool bar # self.target = self.axes.scatter(x, model.target, c="black", s=1) self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(model.target)*1.2, 2)) self.mixed = self.axes.plot(x, model.mixed, c="black", label=self.tr("Mixed"))[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i), label=model.component_prefix+str(i+1))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] colors = [plt.get_cmap()(i) for i in range(model.n_components)] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) if model.n_components < 6: self.axes.legend(loc="upper left") self.figure.tight_layout() self.canvas.draw() else: self.mixed.set_ydata(model.mixed) for comp, distribution, fraction in zip(self.components, model.distributions, model.fractions): comp.set_ydata(distribution*fraction) if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines.set_offsets(modes) self.canvas.draw() def show_result(self, result: SSUResult): if self.animation is not None: self.animation._stop() self.animation = None self.last_model = None self.last_result = result self.interval_label.setEnabled(True) self.interval_input.setEnabled(True) self.repeat_check_box.setEnabled(True) self.save_button.setEnabled(True) models = iter(result.view_models) first = next(models) x = self.transfer(first.classes_φ) self.axes.cla() if self.xlog: self.axes.set_xscale("log") self.axes.set_title(first.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, first.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0)[0] self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(first.target)*1.2, 2)) self.figure.tight_layout() # self.canvas.draw() colors = [plt.get_cmap()(i) for i in range(first.n_components)] def init(): model = first self.mixed = self.axes.plot(x, model.mixed, c="black")[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components def animate(current): model = current self.mixed.set_ydata(current.mixed) for line, distribution, fraction in zip(self.components, model.distributions, model.fractions): line.set_ydata(distribution*fraction) if self.show_mode: self.vlines.remove() modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components self.animation = FuncAnimation(self.figure, animate, frames=models, init_func=init, interval=self.interval, blit=True, repeat=self.repeat, repeat_delay=3.0, save_count=result.n_iterations) def save_animation(self): if self.last_result is not None: filename, format_str = self.file_dialog.getSaveFileName(self, self.tr("Save the animation of this SSU result"), None, self.tr("MPEG-4 Video File (*.mp4);;Graphics Interchange Format (*.gif)")) if filename is None or filename == "": return progress = QProgressDialog(self) progress.setRange(0, 100) progress.setLabelText(self.tr("Saving Animation [{0} Frames]").format(self.last_result.n_iterations)) canceled = False def save_callback(i, n): if progress.wasCanceled(): nonlocal canceled canceled = True raise StopIteration() progress.setValue((i+1)/n*100) QCoreApplication.processEvents() self.show_result(self.last_result) # plt.rcParams["savefig.dpi"] = 120.0 if "*.gif" in format_str: if not ImageMagickWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("ImageMagick is not installed, please download and install it from its offical website (https://imagemagick.org/index.php).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="imagemagick", fps=30, progress_callback=save_callback) elif "*.mp4" in format_str: if not FFMpegWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("FFMpeg is not installed, please download and install it from its offical website (https://ffmpeg.org/).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="ffmpeg", fps=30, progress_callback=save_callback) # plt.rcParams["savefig.dpi"] = 300.0 if not canceled: progress.setValue(100)
class spectralPattern_window(QDialog): def __init__(self,pxx,frequencies,data,Fs): QDialog.__init__(self); loadUi('graph_spectral.ui',self); self.setWindowIcon(QtGui.QIcon("logo.png")) self.label_title.setText('Eliminación de artefactos por espectro de potencia') self.label_maxValue.setText('Valor máximo [potencia]') self.data=data self.Fs=Fs self.channels,self.points=shape(self.data) self.epochs(self.data, self.channels, self.points) self.pxx=pxx self.frequencies=frequencies self.button_zoom.clicked.connect(self.zoom) self.button_pan.clicked.connect(self.pan) self.button_home.clicked.connect(self.home) self.button_apply.clicked.connect(self.implementation) self.button_cancel.clicked.connect(self.cancelar) self.figure = Figure(figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.axes=self.figure.add_subplot(111) self.toolbar=NavigationToolbar(self.canvas,self) self.toolbar.hide() layout=QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) self.field_graph.setLayout(layout) self.axes.clear() self.canvas.draw() self.plot() def cancelar(self): spectralPattern_window.close(self); def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def plot(self): legends=['Ch 1','Ch 2','Ch 3','Ch 4','Ch 5','Ch 6','Ch 7','Ch 8'] r=[] for c in range(shape(self.pxx)[0]): r.append(c*35) self.axes.plot(self.frequencies[c,::],sqrt(self.pxx[c,::])+c*35,linewidth=0.5) self.axes.set_yticklabels(legends) self.axes.set_yticks(r) self.axes.grid(True) self.axes.set_xlim([0,40]) self.axes.set_xlabel('Frecuency [Hz]') self.axes.set_ylabel('Power density [V^2/Hz]') self.axes.set_title('Periodograma de Welch') def epochs(self,data,channels,points): self.filtered_signal=data self.size_epoch=self.Fs*2 self.num_epoch=points/self.size_epoch delete=points%self.size_epoch self.filtered_signal=self.filtered_signal[::,0:(points-delete)] self.epochs_file=reshape(self.filtered_signal,(channels,int(self.size_epoch),int((points-delete)/self.size_epoch)),order='F') return(self.epochs_file) # def implementation(self): max_value=float(self.field_maxValue.text()) self.myepochs=self.epochs(self.data, self.channels, self.points) x,y=spectral_pattern.spectral(self.myepochs, max_value,self.Fs) newSignal=reshape(x,(shape(x)[0],shape(x)[1]*shape(x)[2]),order='F') self.graph_method = window_graph(self.data, self.Fs,3,newSignal,y,shape(self.myepochs)[2]); self.graph_method.show();
class MplGraphQt5Widget(QWidget): def __init__(self, parent=None): super(MplGraphQt5Widget, self).__init__(parent) self.width = 3 self.height = 3 self.dpi = 100 self._dataY = np.array([]) self._dataX = np.array([]) self._spCols = 1 self._spRows = 1 self.all_sp_axes = [] self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi) self.all_sp_axes.append(self.fig.add_subplot(self._spCols, self._spRows, 1)) self.fig.set_frameon(False) self.fig.set_tight_layout(True) self.canvas = Canvas(self.fig) self._navBarOn = False self.mpl_toolbar = NavigationToolbar(self.canvas, parent) self.mpl_toolbar.dynamic_update() self.canvas.mpl_connect('key_press_event', self.on_key_press) self.canvas.mpl_connect('button_press_event', self.on_button_press) self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move) self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() self.canvas.setParent(parent) self.canvas.clearMask() self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.canvas.updateGeometry() vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) if not self._navBarOn: self.mpl_toolbar.hide() self.setLayout(vbox) def get_icon(name): """Return Matplotlib icon *name*""" return QIcon(osp.join(rcParams['datapath'], 'images', name)) key_pressed = pyqtSignal(object, name='keyPressed') def on_key_press(self, event): self.key_pressed.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) button_pressed = pyqtSignal(object, name='buttonPressed') def on_button_press(self, event): self.button_pressed.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) mouse_move = pyqtSignal(object, name='mouseMoved') def on_mouse_move(self, event): self.mouse_move.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) def generateNewAxes(self): for ax in self.all_sp_axes: self.fig.delaxes(ax) self.all_sp_axes = [] numOfAxes = (self._spRows*self._spCols)+1 for i in np.arange(1,numOfAxes): self.all_sp_axes.append(self.fig.add_subplot(self._spRows, self._spCols, i)) self.canvas.setGeometry(100, 100, 300, 300) #Used to update the new number of axes self.canvas.updateGeometry() #This will bring the size of the canvas back to the original (defined by the vbox) spRowsChanged = pyqtSignal(int) def getspRows(self): return self._spRows @pyqtSlot(int) def setspRows(self, spRows): self._spRows = spRows self.generateNewAxes() self.spRowsChanged.emit(spRows) def resetspRows(self): self.setspRows(1) spRows = pyqtProperty(int, getspRows, setspRows, resetspRows) spColsChanged = pyqtSignal(int) def getspCols(self): return self._spCols @pyqtSlot(int) def setspCols(self, spCols): self._spCols = spCols self.generateNewAxes() self.spRowsChanged.emit(spCols) def resetspCols(self): self.setspCols(1) spCols = pyqtProperty(int, getspCols, setspCols, resetspCols) dataChanged = pyqtSignal(bool) def get_Y_data(self): return self._dataY @pyqtSlot(int) def set_Y_data(self, y_data): self._dataY = y_data self.dataChanged.emit(True) def plot(self, on_axes=0): if np.size(self._dataX) == 0: self.all_sp_axes[on_axes].plot(self._dataY) else: self.all_sp_axes[on_axes].plot(self._dataX, self._dataY) def getNavBarOn(self): return self._navBarOn def setNavBarOn(self, navBarOn): self._navBarOn = navBarOn if not navBarOn: self.mpl_toolbar.hide() else: self.mpl_toolbar.show() def resetNavBarOn(self): self._navBarOn = True navBarOn = pyqtProperty(bool, getNavBarOn, setNavBarOn, resetNavBarOn) @pyqtSlot(bool) def set_autoscale(self, autoscale): for axis in self.all_sp_axes: axis.set_autoscale(autoscale)
class CustomPlot(QDialog): """ Class used to create a plot window Data is read from session object """ _colors = ['#FF0000', '#0000FF', '#00FF00', '#00002C', '#FF1AB9', '#FFD300', '#005800', '#8484FF', '#9E4F46', '#00FFC1', '#008495', '#00007B', '#95D34F', '#F69EDC', '#D312FF'] def __init__(self, parent, session): """ Create plot window :param parent: Parent window object :param session: Session object to plot data from """ super(CustomPlot, self).__init__(parent) self.amp = [] self.peak_time = [] self.fwhm = [] self.regular = [] self.smooth = [] self.sem = [] self.scroll = 3 self.ui = Ui_Dialog() self.ui.setupUi(self) self.used_colors = [] self.session = session self.fig = plt.figure() self.current_ax = self.fig.add_subplot(111) self.axes = {} self.ax_list = [] self.add_ax(self.current_ax) self.ui.toolButton_anatomy.hide() if session.anatomy is not None: self.ui.toolButton_anatomy.show() self.canvas = FigureCanvas(self.fig) self.toolbar = NavigationToolbar(self.canvas, self, coordinates=True) self.toolbar.hide() self.ui.mplvl.addWidget(self.canvas) self.ui.checkBox_fwhm.clicked.connect(self.plot_fwhm) self.ui.checkBox_sem.clicked.connect(self.plot_sem) self.ui.checkBox_regular.clicked.connect(self.replot) self.ui.checkBox_smooth.clicked.connect(self.replot) self.ui.checkBox_amp.clicked.connect(self.plot_amplitude) self.ui.checkBox_peak.clicked.connect(self.plot_peak) self.ui.stimuliBox.currentTextChanged.connect(self.replot) self.ui.mean_response_btn.clicked.connect(self.replot) self.ui.several_responses_btn.clicked.connect(self.replot) self.ui.checkBox_labels.clicked.connect(self.show_legends) self.ui.label_size.valueChanged.connect(self.show_legends) self.ui.spinBox.valueChanged.connect(self.replot) self.ui.toolButton_home.clicked.connect(self.toolbar.home) self.ui.toolButton_export.clicked.connect(self.tool_export) self.ui.toolButton_pan.clicked.connect(self.toolbar.pan) self.ui.toolButton_zoom.clicked.connect(self.toolbar.zoom) self.ui.toolButton_anatomy.clicked.connect(self.tool_anatomy) self.setWindowTitle('Plot - ' + session.name) self.export_window = None self.add_stimuli_types() # Enable the 'plot several' button if the object has more than 1 child children = self.session.children + self.session.sessions if children and len(children) > 1: self.ui.several_responses_btn.setEnabled(True) else: self.ui.several_responses_btn.hide() self.ui.toolButton_add_subplot.clicked.connect(self.add_subplot) self.ui.toolButton_rem_subplot.clicked.connect(self.remove_subplot) # Move the subplot to make space for the legend self.fig.subplots_adjust(right=0.8) self.canvas.mpl_connect('button_press_event', self.click_plot) self.fig.tight_layout(pad=2.0) # Change default value in smooth wheel if percent is used if self.session.get_setting("percent"): self.ui.spinBox.setValue(2) self.replot() self.show() self.ui.verticalLayout_3.update() def highlight_current_axis(self): if self.current_ax is not None: for ax in self.axes: ax.spines['bottom'].set_color('black') ax.spines['top'].set_color('black') ax.spines['right'].set_color('black') ax.spines['left'].set_color('black') self.current_ax.spines['bottom'].set_color('red') self.current_ax.spines['top'].set_color('red') self.current_ax.spines['right'].set_color('red') self.current_ax.spines['left'].set_color('red') def add_subplot(self): n = len(self.axes) i = 0 for ax in self.ax_list: self.fig.delaxes(ax) ax.change_geometry(((n + 1) / 2) + (1 * ((n + 1) % 2)), 2, (i + 1)) i += 1 if n == 0: new_ax = self.fig.add_subplot(1, 1, 1) else: for ax in self.ax_list: self.fig.add_axes(ax) new_ax = self.fig.add_subplot(((n + 1) / 2) + (1 * ((n + 1) % 2)), 2, n + 1) self.add_ax(new_ax) if len(self.axes) == 1: self.current_ax = new_ax self.load_ax_settings() else: self.highlight_current_axis() self.fig.tight_layout() self.set_allowed_buttons() self.canvas.draw() def remove_subplot(self): self.axes.pop(self.current_ax) self.ax_list.remove(self.current_ax) self.fig.delaxes(self.current_ax) n = len(self.axes) if n == 1: self.fig.axes[0].change_geometry(1, 1, 1) else: for i in xrange(n): self.fig.axes[i].change_geometry((n / 2) + (1 * (n % 2)), 2, (i + 1)) if self.fig.axes: self.current_ax = self.fig.axes[0] self.load_ax_settings() else: self.current_ax = None self.highlight_current_axis() self.set_allowed_buttons() self.canvas.draw() def add_ax(self, ax): if ax is not None: self.ax_list.append(ax) # Change default value in smooth wheel if percent is used smooth = 2 if self.session.get_setting("percent") else 20 self.axes[ax] = {'plot': 'mean', 'data': {'regular': False, 'smooth': False, 'smooth_fact': smooth, 'stimuli_type': 0, }, 'settings': {'amp': False, 'peak': False, 'sem': False, 'fwhm': False}, 'datalog': {'amp_label': '', 'peak_label': '', 'fwhm_label': ''}, 'used_colors': [] } def save_ax_settings(self, ax): if self.ui.mean_response_btn.isChecked(): plot = 'mean' else: plot = 'several' if ax is not None: self.axes[ax] = {'plot': plot, 'data': {'regular': self.ui.checkBox_regular.isChecked(), 'smooth': self.ui.checkBox_smooth.isChecked(), 'smooth_fact': self.ui.spinBox.value(), 'stimuli_type': self.ui.stimuliBox.currentIndex(), }, 'settings': {'amp': self.ui.checkBox_amp.isChecked(), 'peak': self.ui.checkBox_peak.isChecked(), 'sem': self.ui.checkBox_sem.isChecked(), 'fwhm': self.ui.checkBox_fwhm.isChecked()}, 'datalog': {'amp_label': self.ui.amp_label.text(), 'peak_label': self.ui.peak_label.text(), 'fwhm_label': self.ui.fwhm_label.text()}, 'used_colors': self.used_colors } def load_ax_settings(self): ax = self.current_ax self.ui.mean_response_btn.setChecked(self.axes[ax]['plot'] == 'mean') self.ui.several_responses_btn.setChecked(self.axes[ax]['plot'] == 'several') self.ui.checkBox_regular.setChecked(self.axes[ax]['data']['regular']) self.ui.checkBox_smooth.setChecked(self.axes[ax]['data']['smooth']) self.ui.spinBox.blockSignals(True) self.ui.spinBox.setValue(self.axes[ax]['data']['smooth_fact']) self.ui.spinBox.blockSignals(False) self.ui.stimuliBox.blockSignals(True) self.ui.stimuliBox.setCurrentIndex(self.axes[ax]['data']['stimuli_type']) self.ui.stimuliBox.blockSignals(False) self.ui.checkBox_amp.setChecked(self.axes[ax]['settings']['amp']) self.ui.checkBox_peak.setChecked(self.axes[ax]['settings']['peak']) self.ui.checkBox_sem.setChecked(self.axes[ax]['settings']['sem']) self.ui.checkBox_fwhm.setChecked(self.axes[ax]['settings']['fwhm']) self.ui.fwhm_label.setText(self.axes[ax]['datalog']['fwhm_label']) self.ui.peak_label.setText(self.axes[ax]['datalog']['peak_label']) self.ui.amp_label.setText(self.axes[ax]['datalog']['amp_label']) self.used_colors = self.axes[ax]['used_colors'] def click_plot(self, event): if event.inaxes: self.save_ax_settings(self.current_ax) self.current_ax = event.inaxes self.load_ax_settings() self.set_allowed_buttons() self.highlight_current_axis() self.canvas.draw() def tool_anatomy(self): self.anatomy_window = AnatomyWindow(self, self.session) def tool_export(self): """ Export button callback. Creates a custom export window """ self.export_window = ExportWindow(self, self.session, self.toolbar, self.ui.stimuliBox.currentText()) def plot_fwhm(self): """ If fwhm checkbox is checked this function will update fwhm span and datalog info. If fwhm checkbox is not checked this function will remove fwhm span and datalog info. """ self.remove_fwhm(uncheck=False) if not (self.ui.checkBox_smooth.isChecked() \ or self.ui.checkBox_regular.isChecked()) \ or self.ui.several_responses_btn.isChecked(): self.ui.fwhm_label.hide() return if self.ui.checkBox_fwhm.isChecked(): try: fwhm = self.session.get_fwhm(self.ui.stimuliBox.currentText(), self.ui.spinBox.value()) except Exception as exc: self.ui.checkBox_fwhm.setChecked(False) QMessageBox.warning(self, exc.args[0], exc.args[1]) return fwhm_text = "" for stimuli_val, values in fwhm.iteritems(): self.fwhm.append(self.current_ax.axvspan( values[0], values[1], facecolor='g', alpha=0.2)) fwhm_text += "FWHM " + stimuli_val + " width: " + \ str(values[1] - values[0]) + "\n" self.ui.fwhm_label.setText(fwhm_text[0:-1]) self.ui.fwhm_label.show() else: self.ui.fwhm_label.hide() self.canvas.draw() def replot(self): """ Replot regular and smoothed curve, amplitude, peak and fwhm. Used when changing the data to plot and callback function for many checkboxes. """ self.remove_sem() self.remove_regular_plots() self.plot_regular() self.remove_smoothed_plots() self.plot_smooth() self.plot_peak() self.plot_amplitude() self.plot_fwhm() self.plot_sem() self.set_allowed_buttons() def remove_peak_time(self, uncheck=True): if self.peak_time: for peak in self.peak_time: if peak.axes == self.current_ax: peak.remove() self.ui.peak_label.hide() if uncheck: self.ui.checkBox_peak.setChecked(False) self.peak_time = [] def remove_sem(self): if self.sem: for sem in self.sem: if sem[0].axes == self.current_ax: self.used_colors.remove(sem[0].get_color()) sem[0].remove() for line in sem[1]: line.remove() for line in sem[2]: line.remove() self.ui.checkBox_sem.setChecked(False) def remove_amplitude(self, uncheck=True): if self.amp: for amp in self.amp: if amp.axes == self.current_ax: amp.remove() self.ui.amp_label.hide() if uncheck: self.ui.checkBox_amp.setChecked(False) self.amp = [] def remove_fwhm(self, uncheck=True): if self.fwhm: for fwhm in self.fwhm: if fwhm.axes == self.current_ax: fwhm.remove() self.ui.fwhm_label.hide() if uncheck: self.ui.checkBox_fwhm.setChecked(False) def remove_regular_plots(self): if self.regular: for axis in self.regular: if axis.axes == self.current_ax: self.used_colors.remove(axis.get_color()) axis.remove() def plot_smooth(self): """ Plot smoothed responses from session object if smoothed checkbox is checked. Will otherwise hide smoothed responses """ if self.ui.checkBox_smooth.isChecked() and self.ui.mean_response_btn.isChecked(): self.current_ax.relim() try: smooth = self.session.get_smooth(self.ui.spinBox.value()) except Exception as exc: self.ui.checkBox_smooth.setChecked(False) QMessageBox.warning(self, exc.args[0], exc.args[1]) return if self.ui.stimuliBox.currentText() == "All": for stimuli_type, smoothed_data in smooth.iteritems(): axis, = self.current_ax.plot( self.session.get_x_axis(), smoothed_data, color=self.get_color(), label=stimuli_type + ", smoothed") self.smooth.append(axis) else: stimuli_value = self.ui.stimuliBox.currentText() axis, = self.current_ax.plot( self.session.get_x_axis(), smooth[stimuli_value], color=self.get_color(), label=stimuli_value + ", smoothed") self.smooth.append(axis) else: self.remove_smoothed_plots() self.show_legends() self.set_allowed_buttons() self.canvas.draw() def remove_smoothed_plots(self): if self.smooth: for axis in self.smooth: if axis.axes == self.current_ax: self.used_colors.remove(axis.get_color()) axis.remove() def plot_mean(self): """ Plot mean responses from session object if mean checkbox is checked. Will otherwise hide mean responses """ if self.ui.checkBox_regular.isChecked(): self.current_ax.relim() mean = self.session.get_mean() self.plot_data(mean) else: self.remove_regular_plots() self.show_legends() self.canvas.draw() def plot_sem(self): """ Standard error of mean checkbox callback. Plot standard error of mean. """ if self.ui.checkBox_sem.isChecked() and self.ui.stimuliBox.currentText() != "All": mean = self.session.get_mean()[self.ui.stimuliBox.currentText()] x = np.arange(mean.size)*self.session.get_tr() self.current_ax.relim() sem = self.session.get_sem() self.sem.append(self.current_ax.errorbar(x, mean, color=self.get_color(), yerr=sem[self.ui.stimuliBox.currentText()])) else: self.remove_sem() self.canvas.draw() def plot_amplitude(self): """ If amplitude checkbox is checked this function will update amplitude lines and datalog info. If amplitude checkbox is not checked this function will remove amplitude lines and datalog info. """ self.remove_amplitude(uncheck=False) if not (self.ui.checkBox_smooth.isChecked() \ or self.ui.checkBox_regular.isChecked()) \ or self.ui.several_responses_btn.isChecked(): self.ui.amp_label.hide() return if self.ui.checkBox_amp.isChecked(): try: points = self.session.get_peaks( self.ui.spinBox.value(), smooth=self.ui.checkBox_smooth.isChecked()) except Exception as exc: self.ui.checkBox_amp.setChecked(False) QMessageBox.warning(self, exc.args[0], exc.args[1]) return amp_text = "" if self.ui.stimuliBox.currentText() == "All": for stimuli_val, position in points.iteritems(): amp_text += self.__plot_amp(stimuli_val, position) else: stimuli_val = self.ui.stimuliBox.currentText() amp_text = self.__plot_amp(stimuli_val, points[stimuli_val]) self.ui.amp_label.setText(amp_text[0:-1]) self.ui.amp_label.show() else: self.ui.amp_label.hide() self.canvas.draw() def __plot_amp(self, stimuli_val, position): self.amp.append( self.current_ax.axhline(position[1], color=CustomPlot._colors[-1])) if self.ui.checkBox_smooth.isChecked(): start_value = self.session.get_smooth(self.ui.spinBox.value())[stimuli_val][0] else: start_value = self.session.get_mean()[stimuli_val][0] return "Amplitude " + stimuli_val + ": " + \ str(position[1] - start_value) + "\n" def plot_peak(self): """ If peak checkbox is checked this function will update peak lines and datalog info. If peak checkbox is not checked this function will remove peak lines and datalog info. """ self.remove_peak_time(uncheck=False) if not (self.ui.checkBox_smooth.isChecked() \ or self.ui.checkBox_regular.isChecked()) \ or self.ui.several_responses_btn.isChecked(): self.ui.peak_label.hide() return if self.ui.checkBox_peak.isChecked(): try: points = self.session.get_peaks( self.ui.spinBox.value(), smooth=self.ui.checkBox_smooth.isChecked()) except Exception as exc: self.ui.checkBox_amp.setChecked(False) QMessageBox.warning(self, exc.args[0], exc.args[1]) return peak_text = "" if self.ui.stimuliBox.currentText() == "All": for stimuli_val, position in points.iteritems(): self.peak_time.append( self.current_ax.axvline(position[0], color=CustomPlot._colors[-1])) peak_text += "Peak " + stimuli_val + ": " + \ str(position[0]) + "\n" else: stimuli_val = self.ui.stimuliBox.currentText() self.peak_time.append( self.current_ax.axvline(points[stimuli_val][0], color=CustomPlot._colors[-1])) peak_text += "Peak " + stimuli_val + ": " + \ str(points[stimuli_val][0]) + "\n" self.ui.peak_label.setText(peak_text[0:-1]) self.ui.peak_label.show() else: self.ui.peak_label.hide() self.canvas.draw() self.canvas.draw() def get_color(self): """ :return: RGB hex string """ for color in CustomPlot._colors: if color not in self.used_colors: self.used_colors.append(color) return color def add_stimuli_types(self): """ Add all stimuli types that exists in the data to a combobox """ data = self.session.get_mean() if len(data) > 1: self.ui.stimuliBox.addItem("All") for stimuli_type in data: self.ui.stimuliBox.addItem(stimuli_type) def plot_several_sessions(self): """ Plot the active object's children, each as a separate plot """ if self.ui.checkBox_regular.isChecked(): self.remove_smoothed_plots() self.current_ax.relim() children = self.session.sessions + self.session.children for child in children: if child.ready_for_calculation(self.session.get_mask(), self.session.get_stimuli()): child_mean = child.get_mean(self.session.get_setting('percent'), self.session.get_setting('global'), self.session.get_mask(), self.session.get_stimuli()) self.plot_data(child_mean, child.name) else: self.remove_regular_plots() self.show_legends() self.canvas.draw() def plot_data(self, data_dict, name = None): """ Plots the data that exists in the dictionary data_dict, depending on stimuli selected """ if self.ui.stimuliBox.currentText() == "All": for stimuli_type, stimuli_data in data_dict.iteritems(): x = np.arange(len(stimuli_data))*self.session.get_tr() if name: stimuli_type = name + " - " + stimuli_type axis, = self.current_ax.plot(x, stimuli_data, color=self.get_color(), label=stimuli_type) self.regular.append(axis) else: type = self.ui.stimuliBox.currentText() if type in data_dict: data = data_dict[type] x = np.arange(len(data))*self.session.get_tr() if name: type = name + " - " + type axis, = self.current_ax.plot(x, data, color=self.get_color(), label=type) self.regular.append(axis) def plot_regular(self): """ Plot either the mean of the object's data, or its children separately, depending on if the radiobutton is checked """ if self.ui.mean_response_btn.isChecked(): self.plot_mean() elif not isinstance(self.session, Session): self.plot_several_sessions() self.set_allowed_buttons() def show_legends(self): for ax in self.axes: handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, prop={'size': self.ui.label_size.value()}) if not handles or not self.ui.checkBox_labels.isChecked(): lgd.set_visible(False) else: lgd.set_visible(True) if self.ui.checkBox_labels.isChecked(): self.ui.label_size.setEnabled(True) else: self.ui.label_size.setEnabled(False) self.canvas.draw() def set_allowed_buttons(self): """ Enable or disable buttons depending on if they are meaningful in the current situation """ # Enable the buttons if there are exactly 1 regular curve, or if there is one smoothed curve if self.current_ax is None: self.ui.toolButton_rem_subplot.setEnabled(False) self.ui.checkBox_amp.setEnabled(False) self.ui.checkBox_fwhm.setEnabled(False) self.ui.checkBox_peak.setEnabled(False) self.ui.checkBox_sem.setEnabled(False) self.ui.checkBox_regular.setEnabled(False) self.ui.checkBox_smooth.setEnabled(False) self.ui.mean_response_btn.setEnabled(False) self.ui.several_responses_btn.setEnabled(False) self.ui.spinBox.setEnabled(False) self.ui.stimuliBox.setEnabled(False) else: self.ui.toolButton_rem_subplot.setEnabled(True) self.ui.mean_response_btn.setEnabled(True) self.ui.several_responses_btn.setEnabled(True) self.ui.spinBox.setEnabled(True) self.ui.stimuliBox.setEnabled(True) self.ui.checkBox_regular.setEnabled(True) if not self.ui.several_responses_btn.isChecked() and (self.ui.checkBox_regular.isChecked() or self.ui.checkBox_smooth.isChecked()): self.ui.checkBox_amp.setEnabled(True) self.ui.checkBox_fwhm.setEnabled(True) if self.ui.stimuliBox.currentText() != 'All': self.ui.checkBox_sem.setEnabled(True) else: self.ui.checkBox_sem.setEnabled(False) self.ui.checkBox_peak.setEnabled(True) else: self.ui.checkBox_amp.setEnabled(False) self.remove_amplitude() self.ui.checkBox_fwhm.setEnabled(False) self.remove_fwhm() self.ui.checkBox_peak.setEnabled(False) self.remove_peak_time() self.ui.checkBox_sem.setEnabled(False) self.remove_sem() # Only allow smooth if we are plotting mean if self.ui.mean_response_btn.isChecked(): self.ui.checkBox_smooth.setEnabled(True) else: self.ui.checkBox_smooth.setEnabled(False) if self.ui.checkBox_fwhm.isChecked(): self.ui.fwhm_label.show() else: self.ui.fwhm_label.hide() if self.ui.checkBox_peak.isChecked(): self.ui.peak_label.show() else: self.ui.peak_label.hide() if self.ui.checkBox_amp.isChecked(): self.ui.amp_label.show() else: self.ui.amp_label.hide() def resizeEvent(self, QResizeEvent): try: self.fig.tight_layout(pad=2.0) except: pass
class ImageViewerWidget(QtWidgets.QWidget): """ PyQt Widget holding a matplotlib canvas to visualize and navigate through 3D image data """ def __init__(self, image_data: sitk.Image = None, parent: QtWidgets.QWidget = None): """ Constructs the viewer and initializes all controls. @param image_data: the SimpleITK image to visualize @param QtWidgets.QWidget parent: the widget's parent if used in a full PyQt GUI """ super(ImageViewerWidget, self).__init__(parent) if image_data is None: # create an empty 3d image image_data = sitk.GetImageFromArray(np.zeros((1, 1, 1))) # init attributes self.image = image_data # the SimpleITK image in the viewer self.image_array = None # the image as np.array self.current_slice = 0 self.im_plot = None # storing the mpl plot self.current_window = 0 self.current_level = 0 self.greyval_range = [0, 0] self.masks = {} # dict with mask as key and its plot as value self.markers = [] # list of markers and their pixel coordinates self.orientation = SLICE_ORIENTATION_XY # what dimensions are seen as the plane (values like in VTK) # init the MPL canvas initial_canvas_size_x = 5 # size in pixels. corresponds to a size of 5x5 inches with 100 dpi initial_canvas_size_y = 5 self.max_resolution = 500 self.dpi = self.max_resolution // initial_canvas_size_x self.canvas = FigureCanvas( Figure(figsize=(initial_canvas_size_x, initial_canvas_size_y), dpi=self.dpi, facecolor='black')) self.ax = self.canvas.figure.subplots() self.ax.axis('off') self.canvas.figure.subplots_adjust(0, 0, 1, 1) self.marker_plot = self.ax.scatter([], [], c=ImageMarker.STANDARD_COLOR) self.canvas.setParent(self) # add a hidden mpl toolbar for panning & zooming functionality self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # define a slider for image slicing self.slice_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Vertical, parent=self) self.slice_slider.valueChanged.connect(self.update_slice) # define a text field to display image coordinates and one for the current zoom bottom_bar_layout = QtWidgets.QHBoxLayout() self.pixel_info_label = PixelInfoQLabel(parent=self) bottom_bar_layout.addWidget(self.pixel_info_label) # layout the QtWidget main_layout = QtWidgets.QVBoxLayout() image_with_slider_layout = QtWidgets.QHBoxLayout() image_with_slider_layout.addWidget(self.canvas) image_with_slider_layout.addWidget(self.slice_slider) main_layout.addLayout(image_with_slider_layout) main_layout.addLayout(bottom_bar_layout) self.setLayout(main_layout) # set the focus to be on the canvas, so that keyevents get handled by mpl_connect self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus) self.canvas.setFocus() # prevent the slider from gaining focus self.slice_slider.setFocusPolicy(QtCore.Qt.NoFocus) # initialize the showing image self.init_image() # define the interactor for user inputs self.interactor_style = ImageViewerInteractor(self) # show the widget self.show() def init_image(self): """ Initial call after the image has been set or reset. Constructs the image array and plots the middle slice of the image with window/level spanning the whole grey value range of the image. """ # DON'T swap axes, in order to keep the fastest dimension of the np array the first # -> z-slices are the most performant self.image_array = sitk.GetArrayFromImage(self.image) # take the middle slice as initial slice shown self.current_slice = self.get_slice_dim_size() // 2 # redraw if there is already a plot if self.im_plot is not None: self.im_plot.remove() self.im_plot = self.ax.imshow( self.image_array[index_compatibility( get_3d_plane_index(self.current_slice, self.orientation))], aspect=get_aspect_ratio_for_plane(self.image.GetSpacing(), self.orientation, self.image.GetSize())) # set the window/level to span the whole greyvalue range of the image min_grey = self.image_array.min() max_grey = self.image_array.max() self.greyval_range = [min_grey, max_grey] self.reset_window_level() # init the coordinate label initial_coords = [0, 0] initial_coords.insert(self.orientation, self.current_slice) self.show_pixel_info(initial_coords) # init the slider with the correct range self.adapt_slider() def change_orientation(self, orientation: Union[int, str]): """ Change the slicing dimension of the viewer. Orientation values are expected as in SLICE_ORIENTATION @param orientation: either the number corresponding to the slicing dimension or a string of the new plane: 'xy' or 2 -> the XY plane is shown; 'xz' or 1 -> the XZ plane is shown; 'yz' or 0 -> the YZ plane is shown; """ # don't do anything if orientation stays the same if orientation == self.orientation: return if orientation not in SLICE_ORIENTATION.keys( ) and orientation not in SLICE_ORIENTATION.values(): print( 'Cannot change viewer orientation to {}. Use one of {} or {} respectively.' .format(orientation, SLICE_ORIENTATION.keys(), SLICE_ORIENTATION.values())) # convert the plane string into the number of the slice dimension if isinstance(orientation, str): orientation = SLICE_ORIENTATION[orientation] self.orientation = orientation # make sure the current slice is valid in the new orientation if self.current_slice >= self.get_slice_dim_size(): self.current_slice = self.get_slice_dim_size() - 1 self.redraw_slice() self.adapt_slider() def set_window_level(self, window: Union[int, float], level: Union[int, float]): """ Window levelling operation on the image. @param window: width of the window @param level: window center """ if window < 0: # set a minimum level (could be even smaller in case of normalized images with values in [0,1]) window = 1e-5 self.current_window = window self.current_level = level half_window = self.current_window / 2 self.im_plot.set_clim(vmin=self.current_level - half_window, vmax=self.current_level + half_window) self.canvas.draw( ) # optimizable: only redraw the image, not markers or masks! # print('Window: {}, Level: {}'.format(self.current_window, self.current_level)) def reset_window_level(self): """ Set the window/level to span the entire grey value range of the image. """ # reset to cover the entire image range window = self.greyval_range[1] - self.greyval_range[0] level = self.greyval_range[0] + window / 2 self.set_window_level(window, level) def update_slice(self, new_slice: int): """ Sets the current slice and redraws the image @param new_slice: the slice index """ # if the index is not in range, change it to the min or max index lower = 0 upper = self.get_slice_dim_size() if not lower <= new_slice < upper: new_slice = lower if new_slice < lower else upper - 1 self.current_slice = new_slice self.redraw_slice() def redraw_slice(self): """ Draws the current slice of the image with mask and markers. This resets the current zoom factor but not the window/level. """ # here, blitting could increase performance img_slice = self.image_array[index_compatibility( get_3d_plane_index(slice_index=self.current_slice, orientation=self.orientation))] # set extent to be according to dimensions so that nothing gets squished y_max, x_max = img_slice.shape self.ax.set_xlim([0, x_max]) self.ax.set_ylim([0, y_max]) half_window = self.current_window / 2 # remove the image plot and redraw. this is a little less performant than # setting data on the current image plot, but this way the extent is updated correctly self.im_plot.remove() self.im_plot = self.ax.imshow( img_slice, vmin=self.current_level - half_window, vmax=self.current_level + half_window, aspect=get_aspect_ratio_for_plane(self.image.GetSpacing(), self.orientation, self.image.GetSize())) # update the coordinate text self.pixel_info_label.set_coordinate(self.orientation, self.current_slice) self.show_pixel_info(self.pixel_info_label.coords) # draw masks and markers self.update_masks() self.scatter_markers() # update the canvas (making sure this is only called once per update for performance) self.canvas.draw() def get_slice_dim_size(self) -> int: """ Get the size of the slicing dimension (found in self.orientation) @return: the size of the current slicing dimension """ return self.image.GetSize()[self.orientation] def adapt_slider(self): """ Call this, if the image slice range or the current slice changed. The slider value and range will be adapted to these values """ self.slice_slider.setRange(0, self.get_slice_dim_size() - 1) self.slice_slider.setValue(self.current_slice) def move_slice(self, delta: int): """ Change the current slice by delta steps and then redraw. @param delta: the number of steps by which to change the current slice (positive or negative) """ # moves the slider, which in turn changes the slice and redraws the image self.slice_slider.setValue(self.current_slice + delta) def add_marker(self, position: Sequence[Union[float, int]]): """ Adds a marker to the given pixel position, saves and displays it. @param position: continuous 3d pixel position of the new marker """ assert len(position) == 3 marker = ImageMarker(position) self.markers.append(marker) # only redraw markers, if the new marker is in the current slice if round(position[self.orientation]) == self.current_slice: self.scatter_markers() self.canvas.draw() def remove_markers(self, only_last: bool = False): """ Remove all (or only the last) markers from the image. This does not only apply to visible markers, but markers in all slices. @param only_last: if true, only the last marker will be removed; otherwise all markers get removed """ if len(self.markers) == 0: return if only_last: removed_marker = self.markers.pop() print('removed marker at {}'.format(removed_marker.pixel_position)) else: self.markers.clear() print('removed all markers') # redraw markers self.scatter_markers() self.canvas.draw() def scatter_markers(self): """ Draw all markers in the current slice on the image. Attention: this does not redraw the whole canvas, self.canvas.draw() has to be called separately. This way, multiple changes can be drawn at once, for instance after a new slice is shown. """ # determine which markers are found in the current slice markers_in_slice = [] for m in self.markers: pixel_pos = np.array(m.pixel_position) # choose a marker if the rounded index is the same as the current slice if round(pixel_pos[self.orientation]) == self.current_slice: # only take the plane coordinates markers_in_slice.append( pixel_pos[np.arange(3) != self.orientation]) if len(markers_in_slice) != 0: # combine marker coordinates into one array and and show it in a scatter plot np_markers = np.stack(markers_in_slice, axis=1) self.marker_plot.set_offsets(np_markers.swapaxes(0, 1)) else: # if no markers are found, clear remove the current plot and replace it with an empty one self.marker_plot.remove() self.marker_plot = self.ax.scatter([], [], c=ImageMarker.STANDARD_COLOR, s=ImageMarker.STANDARD_SIZE) def add_mask(self, mask: ImageMask): """ Show a mask overlay on top of the current image. The mask has to have the same size, spacing and origin. @param mask: the mask to overlay (has to have the same properties as the image) """ if not compatible_metadata(self.image, mask.mask_image): return # save the mask in the masks dict self.masks[mask] = None # draw the image self.update_masks() self.canvas.draw() def update_masks(self): """ Draw the masks in the current slice as an overlay on top of the image. Attention: this does not redraw the whole canvas, self.canvas.draw() has to be called separately. This way, multiple changes can be drawn at once, for instance after a new slice is shown. """ # remove old plots self.clear_mask_plots() # draw each mask (no changes to canvas before canvas.draw() is called) for m in self.masks.keys(): self.masks[m] = add_mask_to_image( self.ax, m.get_slice(self.current_slice, self.orientation), aspect=get_aspect_ratio_for_plane(m.get_spacing(), self.orientation, self.image.GetSize()), alpha=m.alpha, color=m.color) def clear_mask_plots(self): """ Remove all masks from the canvas. """ for m in self.masks.keys(): try: # remove the existing plot self.masks[m].remove() except AttributeError: # if the mask was not there in the previous slice it is None here pass def set_image(self, image: sitk.Image): """ Set the image and show it. This resets all markers and masks previously added to the viewer. @param image: the new image to show """ self.image = image # remove old masks self.clear_mask_plots() self.masks.clear() # remove old markers self.markers.clear() self.scatter_markers() # draw image and re-init everything else self.init_image() def show_pixel_info(self, pixel_coords: Sequence[Union[float, int]]): """ Show the given coordinates and the corresponding image intensity in a label below the image. For instance, this is called whenever whenever the user moves the mouse over the image. @param pixel_coords: the (continuous) coordinatates to be displayed in the label """ if pixel_coords is None: # remove the label text self.pixel_info_label.clear() else: # take the discrete index x, y, z = [int(ind) for ind in pixel_coords] try: self.pixel_info_label.set_values(x, y, z, self.image.GetPixel(x, y, z)) except (IndexError, RuntimeError, TypeError): # gets thrown if x, y, z are out of bounds of the image (this can happen on the edges of the figure) return
class FrequencyCurveChart(QDialog): def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Frequency Curve Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems([name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.scale_label, 2, 0) self.main_layout.addWidget(self.scale_combo_box, 2, 1) self.last_samples = [] self.last_max_frequency = 0.0 @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False def update_chart(self): self.show_samples(self.last_samples, append=False) def show_samples(self, samples: typing.Iterable[GrainSizeSample], append=False, title=None): append = append and len(self.last_samples) != 0 if not append: self.axes.clear() self.last_samples = [] max_frequency = self.last_max_frequency for i, sample in enumerate(samples): self.last_samples.append(sample) if i == 0: x = self.transfer(sample.classes_φ) if not append: if self.xlog: self.axes.set_xscale("log") if title is None: self.axes.set_title(self.tr("Frequency curves of samples")) else: self.axes.set_title(title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.axes.set_xlim(x[0], x[-1]) self.axes.plot(x, sample.distribution, c="black") sample_max_freq = np.max(sample.distribution) if sample_max_freq > max_frequency: max_frequency = sample_max_freq self.axes.set_ylim(0.0, round(max_frequency*1.2, 2)) self.last_max_frequency = max_frequency self.figure.tight_layout() self.canvas.draw()
class ChromAnalyzer(QtWidgets.QWidget, Ui_ChromAnalyzer): def __init__(self, parent=None): super(ChromAnalyzer, self).__init__(parent) self.setupUi(self) self.openChromButton.clicked.connect(self.openchrom) self.analyzeButton.clicked.connect(self.analyze) self.aboutButton.clicked.connect(self.about) self.quitButton.clicked.connect(self.quit) self.tablemodel = TableModel(self) self.tableView.setModel(self.tablemodel) self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tableView.customContextMenuRequested.connect(self.openTableMenu) # Add plot self.figure = plt.figure(dpi=100) self.ax = self.figure.add_subplot(111) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # set the layout layout_chormatogram = QtWidgets.QVBoxLayout() layout_chormatogram.addWidget(self.canvas) self.plotterBox.setLayout(layout_chormatogram) def keyPressEvent(self, e): if (e.modifiers() & QtCore.Qt.ControlModifier): if e.key() == QtCore.Qt.Key_C: #copy if len(self.tableView.selectionModel().selectedIndexes()) > 0: previous = self.tableView.selectionModel().selectedIndexes( )[0] columns = [] rows = [] for index in self.tableView.selectionModel( ).selectedIndexes(): if previous.column() != index.column(): columns.append(rows) rows = [] rows.append(str(index.data().toPyObject())) previous = index columns.append(rows) # add rows and columns to clipboard clipboard = "" nrows = len(columns[0]) ncols = len(columns) for r in xrange(nrows): for c in xrange(ncols): clipboard += columns[c][r] if c != (ncols - 1): clipboard += '\t' clipboard += '\n' # copy to the system clipboard sys_clip = QtWidgets.QApplication.clipboard() sys_clip.setText(clipboard) def openTableMenu(self, position): """ context menu event """ menu = QtWidgets.QMenu(self) exportAction = menu.addAction("Export table as CSV") action = menu.exec_(self.tableView.viewport().mapToGlobal(position)) if action == exportAction: fname, _ = QtWidgets.QFileDialog.getSaveFileName( self, "Save File", "CSV (*.csv)") self.tablemodel.SaveTable(fname) else: return return def openchrom(self): fname, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file') if fname and isfile(fname): self.lineEdit.setText(fname) def about(self): adialog = AboutDialog() adialog.changeTitle("ChromAnalyzer") adialog.exec_() def quit(self): QtWidgets.QApplication.quit() def analyze(self): if self.lineEdit.text() and isfile(self.lineEdit.text()): #Clean previous table del self.tablemodel.arraydata[:] del self.tablemodel.header[:] self.tablemodel.clean() #set table header header = [ "Peak", "Peak points", "time min", "time max", "Mu1", "Mu2", "Area" ] self.tablemodel.setHeader(header) #clean prevoius plot self.ax.cla() signal = [] time = [] f = open(self.lineEdit.text()) for line in f: v = str.split(line.strip(), "\t") if len(v) == 2: time.append(float(v[0])) signal.append(float(v[1])) else: continue f.close() #plot entire chromatogram self.ax.plot(time, signal, "b") #Analyse peaks h = self.peakthreshold.value() k = self.windowsize.value() chrom = ChromAnalysis(time, signal, k, h) peaks = chrom.getPeaks() peaklst = chrom.peaksplit(peaks) nleftpnt = self.leftpoints.value() nrightpnt = self.rightpoints.value() pn = 1 for i in range(len(peaklst)): time_ = [] signal_ = [] #print "-"*10 chrom.addLeftNpoints(peaklst[i], nleftpnt) chrom.addRighNpoints(peaklst[i], nrightpnt) for row in peaklst[i]: #print row[0], row[1] time_.append(row[0]) signal_.append(row[1]) if len(time_) > 15: # fill peak detected! self.ax.fill(time_, signal_, zorder=10) self.ax.grid(True, zorder=5) u1 = chrom.Mu1CentralMoment(time_, signal_) u2 = chrom.Mu2CentralMoment(time_, signal_, u1) area = chrom.integrate(signal_, 3) self.tablemodel.addRow( [pn, len(time_), min(time_), max(time_), u1, u2, area]) pn += 1 #self.ax.grid(True, zorder=5) plt.xlabel('Time') plt.ylabel('Signal') self.canvas.draw() return else: return
class Widgetmain(QtWidgets.QWidget): def __init__(self, parent=None): super(Widgetmain, self).__init__(parent) self.figure = Figure() # plt.subplots_adjust(left=0.031, right=0.999, top=0.99, bottom=0.03) # plt.subplots_adjust(left=0.001, right=0.999, top=0.999, bottom=0.001) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) # uncomment for disabling plot toolbar, not recommended self.toolbar.hide() # set the layout self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas) self.setLayout(self.layout) # set the plot class handlers self.wzoom = plot_tools.WheellZoom() self.adapt = plot_tools.WindowResize() # keep tight layout self.cursor = plot_tools.SnaptoCursor() self.selector = plot_tools.Select() self.selection = plot_tools.Selection() self.dataplot = None def setStatus(self, stat): self.statusbar = stat def save_plot(self): self.toolbar.save_figure() def axis_configure(self): self.toolbar.edit_parameters() def plot(self, xaxis=None, real=None, imag=None, magn=None, hxlimit=None, lxlimit=None, datas=None): # self.dataplot.clear() # if self.dataplot: # self.figure.delaxes(self.dataplot) if self.dataplot is None: self.dataplot = self.figure.add_subplot(111) self.figure.patch.set_facecolor("white") self.selector.setAx(self.dataplot, hxlimit, lxlimit, xaxis.size, datas, self.selection) # self.dataplot = self.figure.add_subplot(111) # self.selector.setAx(self.dataplot, hxlimit, lxlimit, xaxis.size, datas, self.selection) self.statusbar.showMessage("Plot updated") self.dataplot.hold(False) self.dataplot.plot(xaxis, real, "r-", xaxis, imag, "g-", xaxis, magn, "-") # uncomment for matlibplot standard cursor # cursor = Cursor(dataplot, useblit=True, color='black', linewidth=1 ) self.wzoom.setAx(self.dataplot, self.selection, datas) self.cursor.setAx(datas, self.dataplot, xaxis, real, self.statusbar) self.adapt.setAx(self.dataplot, self.figure) self.selection.setAx(self.dataplot, datas) self.figure.tight_layout() # dataplot.set_autoscaley_on(False) for auto x scale # setting background color # self.figure.patch.set_visible(False) # self.figure.patch.set_facecolor('white') self.dataplot.set_xlim([lxlimit, hxlimit]) # uncomment the following line for raw axis label inside plot # self.dataplot.tick_params(direction='in', pad=-19) # uncomment the following lines to disable plot toolbar mouse coordinates # def format_coord(x, y): # return ' ' # self.dataplot.format_coord = format_coord self.canvas.draw()
class PyWeramiWindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, filename=None, parent=None): super(PyWeramiWindow, self).__init__(parent) self.settings = QtCore.QSettings("LX", "pywerami") self.setupUi(self) self._fig = Figure(facecolor="white") self._ax = self._fig.add_subplot(111) self._canvas = FigureCanvas(self._fig) self._canvas.setParent(self.widget) self._canvas.setFocusPolicy(QtCore.Qt.StrongFocus) self.matplot.addWidget(self._canvas) self.mpl_toolbar = NavigationToolbar(self._canvas, self.widget) self.mpl_toolbar.hide() self.matplot.addWidget(self.mpl_toolbar) self.setWindowTitle('PyWerami') window_icon = resource_filename(__name__, 'images/pywerami.png') self.setWindowIcon(QtGui.QIcon(window_icon)) self.about_dialog = AboutDialog(__version__) #set combos self.cmaps = [ 'viridis', 'inferno', 'plasma', 'magma', 'Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter', 'BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic', 'gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism' ] self.mapstyle.addItems(self.cmaps) # set validators self.levelmin.setValidator(QtGui.QDoubleValidator(self.levelmin)) self.levelmax.setValidator(QtGui.QDoubleValidator(self.levelmax)) self.levelnum.setValidator(QtGui.QIntValidator(self.levelnum)) self.levelstep.setValidator(QtGui.QDoubleValidator(self.levelstep)) self.clipmin.setValidator(QtGui.QDoubleValidator(self.clipmin)) self.clipmax.setValidator(QtGui.QDoubleValidator(self.clipmax)) # Set icons in toolbar self.actionOpen.setIcon(QtGui.QIcon.fromTheme('document-open')) self.actionSave.setIcon(QtGui.QIcon.fromTheme('document-save')) self.actionSaveas.setIcon(QtGui.QIcon.fromTheme('document-save-as')) self.actionImport.setIcon( QtGui.QIcon.fromTheme('x-office-spreadsheet')) self.actionHome.setIcon(self.mpl_toolbar._icon('home.png')) self.actionPan.setIcon(self.mpl_toolbar._icon('move.png')) self.actionZoom.setIcon(self.mpl_toolbar._icon('zoom_to_rect.png')) self.actionGrid.setIcon(QtGui.QIcon.fromTheme('format-justify-fill')) self.actionAxes.setIcon( self.mpl_toolbar._icon('qt4_editor_options.png')) self.actionSavefig.setIcon(self.mpl_toolbar._icon('filesave.png')) #self.action3D.setIcon(QtGui.QIcon.fromTheme('')) self.actionProperties.setIcon( QtGui.QIcon.fromTheme('preferences-other')) self.actionQuit.setIcon(QtGui.QIcon.fromTheme('application-exit')) self.actionAbout.setIcon(QtGui.QIcon.fromTheme('help-about')) # connect signals self.actionOpen.triggered.connect(self.openProject) self.actionSave.triggered.connect(self.saveProject) self.actionSaveas.triggered.connect(self.saveProjectAs) self.actionImport.triggered.connect(self.import_data) self.actionHome.triggered.connect(self.mpl_toolbar.home) self.actionPan.triggered.connect(self.plotpan) self.actionZoom.triggered.connect(self.plotzoom) self.actionGrid.triggered.connect(self.plotgrid) self.actionAxes.triggered.connect(self.mpl_toolbar.edit_parameters) self.actionSavefig.triggered.connect(self.mpl_toolbar.save_figure) self.actionProperties.triggered.connect(self.edit_options) self.actionQuit.triggered.connect(self.close) self.actionAbout.triggered.connect(self.about_dialog.exec) # buttons signals self.buttonBox.button( QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply_props) self.buttonBox.button( QtWidgets.QDialogButtonBox.RestoreDefaults).clicked.connect( self.restore_props) self.contcolor.clicked.connect(self.contours_color) self.action3D.triggered.connect(self.switch3d) # signals to calculate step size self.levelmin.editingFinished.connect(self.step_from_levels) self.levelmax.editingFinished.connect(self.step_from_levels) self.levelnum.editingFinished.connect(self.step_from_levels) self.setlevels.toggled.connect(self.step_from_levels) # almost done self.ready = False self.changed = False self.project = None if filename: self.import_data(filename) # ready self.statusbar.showMessage("Ready", 5000) def closeEvent(self, event): if self.changed: quit_msg = 'Project have been changed. Save ?' qb = QtWidgets.QMessageBox reply = qb.question(self, 'Message', quit_msg, qb.Cancel | qb.Discard | qb.Save, qb.Save) if reply == qb.Save: self.do_save() if self.project is not None: event.accept() else: event.ignore() elif reply == qb.Discard: event.accept() else: event.ignore() def import_data(self, filename=None): if not filename: filename = QtWidgets.QFileDialog.getOpenFileName( self, "Import data file", ".", "Perple_X Table (*.tab *.TAB);;TCInvestigator (*.tci *.TCI)" )[0] if filename: if filename.lower().endswith('.tab'): self.data = GridData.from_tab(filename) elif filename.lower().endswith('.tci'): self.data = GridData.from_tci(filename) else: raise Exception('Unsupported file format') # populate listview and setup properties self.datafilename = filename self.ready = True self.project = None self.changed = True self.props = {} self._model = QtGui.QStandardItemModel(self.listView) for var in self.data.dep: item = QtGui.QStandardItem(var) item.setCheckable(True) self._model.appendRow(item) self.default_var_props(var) self.listView.setModel(self._model) self.listView.show() # connect listview signals self.varSel = self.listView.selectionModel() try: elf.varSel.selectionChanged.disconnect() except Exception: pass self.varSel.selectionChanged.connect(self.on_var_changed) try: self._model.itemChanged.disconnect() except Exception: pass self._model.itemChanged.connect(self.plot) # all done focus self.action3D.setChecked(False) # no 3d on import self.varSel.setCurrentIndex( self._model.index(0, 0), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.Rows) self.listView.setFocus() self.plot() self.statusbar.showMessage( "Data from {} imported".format(self.data.label), 5000) def openProject(self, checked, projfile=None): """Open pywerami project """ if self.changed: quit_msg = 'Project have been changed. Save ?' qb = QtWidgets.QMessageBox reply = qb.question(self, 'Message', quit_msg, qb.Discard | qb.Save, qb.Save) if reply == qb.Save: self.do_save() if projfile is None: qd = QtWidgets.QFileDialog filt = 'pywermi project (*.pwp)' projfile = qd.getOpenFileName(self, 'Open project', os.path.expanduser('~'), filt)[0] if os.path.exists(projfile): stream = gzip.open(projfile, 'rb') data = pickle.load(stream) stream.close() # set actual working dir in case folder was moved self.datafilename = data['datafilename'] self.import_data(self.datafilename) self.props = data['props'] # all done self.ready = True self.project = projfile self.changed = False # all done focus self.action3D.setChecked(False) # no 3d on import self.varSel.setCurrentIndex( self._model.index(0, 0), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.Rows) self.listView.setFocus() self.plot() self.statusbar.showMessage("Project loaded.", 5000) def saveProject(self): """Save project """ if self.ready: if self.project is None: filename = QtWidgets.QFileDialog.getSaveFileName( self, 'Save current project', os.path.dirname(self.datafilename), 'pywerami project (*.pwp)')[0] if filename: if not filename.lower().endswith('.pwp'): filename = filename + '.pwp' self.project = filename self.do_save() else: self.do_save() def saveProjectAs(self): """Save project as """ if self.ready: filename = QtWidgets.QFileDialog.getSaveFileName( self, 'Save current project as', os.path.dirname(self.datafilename), 'pywerami project (*.pwp)')[0] if filename: if not filename.lower().endswith('.pwp'): filename = filename + '.pwp' self.project = filename self.do_save() def do_save(self): """Do saving of poject """ if self.project: # put to dict data = {'datafilename': self.datafilename, 'props': self.props} # do save stream = gzip.open(self.project, 'wb') pickle.dump(data, stream) stream.close() self.changed = False self.statusBar().showMessage('Project saved.') def contours_color(self): if self.ready: col = QtWidgets.QColorDialog.getColor() if col.isValid(): self.contcolor.setStyleSheet("background-color: {}".format( col.name())) def step_from_levels(self): if self.ready: if int(self.levelnum.text()) < 2: self.levelnum.setText('2') if float(self.levelmax.text()) < float(self.levelmin.text()): self.levelmin.setText(self.levelmax.text()) if self.setlevels.isChecked(): step = (float(self.levelmax.text()) - float( self.levelmin.text())) / (int(self.levelnum.text()) - 1) self.levelstep.setText(repr(step)) self.props[self.var]['step'] = step self.changed = True def default_var_props(self, var): if self.ready: data = self.data.get_var(var) prop = {} #levels prop['min'] = data.min() prop['max'] = data.max() prop['num'] = 10 prop['step'] = (prop['max'] - prop['min']) / (prop['num'] - 1) prop['levels'] = 'num' prop['type'] = 'linear' #style prop['fill'] = False prop['opacity'] = 100 prop['cmap'] = 'viridis' prop['contours'] = 'color' prop['color'] = '#000000' prop['label'] = False prop['digits'] = 3 #processing prop['resample'] = 1 prop['median'] = 1 prop['gauss'] = 0 prop['clipmin'] = data.min() prop['clipmax'] = data.max() self.props[var] = prop def set_var_props(self, var): if self.ready: #levels self.levelmin.setText(repr(self.props[var]['min'])) self.levelmax.setText(repr(self.props[var]['max'])) self.levelnum.setText(repr(self.props[var]['num'])) self.levelstep.setText(repr(self.props[var]['step'])) if self.props[var]['levels'] == 'num': self.setlevels.setChecked(True) else: self.setstep.setChecked(True) if self.props[var]['type'] == 'linear': self.linlevel.setChecked(True) else: self.cdflevel.setChecked(True) #style if self.props[var]['fill']: self.fillstyle.setChecked(True) else: self.fillstyle.setChecked(False) self.opacity.setValue(self.props[var]['opacity']) self.mapstyle.setCurrentIndex( self.cmaps.index(self.props[var]['cmap'])) self.contcolor.setStyleSheet("background-color: {}".format( self.props[var]['color'])) self.labelDigits.setValue(self.props[var]['digits']) if self.props[var]['contours'] == 'map': self.contcheckmap.setChecked(True) elif self.props[var]['contours'] == 'color': self.contcheckcolor.setChecked(True) else: self.contchecknone.setChecked(True) if self.props[var]['label']: self.contlabel.setChecked(True) else: self.contlabel.setChecked(False) #processing self.resample.setValue(self.props[var]['resample']) self.filtersize.setValue(self.props[var]['median']) self.filtersigma.setValue(self.props[var]['gauss']) self.clipmin.setText(repr(self.props[var]['clipmin'])) self.clipmax.setText(repr(self.props[var]['clipmax'])) def on_var_changed(self, selected): if self.ready: self.var = self.data.dep[selected.indexes()[0].row()] self.set_var_props(self.var) if self.action3D.isChecked(): self.plot() def apply_props(self): if self.ready: #levels self.props[self.var]['min'] = float(self.levelmin.text()) self.props[self.var]['max'] = float(self.levelmax.text()) self.props[self.var]['num'] = int(self.levelnum.text()) self.props[self.var]['step'] = float(self.levelstep.text()) if self.setlevels.isChecked(): self.props[self.var]['levels'] = 'num' else: self.props[self.var]['levels'] = 'step' if self.linlevel.isChecked(): self.props[self.var]['type'] = 'linear' else: self.props[self.var]['type'] = 'cdf' #style if self.fillstyle.isChecked(): self.props[self.var]['fill'] = True else: self.props[self.var]['fill'] = False self.props[self.var]['opacity'] = self.opacity.value() self.props[self.var]['cmap'] = str(self.mapstyle.currentText()) self.props[self.var]['color'] = str( self.contcolor.palette().color(1).name()) self.props[self.var]['digits'] = self.labelDigits.value() if self.contcheckmap.isChecked(): self.props[self.var]['contours'] = 'map' elif self.contcheckcolor.isChecked(): self.props[self.var]['contours'] = 'color' else: self.props[self.var]['contours'] = '' if self.contlabel.isChecked(): self.props[self.var]['label'] = True else: self.props[self.var]['label'] = False #processing self.props[self.var]['resample'] = self.resample.value() self.props[self.var]['median'] = self.filtersize.value() self.props[self.var]['gauss'] = self.filtersigma.value() self.props[self.var]['clipmin'] = float(self.clipmin.text()) self.props[self.var]['clipmax'] = float(self.clipmax.text()) self.changed = True self.plot() def restore_props(self): if self.ready: self.default_var_props(self.var) self.set_var_props(self.var) self.plot() def edit_options(self): dlg = OptionsForm(self) dlg.exec_() def plotpan(self): self.actionZoom.setChecked(False) self.mpl_toolbar.pan() def plotzoom(self): self.actionPan.setChecked(False) self.mpl_toolbar.zoom() def plotgrid(self): self._ax.grid() self._canvas.draw() def switch3d(self): if self.ready: if not self.action3D.isChecked(): self._fig.clear() self._ax = self._fig.add_subplot(111) else: self._fig.clear() self._ax = self._fig.add_subplot(111, projection='3d') self.plot() def plot(self, item=None): if self.ready: self._ax.cla() if item: index = self._model.createIndex(item.row(), item.column()) if index.isValid(): self.listView.setCurrentIndex(index) if not self.action3D.isChecked(): extent = self.data.get_extent() i = 0 while self._model.item(i): if self._model.item(i).checkState(): CS = None var = str(self._model.item(i).text()) # get data, smooth and clip data = self.data.get_var(var, nan=np.float( self.settings.value( "nan", "NaN", type=str))) if self.props[var]['resample'] > 1: data = np.ma.array( ndimage.zoom(data.filled(0), self.props[var]['resample']), mask=ndimage.zoom(data.mask, self.props[var]['resample'], order=0)) if self.props[var]['median'] > 1: data = np.ma.array(ndimage.median_filter( data, size=self.props[var]['median'] * self.props[var]['resample']), mask=data.mask) if self.props[var]['gauss'] > 0: data = np.ma.array(ndimage.gaussian_filter( data, sigma=self.props[var]['gauss'] * self.props[var]['resample']), mask=data.mask) data = np.ma.masked_outside(data, self.props[var]['clipmin'], self.props[var]['clipmax']) if self.props[var]['fill']: self._ax.imshow( data, interpolation='none', origin='lower', extent=extent, aspect='auto', cmap=cm.get_cmap(self.props[var]['cmap']), alpha=self.props[var]['opacity'] / 100.0) if self.props[var]['min'] == self.props[var]['max']: clevels = np.array([self.props[var]['min']]) else: if self.props[var]['type'] == 'linear': if self.props[var]['levels'] == 'num': clevels = np.linspace( self.props[var]['min'], self.props[var]['max'], self.props[var]['num']) else: # trick to include max in levels clevels = np.arange( self.props[var]['min'], self.props[var]['max'] + np.finfo(np.float32).eps, self.props[var]['step']) else: # cdf based on histogram binned acording to the Freedman-Diaconis rule data = np.ma.masked_outside( data, self.props[var]['min'], self.props[var]['max']) v = np.sort(data.compressed()) IQR = v[int(round( (v.size - 1) * float(0.75)))] - v[int( round((v.size - 1) * float(0.25)))] bin_size = 2 * IQR * v.size**(-1.0 / 3) nbins = int( round( max(self.props[var]['num'], (v[-1] - v[0]) / (bin_size + 0.001)))) hist, bin_edges = np.histogram(v, bins=nbins) cdf = np.cumsum(hist) cdfx = np.cumsum(np.diff( bin_edges)) + bin_edges[:2].sum() / 2 #clevels = np.interp(np.linspace(cdf[0],cdf[-1],self.props[var]['num'] + 2)[1:-1], cdf, cdfx) clevels = np.interp( np.linspace(cdf[0], cdf[-1], self.props[var]['num']), cdf, cdfx) clevels = np.round( 10**self.props[var]['digits'] * clevels) / 10**self.props[var]['digits'] # Contour levels must be increasing clevels = np.append(clevels[:1], clevels[1:][np.diff(clevels) > 0]) if self.props[var]['contours'] == 'map': CS = self._ax.contour( self.data.get_xrange( self.props[var]['resample']), self.data.get_yrange( self.props[var]['resample']), data, clevels, cmap=cm.get_cmap(self.props[var]['cmap'])) elif self.props[var]['contours'] == 'color': CS = self._ax.contour( self.data.get_xrange( self.props[var]['resample']), self.data.get_yrange( self.props[var]['resample']), data, clevels, colors=self.props[var]['color']) if self.props[var]['label'] and CS: self._ax.clabel(CS, fontsize=8, inline=1, fmt='%g') i += 1 self._ax.axis(extent) self._ax.set_title(self.data.label) else: # get data, smooth and clip data = self.data.get_var(self.var) if self.props[self.var]['resample'] > 1: data = np.ma.array( ndimage.zoom(data.filled(0), self.props[self.var]['resample']), mask=ndimage.zoom(data.mask, self.props[self.var]['resample'], order=0)) if self.props[self.var]['median'] > 1: data = np.ma.array(ndimage.median_filter( data, size=self.props[self.var]['median'] * self.props[self.var]['resample']), mask=data.mask) if self.props[self.var]['gauss'] > 0: data = np.ma.array(ndimage.gaussian_filter( data, sigma=self.props[self.var]['gauss'] * self.props[self.var]['resample']), mask=data.mask) data = np.ma.masked_outside(data, self.props[self.var]['clipmin'], self.props[self.var]['clipmax']) x, y = np.meshgrid( self.data.get_xrange(self.props[self.var]['resample']), self.data.get_yrange(self.props[self.var]['resample'])) self._ax.plot_surface( x, y, data.filled(np.NaN), vmin=data.min(), vmax=data.max(), cmap=cm.get_cmap(self.props[self.var]['cmap']), linewidth=0.5, alpha=self.props[self.var]['opacity'] / 100.0) self._ax.view_init(azim=235, elev=30) self._ax.set_xlabel(self.data.ind[self.data.xvar]['name']) self._ax.set_ylabel(self.data.ind[self.data.yvar]['name']) self._fig.tight_layout() self._canvas.draw()
class MercuryMonitorApp(QtWidgets.QMainWindow): def __init__(self, feed): super(self.__class__, self).__init__() uic.loadUi(MAIN_UI_PATH, self) self.feed = feed # create popup Widgets self.connection_dialog = ConnectionDialog(self, feed.mercury) self.readingsWindow = None # create LED indicator self.led = LedIndicator(self) self.statusbar.addPermanentWidget(self.led) self.led.setChecked(False) # set up figure for plotting self.canvas = MercuryPlotCanvas(self) self.gridLayoutCanvas.addWidget(self.canvas) self.canvas.draw() # adapt text edit colors to graph colors self.t1_reading.setStyleSheet('color:rgb%s' % str(tuple(self.canvas.GREEN * 255))) self.gf1_edit.setStyleSheet('color:rgb%s' % str(tuple(self.canvas.BLUE * 255))) self.h1_edit.setStyleSheet('color:rgb%s' % str(tuple(self.canvas.RED * 255))) self.gf1_unit.setStyleSheet('color:rgb%s' % str(tuple(self.canvas.BLUE * 255))) self.h1_unit.setStyleSheet('color:rgb%s' % str(tuple(self.canvas.RED * 255))) # allow panning of plot self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() self.toolbar.pan() # set up data vectors for plot self.xdata = np.array([]) self.xdata_zero = np.array([]) self.ydata_tmpr = np.array([]) self.ydata_gflw = np.array([]) self.ydata_htr = np.array([]) # restore previous window geometry self.restore_geometry() # Connect menu bar actions self.set_up_menubar() # set input validators for all fields self.set_input_validators() # check if mercury is connected, connect slots self.display_message('Looking for Mercury at %s...' % self.feed.visa_address) if self.feed.mercury.connected: self.update_gui_connection(connected=True) # start (stop) updates of GUI when mercury is connected (disconnected) # adjust clickable buttons upon connect / disconnect self.feed.connected_signal.connect(self.update_gui_connection) # get new readings when available, send as out signals self.feed.new_readings_signal.connect(self.fetch_readings) # update plot when new data arrives self.feed.new_readings_signal.connect(self.update_plot_data) # check for overheating when new data arrives self.feed.new_readings_signal.connect(self._check_overheat) # set up logging to file self.setup_logging() # =================== BASIC UI SETUP ========================================== def restore_geometry(self): x = CONF.get('Window', 'x') y = CONF.get('Window', 'y') w = CONF.get('Window', 'width') h = CONF.get('Window', 'height') self.setGeometry(x, y, w, h) def save_geometry(self): geo = self.geometry() CONF.set('Window', 'height', geo.height()) CONF.set('Window', 'width', geo.width()) CONF.set('Window', 'x', geo.x()) CONF.set('Window', 'y', geo.y()) def exit_(self): self.feed.exit_() self.save_geometry() self.deleteLater() def closeEvent(self, event): self.exit_() def set_up_menubar(self): """ Connects menu bar items to callbacks, sets their initial activation. """ # connect to callbacks self.showLogAction.triggered.connect(self.on_log_clicked) self.exitAction.triggered.connect(self.exit_) self.readingsAction.triggered.connect(self.on_readings_clicked) self.connectAction.triggered.connect(self.feed.connect) self.disconnectAction.triggered.connect(self.feed.disconnect) self.updateAddressAction.triggered.connect(self.connection_dialog.open) # initially disable menu bar items, will be enabled later individually self.connectAction.setEnabled(True) self.disconnectAction.setEnabled(False) self.modulesAction.setEnabled(False) self.readingsAction.setEnabled(False) @QtCore.Slot(bool) def update_gui_connection(self, connected): if connected: self.display_message('Connection established.') self.led.setChecked(True) # enable / disable menu bar items self.connectAction.setEnabled(False) self.disconnectAction.setEnabled(True) self.modulesAction.setEnabled(True) self.readingsAction.setEnabled(True) # connect user input to change mercury settings self.t2_edit.returnPressed.connect(self.change_t_setpoint) self.r1_edit.returnPressed.connect(self.change_ramp) self.r2_checkbox.clicked.connect(self.change_ramp_auto) self.gf1_edit.returnPressed.connect(self.change_flow) self.gf2_checkbox.clicked.connect(self.change_flow_auto) self.h1_edit.returnPressed.connect(self.change_heater) self.h2_checkbox.clicked.connect(self.change_heater_auto) # set update_plot to be executed every time the slider position changes self.horizontalSlider.valueChanged.connect(self.update_plot) elif not connected: self.display_error('Connection lost.') logger.info('Connection to MercuryiTC lost.') self.led.setChecked(False) # enable / disable menu bar items self.connectAction.setEnabled(True) self.disconnectAction.setEnabled(False) self.modulesAction.setEnabled(False) self.readingsAction.setEnabled(False) # disconnect user input from mercury self.t2_edit.returnPressed.disconnect(self.change_t_setpoint) self.r1_edit.returnPressed.disconnect(self.change_ramp) self.r2_checkbox.clicked.disconnect(self.change_ramp_auto) self.gf1_edit.returnPressed.disconnect(self.change_flow) self.gf2_checkbox.clicked.disconnect(self.change_flow_auto) self.h1_edit.returnPressed.disconnect(self.change_heater) self.h2_checkbox.clicked.disconnect(self.change_heater_auto) # disconnect update_plot self.horizontalSlider.valueChanged.disconnect(self.update_plot) def set_input_validators(self): """ Sets validators for input fields""" self.t2_edit.setValidator(QtGui.QDoubleValidator()) self.r1_edit.setValidator(QtGui.QDoubleValidator()) self.gf1_edit.setValidator(QtGui.QDoubleValidator()) self.h1_edit.setValidator(QtGui.QDoubleValidator()) def display_message(self, text): self.statusbar.showMessage('%s' % text, 5000) def display_error(self, text): self.statusbar.showMessage('%s' % text) @QtCore.Slot(object) def fetch_readings(self, readings): """ Parses readings for the MercuryMonitorApp and updates UI accordingly """ # heater signals self.h1_label.setText('Heater, %s V:' % readings['HeaterVolt']) self.h1_edit.updateValue(readings['HeaterPercent']) is_heater_auto = readings['HeaterAuto'] == 'ON' self.h1_edit.setReadOnly(is_heater_auto) self.h1_edit.setEnabled(not is_heater_auto) self.h2_checkbox.setChecked(is_heater_auto) # gas flow signals self.gf1_edit.updateValue(readings['FlowPercent']) self.gf1_label.setText('Gas flow (min = %s%%):' % readings['FlowMin']) is_gf_auto = readings['FlowAuto'] == 'ON' self.gf2_checkbox.setChecked(is_gf_auto) self.gf1_edit.setEnabled(not is_gf_auto) self.gf1_edit.setReadOnly(is_gf_auto) # temperature signals self.t1_reading.setText('%s K' % round(readings['Temp'], 3)) self.t2_edit.updateValue(readings['TempSetpoint']) self.r1_edit.updateValue(readings['TempRamp']) is_ramp_enable = readings['TempRampEnable'] == 'ON' self.r2_checkbox.setChecked(is_ramp_enable) @QtCore.Slot(object) def update_plot_data(self, readings): # append data for plotting self.xdata = np.append(self.xdata, time.time()) self.ydata_tmpr = np.append(self.ydata_tmpr, readings['Temp']) self.ydata_gflw = np.append(self.ydata_gflw, readings['FlowPercent'] / 100) self.ydata_htr = np.append(self.ydata_htr, readings['HeaterPercent'] / 100) # prevent data vector from exceeding 86400 entries self.xdata = self.xdata[-86400:] self.ydata_tmpr = self.ydata_tmpr[-86400:] self.ydata_gflw = self.ydata_gflw[-86400:] self.ydata_htr = self.ydata_htr[-86400:] # convert xData to minutes and set current time to t = 0 self.xdata_zero = (self.xdata - max(self.xdata)) / 60 self.update_plot() @QtCore.Slot() def update_plot(self): # select data to be plotted x_slice = self.xdata_zero >= -self.horizontalSlider.value() self.current_xdata = self.xdata_zero[x_slice] self.current_ydata_tmpr = self.ydata_tmpr[x_slice] self.current_ydata_gflw = self.ydata_gflw[x_slice] self.current_ydata_htr = self.ydata_htr[x_slice] # determine first plotted data point if self.current_xdata.size == 0: x_min = -self.horizontalSlider.value() else: x_min = max(-self.horizontalSlider.value(), self.current_xdata[0]) # update plot self.canvas.update_plot(self.current_xdata, self.current_ydata_tmpr, self.current_ydata_gflw, self.current_ydata_htr, x_min) # update label self.timeLabel.setText('Show last %s min' % self.horizontalSlider.value()) # =================== LOGGING DATA ============================================ def setup_logging(self): """ Set up logging of temperature history to files. Save temperature history to log file at '~/.CustomXepr/LOG_FILES/' after every 10 min. """ # find user home directory home_path = os.path.expanduser('~') self.logging_path = os.path.join(home_path, '.mercurygui', 'LOG_FILES') # create folder '~/.CustomXepr/LOG_FILES' if not present if not os.path.exists(self.logging_path): os.makedirs(self.logging_path) # set logging file path self.log_file = os.path.join( self.logging_path, 'temperature_log ' + time.strftime("%Y-%m-%d_%H-%M-%S") + '.txt') t_save = 10 # time interval to save temperature data in min self.new_file = True # create new log file for every new start self.save_timer = QtCore.QTimer() self.save_timer.setInterval(t_save * 60 * 1000) self.save_timer.setSingleShot(False) # set to reoccur self.save_timer.timeout.connect(self.log_temperature_data) self.save_timer.start() def save_temperature_data(self, path=None): # prompt user for file path if not given if path is None: text = 'Select path for temperature data file:' path = QtWidgets.QFileDialog.getSaveFileName(caption=text) path = path[0] if not path.endswith('.txt'): path += '.txt' title = 'temperature trace, saved on ' + time.strftime( '%d/%m/%Y') + '\n' heater_vlim = self.feed.heater.vlim header = '\t'.join([ 'Time (sec)', 'Temperature (K)', 'Heater (%% of %sV)' % heater_vlim, 'Gas flow (%)' ]) data_matrix = np.concatenate( (self.xdata[:, np.newaxis], self.ydata_tmpr[:, np.newaxis], self.ydata_htr[:, np.newaxis], self.ydata_gflw[:, np.newaxis]), axis=1) # noinspection PyTypeChecker np.savetxt(path, data_matrix, delimiter='\t', header=title + header) def log_temperature_data(self): # save temperature data to log file if self.feed.mercury.connected: self.save_temperature_data(self.log_file) # =================== CALLBACKS FOR SETTING CHANGES =========================== @QtCore.Slot() def change_t_setpoint(self): new_t = self.t2_edit.value() if 3.5 < new_t < 300: self.display_message('T_setpoint = %s K' % new_t) self.feed.control.t_setpoint = new_t else: self.display_error('Error: Only temperature setpoints between ' + '3.5 K and 300 K allowed.') @QtCore.Slot() def change_ramp(self): self.feed.control.ramp = self.r1_edit.value() self.display_message('Ramp = %s K/min' % self.r1_edit.value()) @QtCore.Slot(bool) def change_ramp_auto(self, checked): if checked: self.feed.control.ramp_enable = 'ON' self.display_message('Ramp is turned ON') else: self.feed.control.ramp_enable = 'OFF' self.display_message('Ramp is turned OFF') @QtCore.Slot() def change_flow(self): self.feed.control.flow = self.gf1_edit.value() self.display_message('Gas flow = %s%%' % self.gf1_edit.value()) @QtCore.Slot(bool) def change_flow_auto(self, checked): if checked: self.feed.control.flow_auto = 'ON' self.display_message('Gas flow is automatically controlled.') self.gf1_edit.setReadOnly(True) self.gf1_edit.setEnabled(False) else: self.feed.control.flow_auto = 'OFF' self.display_message('Gas flow is manually controlled.') self.gf1_edit.setReadOnly(False) self.gf1_edit.setEnabled(True) @QtCore.Slot() def change_heater(self): self.feed.control.heater = self.h1_edit.value() self.display_message('Heater power = %s%%' % self.h1_edit.value()) @QtCore.Slot(bool) def change_heater_auto(self, checked): if checked: self.feed.control.heater_auto = 'ON' self.display_message('Heater is automatically controlled.') self.h1_edit.setReadOnly(True) self.h1_edit.setEnabled(False) else: self.feed.control.heater_auto = 'OFF' self.display_message('Heater is manually controlled.') self.h1_edit.setReadOnly(False) self.h1_edit.setEnabled(True) @QtCore.Slot(object) def _check_overheat(self, readings): if readings['Temp'] > 310: self.display_error('Over temperature!') self.feed.control.heater_auto = 'OFF' self.feed.control.heater = 0 # ========================== CALLBACKS FOR MENU BAR =========================== @QtCore.Slot() def on_readings_clicked(self): # create readings overview window if not present if self.readingsWindow is None: self.readingsWindow = ReadingsOverview(self.feed.mercury) # show it self.readingsWindow.show() @QtCore.Slot() def on_log_clicked(self): """ Opens directory with log files with current log file selected. """ if platform.system() == 'Windows': os.startfile(self.logging_path) elif platform.system() == 'Darwin': subprocess.Popen(['open', self.logging_path]) else: subprocess.Popen(['xdg-open', self.logging_path])
class PlotMaps(QtWidgets.QDialog, Ui_PlotMaps): def __init__(self, modellst, type="sel", parent=None): QtWidgets.QDialog.__init__(self, parent) self.setupUi(self) # Create the plot #self.figure = plt.figure() self.figure, self.axes = plt.subplots(nrows=3, ncols=1) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # set the layout layout = QtWidgets.QVBoxLayout() layout.addWidget(self.canvas) self.plotterBox.setLayout(layout) self.closeButton.clicked.connect(self.close_) if type == "sel": self.modelBox.currentIndexChanged.connect(self.plotselectivitymap) else: self.modelBox.currentIndexChanged.connect(self.plotresolutionmap) self.modellst = modellst for model in self.modellst: self.modelBox.addItem(model.modname) def close_(self): self.reject() def plotchromatogram(self): ''' plot some random stuff ''' data = [random.random() for i in range(25)] ax = self.figure.add_subplot(111) ax.hold(False) ax.plot(data, '*-') self.canvas.draw() def plotresolutionmap(self): indx = self.modelBox.currentIndex() if indx >= 0 and indx < len(self.modellst): lss = self.modellst[indx].lss flow_sofware = self.modellst[indx].flow v_m = self.modellst[indx].v_m v_d = self.modellst[indx].v_d c_particle = self.modellst[indx].c_particle c_length = self.modellst[indx].c_length opt = OptSep(v_m, v_d, flow_sofware, lss) opt.c_particle = c_particle opt.c_length = c_length [gcondlst, reslst, trlst] = opt.getResMapPlot("lss", float(flow_sofware), g_start_min=0.00, g_start_max=0.30, g_stop_min=0.50, g_stop_max=1.0, time_grad_min=2, time_grad_max=60) #Plot resolution map x = [] y_gsteepness = [] y_final_b = [] y_tg = [] z = [] for i in range(len(gcondlst)): #gcondlst.append([init_b, final_b, tg, self.flow, lowest_alpha]) x.append(float(gcondlst[i][0]) * 100) y_gsteepness.append( float((gcondlst[i][1] - gcondlst[i][0]) / gcondlst[i][2])) # alpha y_final_b.append(float(gcondlst[i][1]) * 100) # final b y_tg.append(float(gcondlst[i][2])) # tg z.append(float(gcondlst[i][-1])) x = np.asarray(x) y_gsteepness = np.asarray(y_gsteepness) y_final_b = np.asarray(y_final_b) y_tg = np.asarray(y_tg) z = np.asarray(z) # Set up a regular grid of interpolation points npoints = 500 xi, yi_gsteepness = np.linspace(x.min(), x.max(), npoints), np.linspace( y_gsteepness.min(), y_gsteepness.max(), npoints) xi, yi_gsteepness = np.meshgrid(xi, yi_gsteepness) xi, yi_final_b = np.linspace(x.min(), x.max(), npoints), np.linspace( y_final_b.min(), y_final_b.max(), npoints) xi, yi_final_b = np.meshgrid(xi, yi_final_b) xi, yi_tg = np.linspace(x.min(), x.max(), npoints), np.linspace( y_tg.min(), y_tg.max(), npoints) xi, yi_tg = np.meshgrid(xi, yi_tg) # Interpolate #rbf = scipy.interpolate.Rbf(x, y, z, function='linear') #zi = rbf(xi, yi) zi_gsteepness = scipy.interpolate.griddata((x, y_gsteepness), z, (xi, yi_gsteepness), method='linear') zi_final_b = scipy.interpolate.griddata((x, y_final_b), z, (xi, yi_final_b), method='linear') zi_tg = scipy.interpolate.griddata((x, y_tg), z, (xi, yi_tg), method='linear') #f, axarr = plt.subplots(3, sharex=True) #axes = self.figure.add_subplot(nrows=3, ncols=1) im = self.axes.flat[0].imshow(zi_gsteepness, vmin=z.min(), vmax=z.max(), origin='lower', extent=[ x.min(), x.max(), y_gsteepness.min(), y_gsteepness.max() ], aspect='auto') self.axes.flat[0].set_xlabel('Initial B (%)') self.axes.flat[0].set_ylabel('Gradient steepness') im = self.axes.flat[1].imshow( zi_final_b, vmin=z.min(), vmax=z.max(), origin='lower', extent=[x.min(), x.max(), y_final_b.min(), y_final_b.max()], aspect='auto') self.axes.flat[1].set_xlabel('Initial B (%)') self.axes.flat[1].set_ylabel('Final B (%)') im = self.axes.flat[2].imshow( zi_tg, vmin=z.min(), vmax=z.max(), origin='lower', extent=[x.min(), x.max(), y_tg.min(), y_tg.max()], aspect='auto') self.axes.flat[2].set_xlabel('Initial B (%)') self.axes.flat[2].set_ylabel('Time gradient (min)') self.figure.colorbar(im, ax=self.axes.ravel().tolist()) self.canvas.draw() def plotselectivitymap(self): plt.cla() indx = self.modelBox.currentIndex() if indx >= 0 and indx < len(self.modellst): lss = self.modellst[indx].lss flow_sofware = self.modellst[indx].flow v_m = self.modellst[indx].v_m v_d = self.modellst[indx].v_d opt = OptSep(v_m, v_d, flow_sofware, lss) [gcondlst, sellst, trlst] = opt.getSelMapPlot("lss", float(flow_sofware), g_start_min=0.00, g_start_max=0.30, g_stop_min=0.50, g_stop_max=1.0, time_grad_min=2, time_grad_max=60) #Plot selectivity map x = [] y_gsteepness = [] y_final_b = [] y_tg = [] z = [] for i in range(len(gcondlst)): #gcondlst.append([init_b, final_b, tg, self.flow, lowest_alpha]) x.append(float(gcondlst[i][0]) * 100) y_gsteepness.append( float((gcondlst[i][1] - gcondlst[i][0]) / gcondlst[i][2] + 1)) # alpha y_final_b.append(float(gcondlst[i][1]) * 100) # final b y_tg.append(float(gcondlst[i][2])) # tg z.append(float(gcondlst[i][-1])) x = np.asarray(x) y_gsteepness = np.asarray(y_gsteepness) y_final_b = np.asarray(y_final_b) y_tg = np.asarray(y_tg) z = np.asarray(z) # Set up a regular grid of interpolation points npoints = 500 xi, yi_gsteepness = np.linspace(x.min(), x.max(), npoints), np.linspace( y_gsteepness.min(), y_gsteepness.max(), npoints) xi, yi_gsteepness = np.meshgrid(xi, yi_gsteepness) xi, yi_final_b = np.linspace(x.min(), x.max(), npoints), np.linspace( y_final_b.min(), y_final_b.max(), npoints) xi, yi_final_b = np.meshgrid(xi, yi_final_b) xi, yi_tg = np.linspace(x.min(), x.max(), npoints), np.linspace( y_tg.min(), y_tg.max(), npoints) xi, yi_tg = np.meshgrid(xi, yi_tg) # Interpolate #rbf = scipy.interpolate.Rbf(x, y, z, function='linear') #zi = rbf(xi, yi) zi_gsteepness = scipy.interpolate.griddata((x, y_gsteepness), z, (xi, yi_gsteepness), method='linear') zi_final_b = scipy.interpolate.griddata((x, y_final_b), z, (xi, yi_final_b), method='linear') zi_tg = scipy.interpolate.griddata((x, y_tg), z, (xi, yi_tg), method='linear') #f, axarr = plt.subplots(3, sharex=True) #axes = self.figure.add_subplot(nrows=3, ncols=1) im = self.axes.flat[0].imshow(zi_gsteepness, vmin=z.min(), vmax=z.max(), origin='lower', extent=[ x.min(), x.max(), y_gsteepness.min(), y_gsteepness.max() ], aspect='auto') self.axes.flat[0].set_xlabel('Initial B (%)') self.axes.flat[0].set_ylabel('Gradient steepness') im = self.axes.flat[1].imshow( zi_final_b, vmin=z.min(), vmax=z.max(), origin='lower', extent=[x.min(), x.max(), y_final_b.min(), y_final_b.max()], aspect='auto') self.axes.flat[1].set_xlabel('Initial B (%)') self.axes.flat[1].set_ylabel('Final B (%)') im = self.axes.flat[2].imshow( zi_tg, vmin=z.min(), vmax=z.max(), origin='lower', extent=[x.min(), x.max(), y_tg.min(), y_tg.max()], aspect='auto') self.axes.flat[2].set_xlabel('Initial B (%)') self.axes.flat[2].set_ylabel('Time gradient (min)') self.figure.colorbar(im, ax=self.axes.ravel().tolist()) self.canvas.draw()
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("Data View")) #MainWindow.resize(539, 600) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.splitter = QtWidgets.QSplitter(self.centralwidget) self.splitter.setOrientation(QtCore.Qt.Horizontal) self.splitter.setObjectName(_fromUtf8("splitter")) # File navigation self.fileSystemModel = QtWidgets.QDirModel(self.splitter) self.fileSystemModel.setObjectName(_fromUtf8("fileSystemModel")) currentDir = str(QtCore.QDir.currentPath()) index = self.fileSystemModel.index(currentDir) self.treeView = QtWidgets.QTreeView(self.splitter) self.treeView.setObjectName(_fromUtf8("treeView")) self.treeView.setModel(self.fileSystemModel) if len(sys.argv)>1: self.currentFile = currentDir + '/' + sys.argv[1] else: self.currentFile = None self.recursive_expand( index, self.treeView ) tVheader = self.treeView.header() for i in range(1,4): tVheader.hideSection(i) self.treeView.doubleClicked.connect(self.on_treeView_doubleClicked) # Plots tab self.tabWidget = QtWidgets.QTabWidget(self.splitter) self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.tab = QtWidgets.QWidget() self.tab.setObjectName(_fromUtf8("tab")) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) self.figure = plt.figure(figsize=(15,5)) self.canvas = FigureCanvas(self.figure) self.canvas.setObjectName(_fromUtf8("canvas")) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() self.verticalLayout_2.addWidget(self.canvas) # Plot buttons self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout_plotButtons")) self.plotTypeCombo = QtWidgets.QComboBox(self.tab) self.plotTypeCombo.setObjectName(_fromUtf8("plotTypeCombo")) self.plotTypeCombo.currentIndexChanged.connect(self.ChangePlotType) self.horizontalLayout.addWidget(self.plotTypeCombo) self.btnPlot = QtWidgets.QPushButton(self.tab) self.btnPlot.setObjectName(_fromUtf8("btnPlot")) self.btnPlot.clicked.connect(self.drawPlot) self.horizontalLayout.addWidget(self.btnPlot) self.btnZoom = QtWidgets.QPushButton(self.tab) self.btnZoom.setObjectName(_fromUtf8("btnZoom")) self.btnZoom.clicked.connect(self.plotZoom) self.horizontalLayout.addWidget(self.btnZoom) self.btnPan = QtWidgets.QPushButton(self.tab) self.btnPan.setObjectName(_fromUtf8("btnPan")) self.btnPan.clicked.connect(self.plotPan) self.horizontalLayout.addWidget(self.btnPan) self.btnHome = QtWidgets.QPushButton(self.tab) self.btnHome.setObjectName(_fromUtf8("btnHome")) self.btnHome.clicked.connect(self.plotHome) self.horizontalLayout.addWidget(self.btnHome) self.verticalLayout_2.addLayout(self.horizontalLayout) # x axis options self.horizontalLayout_xAxis = QtWidgets.QHBoxLayout() self.horizontalLayout_xAxis.setObjectName(_fromUtf8("horizontalLayout_xAxis")) self.xColSelect = QtWidgets.QComboBox(self.tab) self.xColSelect.setObjectName(_fromUtf8("xColSelect")) self.xColSelect.currentIndexChanged.connect(self.ResetUserSelections) self.horizontalLayout_xAxis.addWidget(self.xColSelect) self.horizontalLayout_xAxis.setStretchFactor(self.xColSelect,2) self.xbinsLabel = QtWidgets.QLabel(self.tab) self.xbinsLabel.setText("X Bins:") self.horizontalLayout_xAxis.addWidget( self.xbinsLabel ) self.xnBinsSpin = QtWidgets.QSpinBox(self.tab) self.xnBinsSpin.setObjectName(_fromUtf8("xnBinsSpin")) self.xnBinsSpin.valueChanged.connect(self.XChangeBinning) self.xnBinsSpin.setRange(1,10000) self.horizontalLayout_xAxis.addWidget( self.xnBinsSpin ) self.horizontalLayout_xAxis.setStretchFactor(self.xnBinsSpin,2) self.xminLabel = QtWidgets.QLabel(self.tab) self.xminLabel.setText("X Min:") self.horizontalLayout_xAxis.addWidget( self.xminLabel ) self.xnMinSpin = QtWidgets.QDoubleSpinBox(self.tab) self.xnMinSpin.setObjectName(_fromUtf8("xnMinSpin")) self.xnMinSpin.valueChanged.connect(self.XChangeMin) self.xnMinSpin.setRange( -1e20, 1e20 ) self.horizontalLayout_xAxis.addWidget( self.xnMinSpin ) self.horizontalLayout_xAxis.setStretchFactor(self.xnMinSpin,2) self.xmaxLabel = QtWidgets.QLabel(self.tab) self.xmaxLabel.setText("X Max:") self.horizontalLayout_xAxis.addWidget( self.xmaxLabel ) self.xnMaxSpin = QtWidgets.QDoubleSpinBox(self.tab) self.xnMaxSpin.setObjectName(_fromUtf8("xnMaxSpin")) self.xnMaxSpin.setRange( -1e20, 1e20 ) self.xnMaxSpin.valueChanged.connect(self.XChangeMax) self.horizontalLayout_xAxis.addWidget( self.xnMaxSpin ) self.horizontalLayout_xAxis.setStretchFactor(self.xnMaxSpin,2) self.xTypeCombo = QtWidgets.QComboBox(self.tab) self.xTypeCombo.setObjectName(_fromUtf8("xTypeCombo")) self.xTypeCombo.addItems( ['int','float','str','datetime'] ) self.xTypeCombo.currentIndexChanged.connect(self.ChangeXDataType) self.horizontalLayout_xAxis.addWidget(self.xTypeCombo) self.horizontalLayout_xAxis.setStretchFactor(self.xTypeCombo,2) self.verticalLayout_2.addLayout(self.horizontalLayout_xAxis ) # y axis options self.horizontalLayout_yAxis = QtWidgets.QHBoxLayout() self.horizontalLayout_yAxis.setObjectName(_fromUtf8("horizontalLayout_yAxis")) self.yColSelect = QtWidgets.QComboBox(self.tab) self.yColSelect.setObjectName(_fromUtf8("yColSelect")) self.yColSelect.currentIndexChanged.connect(self.ResetUserSelections) self.horizontalLayout_yAxis.addWidget(self.yColSelect) self.horizontalLayout_yAxis.setStretchFactor(self.yColSelect,2) self.ybinsLabel = QtWidgets.QLabel(self.tab) self.ybinsLabel.setText("Y Bins:") self.horizontalLayout_yAxis.addWidget( self.ybinsLabel ) self.ynBinsSpin = QtWidgets.QSpinBox(self.tab) self.ynBinsSpin.setObjectName(_fromUtf8("ynBinsSpin")) self.ynBinsSpin.valueChanged.connect(self.YChangeBinning) self.horizontalLayout_yAxis.addWidget( self.ynBinsSpin ) self.horizontalLayout_yAxis.setStretchFactor(self.ynBinsSpin,2) self.yminLabel = QtWidgets.QLabel(self.tab) self.yminLabel.setText("Y Min:") self.horizontalLayout_yAxis.addWidget( self.yminLabel ) self.ynMinSpin = QtWidgets.QDoubleSpinBox(self.tab) self.ynMinSpin.setObjectName(_fromUtf8("ynMinSpin")) self.ynMinSpin.setRange( -1e20, 1e20 ) self.ynMinSpin.valueChanged.connect(self.YChangeMin) self.horizontalLayout_yAxis.addWidget( self.ynMinSpin ) self.horizontalLayout_yAxis.setStretchFactor(self.ynMinSpin,2) self.ymaxLabel = QtWidgets.QLabel(self.tab) self.ymaxLabel.setText("Y Max:") self.horizontalLayout_yAxis.addWidget( self.ymaxLabel ) self.ynMaxSpin = QtWidgets.QDoubleSpinBox(self.tab) self.ynMaxSpin.setObjectName(_fromUtf8("ynMaxSpin")) self.ynMaxSpin.setRange( -1e20, 1e20 ) self.ynMaxSpin.valueChanged.connect(self.YChangeMax) self.horizontalLayout_yAxis.addWidget( self.ynMaxSpin ) self.horizontalLayout_yAxis.setStretchFactor(self.ynMaxSpin,2) self.yTypeCombo = QtWidgets.QComboBox(self.tab) self.yTypeCombo.setObjectName(_fromUtf8("yTypeCombo")) self.yTypeCombo.addItems( ['int','float','str','datetime'] ) self.yTypeCombo.currentIndexChanged.connect(self.ChangeYDataType) self.horizontalLayout_yAxis.addWidget(self.yTypeCombo) self.horizontalLayout_yAxis.setStretchFactor(self.yTypeCombo,2) #self.verticalLayout_2.addLayout(self.horizontalLayout_yAxis ) self.yAxisItems = [ self.yColSelect, self.ybinsLabel, self.ynBinsSpin, self.yminLabel, self.ynMinSpin, self.ymaxLabel, self.ynMaxSpin, self.yTypeCombo ] self.binningChoices = [ self.ybinsLabel, self.ynBinsSpin, self.xbinsLabel, self.xnBinsSpin ] # Data tab self.tabWidget.addTab(self.tab, _fromUtf8("")) self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName(_fromUtf8("tab_2")) self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_2) self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) self.tableView = QtWidgets.QTableView(self.tab_2) self.tableView.setObjectName(_fromUtf8("tableView")) self.verticalLayout_3.addWidget(self.tableView) # Filter text entry self.filterHorLayout = QtWidgets.QHBoxLayout() self.filterHorLayout.setObjectName(_fromUtf8("filterHorLayout")) self.filterBox = QtWidgets.QLineEdit(self.centralwidget) self.filterBox.setObjectName(_fromUtf8("filterBox")) self.btnFilter = QtWidgets.QPushButton(self.centralwidget) self.btnFilter.setObjectName(_fromUtf8("btnFilter")) self.btnFilter.clicked.connect(self.updateTable) self.filterHorLayout.addWidget(self.filterBox) self.filterHorLayout.addWidget(self.btnFilter) self.verticalLayout.addLayout(self.filterHorLayout) # Setup self.treeView.raise_() self.tabWidget.addTab(self.tab_2, _fromUtf8("")) self.verticalLayout.addWidget(self.splitter) self.splitter.setStretchFactor(1, 2) self.centralwidget.showFullScreen() MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName(_fromUtf8("statusbar")) MainWindow.setStatusBar(self.statusbar) # Set the default picture import matplotlib.image as mpimg img=mpimg.imread(r'H:\stash_projects\DataView\PandaViz.jpg') plt.imshow(img) self.hdfStore = None self.plotTypeCombo.addItems( ['Scatter','Line','Hist','Hist2d'] ) self.userxBinning = False self.userxMax = False self.userxMin = False self.useryBinning = False self.useryMax = False self.useryMin = False self.fileName = "" self.df = None self.orig_df = None self.filterStr = None self.lastFilterStr = None self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) if self.currentFile is not None: if len(self.currentFile.split( r':' )) > 1: self.currentFile = self.currentFile.split( r':' )[1][-1] + r':' + self.currentFile.split( r':' )[2] self.treeView.setCurrentIndex( self.fileSystemModel.index( self.currentFile ) ) self.recursive_expand( index, self.treeView ) #self.on_treeView_doubleClicked( self.fileSystemModel.index( self.currentFile ) ) self.on_treeView_doubleClicked(self.fileSystemModel.index( self.currentFile ) ,self.currentFile ) def setupCsv(self): self.data() self.columns = self.df.columns.tolist() self.xColSelect.clear() self.xColSelect.addItems( self.columns ) self.yColSelect.clear() self.yColSelect.addItems( self.columns ) self.xTypeCombo.Text = str(self.df[ self.columns[0] ].dtype) self.updateTable() def setupH5(self): self.h5keys = self.hdfStore.keys() if len(self.h5keys) > 1: self.chosen_h5key = str( SelectDialog.SelectDialog.getOptions( self.h5keys ) ) else: self.chosen_h5key = "" if self.chosen_h5key == "": self.orig_df = self.hdfStore[ self.h5keys[0] ] else: self.orig_df = self.hdfStore[ self.chosen_h5key ] self.setupCsv() def plotLine(self): if self.data() is None: return plt.clf() xCol = str( self.xColSelect.currentText() ) yCol = str( self.yColSelect.currentText() ) ax = plt.gca() ax.plot( self.df[ xCol ].tolist(), self.df[ yCol ].tolist(), label=xCol + ' v ' + yCol ) if self.userxMin or self.userxMax or self.useryMin or self.useryMax: ax.set_xlim( self.xnMinSpin.value(), self.xnMaxSpin.value() ) ax.set_ylim( self.ynMinSpin.value(), self.ynMaxSpin.value() ) else: self.xnMinSpin.setValue( ax.get_xlim()[0] ) self.xnMaxSpin.setValue( ax.get_xlim()[1] ) self.ynMinSpin.setValue( ax.get_ylim()[0] ) self.ynMaxSpin.setValue( ax.get_ylim()[1] ) ax.set_xlabel( xCol ) ax.set_ylabel( yCol ) self.canvas.draw() def plotHist(self): if self.data() is None: return plt.clf() col = str( self.xColSelect.currentText() ) if self.userxBinning: bins = self.xnBinsSpin.value() else: bins = 10 if not self.userxBinning: self.xnBinsSpin.setValue( bins ) if self.userxMin: Min = self.xnMinSpin.value() else: Min = self.df[col].min() try: self.xnMinSpin.setValue( Min ) except: self.xnMinSpin.setValue( 0. ) if self.userxMax: Max = self.xnMaxSpin.value() else: Max = self.df[col].max() try: self.xnMaxSpin.setValue( Max ) except: self.xnMaxSpin.setValue( 0. ) plt.hist( sorted(self.df[ col ].tolist()), bins=bins, range=(Min,Max) ) plt.xlabel( col ) self.canvas.draw() def plotScatter(self): if self.data() is None: return plt.clf() xCol = str( self.xColSelect.currentText() ) yCol = str( self.yColSelect.currentText() ) ax = plt.gca() ax.scatter( self.df[ xCol ].tolist(), self.df[ yCol ].tolist(), label=xCol + ' v ' + yCol ) if self.userxMin or self.userxMax or self.useryMin or self.useryMax: ax.set_xlim( self.xnMinSpin.value(), self.xnMaxSpin.value() ) ax.set_ylim( self.ynMinSpin.value(), self.ynMaxSpin.value() ) else: self.xnMinSpin.setValue( ax.get_xlim()[0] ) self.xnMaxSpin.setValue( ax.get_xlim()[1] ) self.ynMinSpin.setValue( ax.get_ylim()[0] ) self.ynMaxSpin.setValue( ax.get_ylim()[1] ) ax.set_xlabel( xCol ) ax.set_ylabel( yCol ) self.canvas.draw() def plotHist2d(self): if self.data() is None: return plt.clf() xCol = str( self.xColSelect.currentText() ) yCol = str( self.yColSelect.currentText() ) ax = plt.gca() if self.userxBinning: bins = self.xnBinsSpin.value() else: bins = 10 ax.hist2d( self.df[xCol].tolist(), self.df[yCol].tolist(), bins=bins ) if not self.userxBinning: self.xnBinsSpin.setValue( bins ) if not self.userxMin: self.xnMinSpin.setValue( ax.get_xlim()[0] ) else: ax.set_xlim([self.xnMinSpin.value(), ax.get_xlim()[1] ] ) if not self.userxMax: self.xnMaxSpin.setValue( ax.get_xlim()[1] ) else: ax.set_xlim([ax.get_xlim()[0], self.xnMaxSpin.value() ] ) if not self.useryMin: self.ynMinSpin.setValue( ax.get_ylim()[0] ) else: ax.set_ylim([self.ynMinSpin.value(), ax.get_ylim()[1] ] ) if not self.useryMax: self.ynMaxSpin.setValue( ax.get_ylim()[1] ) else: ax.set_ylim([ax.get_ylim()[0], self.ynMaxSpin.value() ] ) plt.xlabel( xCol ) plt.ylabel( yCol ) self.canvas.draw() def data(self): if self.orig_df is None: return None self.filterStr = str(self.filterBox.text()) if self.filterStr is not None and self.filterStr != "": if self.filterStr != self.lastFilterStr: try: exec("self.df = self.orig_df[" + self.filterStr.replace("df","self.orig_df") + "]" ) self.lastFilterStr = self.filterStr except Exception as e: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("Filtering failed") msg.setWindowTitle("Error") msg.setDetailedText( str(e) ) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() pass pass pass else: self.df = self.orig_df return True def updateTable(self): if self.data() is None: return header = self.columns tm = MyTableModel.MyTableModel(self.df.values, header, self) self.tableView.setModel(tm) vh = self.tableView.verticalHeader() vh.setVisible(False) hh = self.tableView.horizontalHeader() hh.setStretchLastSection(True) self.tableView.setSortingEnabled(True) #self.tableView.sortByColumn(0); tm.sort( 0, Qt.AscendingOrder ) def plotZoom(self): self.toolbar.zoom() def plotPan(self): self.toolbar.pan() def plotHome(self): self.toolbar.home() def on_treeView_doubleClicked(self, index, fileName = '' ): if fileName == '': fileName = str(index.model().filePath(index)) if self.fileName == fileName: return if self.hdfStore is not None: self.hdfStore.close() if fileName.endswith( ".csv" ): self.hdfStore = None self.fileName = fileName self.isCsv = True self.isH5 = False self.orig_df = pd.read_csv( fileName ) self.setupCsv() elif fileName.endswith(".h5"): self.fileName = fileName self.isCsv = False self.isH5 = True self.hdfStore = pd.HDFStore( fileName, 'r' ) self.setupH5() def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "PandaViz", None)) import os.path as osp path = osp.join(osp.dirname(sys.modules[__name__].__file__), 'PandaViz.jpg') MainWindow.setWindowIcon(QIcon(path)) self.btnZoom.setText(_translate("MainWindow", "Zoom", None)) self.btnPlot.setText(_translate("MainWindow", "Draw", None)) self.btnPan.setText(_translate("MainWindow", "Pan", None)) self.btnHome.setText(_translate("MainWindow", "Reset", None)) self.btnFilter.setText(_translate("MainWindow","Update Table", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Plot", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Data", None)) def recursive_expand(self, index, treeView): treeView.expand( index ) parent = index.parent() if parent != index: self.recursive_expand( parent, treeView ) def drawPlot(self): pType = self.plotTypeCombo.currentText() if pType == 'Scatter': self.plotScatter() elif pType == 'Hist': self.plotHist() elif pType == 'Hist2d': self.plotHist2d() elif pType == 'Line': self.plotLine() def ChangeXDataType(self): if self.data() is None: return col = str( self.xColSelect.currentText() ) changeType = str( self.xTypeCombo.currentText() ) try: if changeType == 'datetime': self.df[ col ] = pd.to_datetime( self.df[ col ]) else: exec("self.df[ col ] = self.df[ col ].astype("+ changeType + ")") except Exception as e: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("Type Conversion Failed") msg.setWindowTitle("Error") msg.setDetailedText( str(e) ) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() pass pass def ChangeYDataType(self): if self.data() is None: return col = str( self.yColSelect.currentText() ) changeType = str( self.yTypeCombo.currentText() ) try: if changeType == 'datetime': self.df[ col ] = pd.to_datetime( self.df[ col ]) else: exec("self.df[ col ] = self.df[ col ].astype("+ changeType + ")") except Exception as e: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("Type Conversion Failed") msg.setWindowTitle("Error") msg.setDetailedText( str(e) ) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() pass pass def ChangePlotType(self): self.userBinning = False pType = self.plotTypeCombo.currentText() if pType == 'Scatter' or pType == 'Line' or pType == 'Hist2d': [ x.show() for x in self.yAxisItems ] found = False for i in range( self.verticalLayout_2.count() ): if self.verticalLayout_2.itemAt(i) == self.horizontalLayout_yAxis: found=True if not found: self.verticalLayout_2.addLayout( self.horizontalLayout_yAxis ) if pType == 'Scatter' or pType == 'Line': [ x.hide() for x in self.binningChoices ] else: [ x.show() for x in self.binningChoices ] else: [ x.show() for x in self.binningChoices ] [ x.hide() for x in self.yAxisItems ] for i in range( self.verticalLayout_2.count() ): if self.verticalLayout_2.itemAt(i) == self.horizontalLayout_yAxis: self.verticalLayout_2.removeItem( self.horizontalLayout_yAxis ) self.verticalLayout_2.update() def XChangeBinning(self): self.userxBinning = True def YChangeBinning(self): self.useryBinning = True def XChangeMax(self): self.userxMax = True def YChangeMax(self): self.useryMax = True def XChangeMin(self): self.userxMin = True def YChangeMin(self): self.useryMin = True def ResetUserSelections(self): self.userxMin = False self.useryMin = False self.userxMax = False self.useryMax = False self.userxBinning = False self.useryBinning = False
class SliceWidget(FigureCanvas): def __init__(self, parent=None, dpi=100): fig = Figure(dpi=dpi) self.axes = fig.add_subplot(111) FigureCanvas.__init__(self, fig) self.setParent(parent) fig.tight_layout() self.toolbar = NavigationToolbar(self, self) self.toolbar.hide() self.mpl_connect('motion_notify_event', self.mouseMoved) FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self.locationLabel = None self.image = None self.sliceNumber = 0 self.diaphragmAxial = None self.umbilicisInferior = None self.umbilicisSuperior = None self.umbilicisLeft = None self.umbilicisRight = None self.umbilicisCoronal = None self.CATLine = None def updateFigure(self): self.axes.cla() if self.image is not None: self.axes.imshow(self.image[:, :, self.sliceNumber], cmap="gray") if self.sliceNumber == self.diaphragmAxial: self.axes.add_patch(patches.Rectangle((0, 0), 20, 20, color='purple')) if self.umbilicisInferior is not None: if self.umbilicisInferior <= self.sliceNumber <= self.umbilicisSuperior: x = self.umbilicisLeft y = self.umbilicisCoronal width = 1 height = self.umbilicisRight - x self.axes.add_patch(patches.Rectangle((y, x), width, height, color='orange')) if self.CATLine is not None and len(self.CATLine) > 1: startIndex = next((i for i, x in enumerate(self.CATLine) if min(x) != -1), None) CATLine = self.CATLine[startIndex:] if len(CATLine) > 1 and CATLine[0][0] <= self.sliceNumber <= CATLine[-1][0]: posterior = int(np.round(np.interp(self.sliceNumber, np.array([i[0] for i in CATLine]), np.array([i[1] for i in CATLine])))) anterior = int(np.round(np.interp(self.sliceNumber, np.array([i[0] for i in CATLine]), np.array([i[2] for i in CATLine])))) x = self.image.shape[0] // 3 y = posterior width = 1 height = 75 self.axes.add_patch(patches.Rectangle((y, x), width, height, color='red')) y = anterior self.axes.add_patch(patches.Rectangle((y, x), width, height, color='red')) self.draw() def mouseMoved(self, event): if self.locationLabel is not None and event.inaxes: x, y, z = event.xdata, event.ydata, self.sliceNumber else: x, y, z = 0, 0, self.sliceNumber self.locationLabel.setText("(%i, %i, %i)" % (x, y, z))
class MinWindow(QMainWindow): def __init__(self, parent=None): super(MinWindow, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # self.ui.setupUi(self) self.center() self.on_draw() self.load_event() def on_draw(self): self.draw_all_stack_tab() self.set_all_stack_tab_toolbar() pass def load_event(self): self.event_with_buttons() pass def draw_all_stack_tab(self): # 绘制所有图像 # 添加到stack_tabWidget上 # 创建matplot画布 self.tab1 = PlotCanvas(self, width=9, height=6, dpi=100) self.tab2 = PlotCanvas(self, width=9, height=6, dpi=100) self.tab3 = PlotCanvas(self, width=9, height=6, dpi=100) # mpl.draw_one_line() # 加载数据 # 在不同的画布上画出来 # 将所有f分页加入到tabWidget上 lines = load_all_lines() self.tab1.draw_one_line(lines[0]) self.tab2.draw_one_line(lines[1]) self.tab3.draw_one_line(lines[2]) # 将所有的tab添加到stack_tabWidget上 self.ui.stack_tabWidget.addTab(self.tab1, 'td=0.12') self.ui.stack_tabWidget.addTab(self.tab2, 'td=0.144') self.ui.stack_tabWidget.addTab(self.tab3, 'td=0.176') pass def set_all_stack_tab_toolbar(self): # 添加图像的工具栏 所有的工具栏用容器toolbar_stackedWidget保存 self.tab1_ntb = NavigationToolbar(self.tab1, self.ui.page) # 添加完整的 toolbar self.tab2_ntb = NavigationToolbar(self.tab2, self.ui.page) # 添加完整的 toolbar self.tab3_ntb = NavigationToolbar(self.tab3, self.ui.page) # 添加完整的 toolbar self.tab1.mpl_nav_toolbar = self.tab1_ntb self.tab2.mpl_nav_toolbar = self.tab2_ntb self.tab3.mpl_nav_toolbar = self.tab3_ntb # log('设置了的') # 隐藏当前的toolbar self.tab1_ntb.hide() self.tab2_ntb.hide() self.tab3_ntb.hide() def set_button_tip(self, button, tip=None): # 设置左下角显示button 的提示 if tip is not None: button.setToolTip(tip) button.setStatusTip(tip) pass def event_with_buttons(self): # button绑定事件 # self.ui.reset_Button.clocked() # 点击按钮 保存当前图像 self.ui.save_pushButton.clicked.connect(self.toolbar_save_plot) self.ui.reset_Button.clicked.connect(self.toolbar_home) self.ui.forward_Button.clicked.connect(self.toolbar_forward) self.ui.back_Button.clicked.connect(self.toolbar_back) self.ui.zoom_pushButton.clicked.connect(self.toolbar_zoom) self.ui.pan_pushButton.clicked.connect(self.toolbar_pan) self.set_button_tip(self.ui.save_pushButton, '点击保存 快捷键Ctrl + S') self.set_button_tip(self.ui.reset_Button, '显示图像最初的样子') self.set_button_tip(self.ui.forward_Button, '返回上一步图像') self.set_button_tip(self.ui.back_Button, '返回下一步图像') self.set_button_tip(self.ui.zoom_pushButton, '选中放大图像') self.set_button_tip(self.ui.pan_pushButton, '选中图像 可拖动查看细节') # 给菜单按钮添加绑定事件 # QtCore.QMetaObject.connectSlotsByName(self) save_file_action = self.create_action("&保存图像", shortcut="Ctrl+S", slot=self.toolbar_save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.ui.file_menu, (save_file_action, None, quit_action)) pass def toolbar_set_curt(self): # self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar pass def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): # 创建事件 action = QAction(text, self) if icon is not None: # action.setIcon(QIcon(":/%s.png" % button_icon)) pass if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) # bottons = QPushButton(text, self) # bottons.setti action.setStatusTip(tip) if slot is not None: action.triggered.connect(slot) if checkable: action.setCheckable(True) return action def add_actions(self, target, actions): # 添加事件 for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def center(self): # 设置主窗口在屏幕中间 screen = QDesktopWidget().screenGeometry() size = self.geometry() # from utils import log # log('screen.width ({}) size.width({})'.format(screen.width(), size.width())) self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def toolbar_save_plot(self): # 保存当前图像 log('点击了保存') # 给button设置tips self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.save_figure() # log('点击了保存啊') pass def toolbar_zoom(self): # 放大当前图像 log('点击了放大') self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.zoom() def toolbar_back(self): # 显示前一步操作的图像 log("点击了前进") self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.back() def toolbar_forward(self): # 显示前一步操作的图像 log("点击了上一步") self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.forward() def toolbar_edit_parameters(self): # 编辑图像的参数 self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.edit_parameters() def toolbar_pan(self): # self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.pan() def toolbar_edit_parameters(self): self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.edit_parameters() def toolbar_home(self): log('点击了重置') self.ui.stack_tabWidget.currentWidget().mpl_nav_toolbar.home()
class PIVPlot(QtWidgets.QWidget): def __init__(self, parent=QtWidgets.QWidget, main_class=None): super(PIVPlot, self).__init__(parent) self.figure = Figure() self.figure.patch.set_facecolor((0.94117647, 0.94117647, 0.94117647)) # the canvas is where the graph and the tool bar is self.piv_canvas = FigureCanvas(self.figure) self.piv_tool_bar = NavigationToolbar(self.piv_canvas, self) self.piv_tool_bar.hide() self.main_class = main_class # the piv_images_list is where the images are saved self.piv_images_list = [] # the list where the results of the piv are saved self.piv_results_list = [] self.ax = self.figure.add_subplot(111) # self.zoom_ax = self.figure.add_subplot(221) # self.zoom_ax.axis('off') self.xy_zoom = [[None, None], [None, None]] self.zoom_rectangle = None self.cid = None self.bit = '8 bit' self.current_image = 1 self.canvas_layout = QtWidgets.QGridLayout(parent) self.canvas_layout.addWidget(self.piv_canvas) # function that shows the chosen image def show_plot(self, image_number, bit, change_number=False): self.ax.clear() self.bit = bit self.current_image = image_number """ if self.bit == "8 bit": if self.piv_images_list[image_number][3]: self.ax.quiver(self.piv_images_list[image_number][3][0], max(self.piv_images_list[image_number][3][1].flatten()) - self.piv_images_list[image_number][3][1], self.piv_images_list[image_number][3][2], self.piv_images_list[image_number][3][3], color='b', pivot='middle') self.ax.quiver(self.piv_images_list[image_number][3][0][self.piv_images_list[image_number][3][4]], max(self.piv_images_list[image_number][3][1][ self.piv_images_list[image_number][3][4]].flatten()) - self.piv_images_list[image_number][3][1][self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][2][self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][3][self.piv_images_list[image_number][3][4]], color='r', pivot='middle') self.ax.imshow(np.uint8(self.piv_images_list[image_number][2]), cmap=plt.cm.gray) self.ax.axis('off') self.zoom_ax.imshow(np.uint8(self.piv_images_list[image_number][2]), cmap=plt.cm.gray) self.zoom_ax.axis('off') if self.xy_zoom[0][0] != None: self.zoom_ax.set_xlim(self.xy_zoom[0][0], self.xy_zoom[0][1]) self.zoom_ax.set_ylim(self.xy_zoom[1][0], self.xy_zoom[1][1]) """ if self.piv_images_list[image_number][3]: self.ax.quiver(self.piv_images_list[image_number][3][0], self.piv_images_list[image_number][3][1], self.piv_images_list[image_number][3][2], self.piv_images_list[image_number][3][3], color='b', pivot='middle') try: self.ax.quiver( self.piv_images_list[image_number][3][0][ self.piv_images_list[image_number][3][4]], max(self.piv_images_list[image_number][3][1][ self.piv_images_list[image_number][3][4]].flatten()) - self.piv_images_list[image_number][3][1][ self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][2][ self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][3][ self.piv_images_list[image_number][3][4]], color='r', pivot='middle') except ValueError: self.ax.quiver(self.piv_images_list[image_number][3][0][ self.piv_images_list[image_number][3][4]], 0 - self.piv_images_list[image_number][3][1][ self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][2][ self.piv_images_list[image_number][3][4]], self.piv_images_list[image_number][3][3][ self.piv_images_list[image_number][3][4]], color='r', pivot='middle') if self.bit == "8 bit": self.ax.imshow(np.uint8(self.piv_images_list[image_number][2]), cmap=plt.cm.gray, origin="upper") else: self.ax.imshow(np.uint16(self.piv_images_list[image_number][2]), cmap=plt.cm.gray, origin="upper") self.ax.axis('off') """ self.zoom_ax.imshow(np.uint16(self.piv_images_list[image_number][2]), cmap=plt.cm.gray) self.zoom_ax.axis('off') if self.xy_zoom[0][0] != None: self.zoom_ax.set_xlim(self.xy_zoom[0][0], self.xy_zoom[0][1]) self.zoom_ax.set_ylim(self.xy_zoom[1][0], self.xy_zoom[1][1]) """ if self.xy_zoom[0][0] == 0 and self.xy_zoom[0][1] == len( self.piv_images_list[0][2] [0]) and self.xy_zoom[1][0] == 0 and self.xy_zoom[1][1] == len( self.piv_images_list[0][2]): self.zoom_rectangle = Rectangle( (self.xy_zoom[0][0], self.xy_zoom[1][0]), abs(self.xy_zoom[0][1] - self.xy_zoom[0][0]), abs(self.xy_zoom[1][1] - self.xy_zoom[1][0]), facecolor='none', alpha=0.1, edgecolor='none', linewidth=1, fill=False) elif not self.xy_zoom[0][0] == None: self.zoom_rectangle = Rectangle( (self.xy_zoom[0][0], self.xy_zoom[1][0]), abs(self.xy_zoom[0][1] - self.xy_zoom[0][0]), abs(self.xy_zoom[1][1] - self.xy_zoom[1][0]), facecolor='none', alpha=0.6, linestyle='-', edgecolor='white', linewidth=2, fill=True) self.ax.add_patch(self.zoom_rectangle) if change_number: self.main_class.current_image_number.setText(str(image_number + 1)) self.piv_canvas.draw() # function to add an image def add_image(self, image_path, bit): self.bit = bit if self.bit == "8 bit": self.piv_images_list.append([ image_path, QtCore.QFileInfo(image_path).fileName(), np.uint8(tools.imread(image_path)), None, None ]) else: self.piv_images_list.append([ image_path, QtCore.QFileInfo(image_path).fileName(), np.uint16(tools.imread(image_path)), None, None ]) @staticmethod def invert(img_read, is_bmp, bit): if is_bmp: # for i in range(len(img_read)): # for j in range(len(img_read[i])): img_read = 255 - img_read else: # for i in range(len(img_read)): # for j in range(len(img_read[i])): img_read = 1.0 - img_read # it's not 0.255 if bit == "8 bit": return np.uint8(img_read) else: return np.uint16(img_read) def ROI_buttons(self, is_select): if is_select: self.rs = RectangleSelector(self.ax, self.zoom, drawtype='box', useblit=False, button=[1], spancoords='pixels', interactive=False, rectprops=dict(alpha=0.6, linestyle='-', edgecolor='white', linewidth=2)) else: self.xy_zoom[0][0] = 0 # top left / x1 self.xy_zoom[0][1] = len( self.piv_images_list[0][2][0]) # top right / x2 self.xy_zoom[1][0] = 0 # bottom left/ y1 self.xy_zoom[1][1] = len( self.piv_images_list[0][2]) # bottom right/ y2 self.reset_piv() self.show_plot(self.current_image, self.bit) def zoom(self, click_point, release_point): x1, y1 = click_point.xdata, click_point.ydata x2, y2 = release_point.xdata, release_point.ydata self.xy_zoom[0][0] = min([x1, x2]) self.xy_zoom[0][1] = max([x1, x2]) self.xy_zoom[1][0] = min([y1, y2]) self.xy_zoom[1][1] = max([y1, y2]) self.rs = None self.show_plot(self.current_image, self.bit) def reset_piv(self): for i in range(len(self.piv_images_list)): self.piv_images_list[i][3] = None