def main(): app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QMainWindow() area = DockArea() win.setCentralWidget(area) win.show() dock = Dock(name='Test Dock', area=area) area.addDock(dock) QtCore.QThread.sleep(2) dock.close() QtWidgets.QApplication.processEvents() sys.exit(app.exec_())
class MainWindow(QtGui.QMainWindow): # for dock def __init__(self, parent=None): from pyqtgraph import exporters exporters.Exporter.register = self.modified_register QtGui.QMainWindow.__init__(self, parent) self.setWindowIcon(QtGui.QIcon(logo)) self.setWindowTitle('CIVET') self.setGeometry(200, 143, 1574, 740) pg.setConfigOption('background', QtGui.QColor(215, 214, 213, 255)) pg.setConfigOption('foreground', 'k') palette = QtGui.QPalette() palette.setColor(QtGui.QPalette.Background, QtGui.QColor(215, 214, 213, 255)) self.setPalette(palette) self.mainframe = QtGui.QFrame() self.copen = 0 self.sopen = 0 self.ropen = 0 self.gridstate = 0 self.current_docks = [] self.loadaction = QtGui.QAction("&Open", self) self.loadaction.setShortcut("Ctrl+O") self.loadaction.setStatusTip("Open File") self.loadaction.triggered.connect(self.loaddata) self.exportaction = QtGui.QAction("&Export", self) self.exportaction.setShortcut("Ctrl+E") self.exportaction.setStatusTip("Export to a folder") self.exportaction.triggered.connect(self.exportdata) self.viewaction1 = QtGui.QAction("&Grid View 1", self) self.viewaction1.setStatusTip("Grid View 1") self.viewaction1.triggered.connect(self.grid_view_1) self.viewaction2 = QtGui.QAction("&Grid View 2", self) self.viewaction2.setStatusTip("Grid View 2") self.viewaction2.triggered.connect(self.grid_view_2) self.about = QtGui.QAction("&Info", self) self.about.setStatusTip("Info") self.about.triggered.connect(self.about_info) self.mainMenu = self.menuBar() self.fileMenu = self.mainMenu.addMenu("&File") self.fileMenu.addAction(self.loadaction) self.fileMenu.addAction(self.exportaction) self.viewMenu = self.mainMenu.addMenu("&Display") self.viewMenu.addAction(self.viewaction1) self.viewMenu.addAction(self.viewaction2) self.aboutMenu = self.mainMenu.addMenu("&About") self.aboutMenu.addAction(self.about) self.maketoolbar() self.area = DockArea() self.drho = Dock("\u03c1") self.dlamb = Dock("\u03bb") self.dalpha = Dock("\u03b1") self.dbeta = Dock("\u03b2") self.dphase = Dock("Phase") self.dcontrols = Dock("Controls") self.layout = QtGui.QHBoxLayout() self.layout.addWidget(self.area) self.mainframe.setLayout(self.layout) self.mainframe.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Raised) self.mainframe.setLineWidth(8) # all those have different relative positions so made bunch of functions, not one. def modified_register(self, cls): exporters.Exporter.Exporters.append(None) def dlambadd(self): try: self.area.addDock(self.dlamb, 'bottom', self.drho) except: try: self.area.addDock(self.dlamb, 'left', self.dphase) except: self.area.addDock(self.dlamb, 'left') def drhoadd(self): try: self.area.addDock(self.drho, 'top', self.dlamb) except: try: self.area.addDock(self.drho, 'left', self.dalpha) except: self.area.addDock(self.drho, 'left') def dalphaadd(self): try: self.area.addDock(self.dalpha, 'left', self.dbeta) except: try: self.area.addDock(self.dalpha, 'right', self.drho) except: try: self.area.addDock(self.dalpha, 'top', self.dphase) except: self.area.addDock(self.dalpha, 'left') def dbetaadd(self): try: self.area.addDock(self.dbeta, 'top', self.dcontrols) except: try: self.area.addDock(self.dbeta, 'right', self.dalpha) except: self.area.addDock(self.dbeta, 'right') def dphaseadd(self): try: self.area.addDock(self.dphase, 'left', self.dcontrols) except: try: self.area.addDock(self.dphase, 'bottom', self.dalpha) except: try: self.area.addDock(self.dphase, 'right', self.dlamb) except: self.area.addDock(self.drho, 'bottom') def dcontrolsadd(self): try: self.area.addDock(self.dcontrols, 'right', self.dphase) except: try: self.area.addDock(self.dcontrols, 'bottom', self.dbeta) except: self.area.addDock(self.dcontrols, 'right') def checkbox_init(self): self.dock_on = [] self.cwindow = QtGui.QMainWindow() self.cwindow.setWindowIcon(QtGui.QIcon(logo)) self.cwindow.setWindowTitle('Plots') self.boxeswidget = QtGui.QWidget(self.cwindow) checkbox_layout = QtGui.QGridLayout(self.boxeswidget) self.ch_lamb = QtGui.QCheckBox(self.boxeswidget) self.ch_lamb.setText('\u03bb') self.ch_lamb.setChecked(True) self.ch_lamb.stateChanged.connect(lambda: self.dlamb.close( ) if not self.ch_lamb.isChecked() else self.dlambadd()) self.ch_rho = QtGui.QCheckBox(self.boxeswidget) self.ch_rho.setText('\u03c1') self.ch_rho.setChecked(True) self.ch_rho.stateChanged.connect(lambda: self.drho.close( ) if not self.ch_rho.isChecked() else self.drhoadd()) self.ch_alpha = QtGui.QCheckBox(self.boxeswidget) self.ch_alpha.setText('\u03b1') self.ch_alpha.setChecked(True) self.ch_alpha.stateChanged.connect(lambda: self.dalpha.close( ) if not self.ch_alpha.isChecked() else self.dalphaadd()) self.ch_beta = QtGui.QCheckBox(self.boxeswidget) self.ch_beta.setText('\u03b2') self.ch_beta.setChecked(True) self.ch_beta.stateChanged.connect(lambda: self.dbeta.close( ) if not self.ch_beta.isChecked() else self.dbetaadd()) self.ch_phase = QtGui.QCheckBox(self.boxeswidget) self.ch_phase.setText('Phase') self.ch_phase.setChecked(True) self.ch_phase.stateChanged.connect(lambda: self.dphase.close( ) if not self.ch_phase.isChecked() else self.dphaseadd()) self.ch_controls = QtGui.QCheckBox(self.boxeswidget) self.ch_controls.setText('Controls') self.ch_controls.setChecked(True) self.ch_controls.stateChanged.connect(lambda: self.dcontrols.close( ) if not self.ch_controls.isChecked() else self.dcontrolsadd()) checkbox_layout.addWidget(self.ch_rho, 0, 0, 1, 1) checkbox_layout.addWidget(self.ch_lamb, 1, 0, 1, 1) checkbox_layout.addWidget(self.ch_alpha, 0, 1, 1, 1) checkbox_layout.addWidget(self.ch_phase, 1, 1, 1, 1) checkbox_layout.addWidget(self.ch_beta, 0, 2, 1, 1) checkbox_layout.addWidget(self.ch_controls, 1, 2, 1, 1) self.cwindow.setCentralWidget(self.boxeswidget) def checkbox(self): if self.copen == 0: self.check_current_docks() self.closed_docks = [] for i in self.docklist: if i not in self.current_docks: self.closed_docks += [i] for i in self.closed_docks: eval(i[:5] + "ch_" + i[6:]).setChecked(False) self.cwindow.show() self.copen = 1 else: self.cwindow.hide() self.copen = 0 def about_info(self): self.awindow = QtGui.QMainWindow() self.awindow.setWindowIcon(QtGui.QIcon(logo)) self.awindow.setWindowTitle('About') labels = QtGui.QWidget(self.awindow) labels_layout = QtGui.QVBoxLayout(labels) self.line0 = QtGui.QLabel() self.line1 = QtGui.QLabel( "CIVET: Current Inspired Visualization and Evaluation of the") self.line2 = QtGui.QLabel() self.line3 = QtGui.QLabel( "Totally asymmetric simple exclusion process (TASEP)") self.line4 = QtGui.QLabel() self.line5 = QtGui.QLabel("Copyright (c) 2018,") self.line6 = QtGui.QLabel() self.line7 = QtGui.QLabel( "Wonjun Son, Dan D. Erdmann-Pham, Khanh Dao Duc, Yun S. Song") self.line8 = QtGui.QLabel() self.line9 = QtGui.QLabel("All rights reserved.") self.line10 = QtGui.QLabel() for i in range(11): eval("self.line" + str(i)).setAlignment(QtCore.Qt.AlignCenter) labels_layout.addWidget(eval("self.line" + str(i))) self.awindow.setCentralWidget(labels) self.awindow.show() def exportdata(self): self.dialog = QtGui.QFileDialog() self.dialog.setOption(QtGui.QFileDialog.ShowDirsOnly) self.dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly) folder_name = self.dialog.getSaveFileName(self, "Save Directory") filepath = os.path.join(os.path.abspath(os.sep), folder_name) if not os.path.exists(filepath): makedirs(filepath) # arr = QtCore.QByteArray() # buf = QtCore.QBuffer(arr) # svg = QtSvg.QSvgGenerator() # svg.setOutputDevice(buf) # dpi = QtGui.QDesktopWidget().physicalDpiX() # svg.setResolution(dpi) svg = QtSvg.QSvgGenerator() svg.setFileName("hh") svg.setSize(QtCore.QSize(2000, 2000)) svg.setViewBox(self.rect()) svg.setTitle("SVG svg Example Drawing") svg.setDescription("An SVG drawing created by the SVG Generator ") p = QtGui.QPainter() p.begin(svg) try: self.render(p) finally: p.end() # gen.setViewBox(QtGui.QRect(0, 0, 200, 200)); # gen.setTitle(tr("SVG Gen Example Drawing")); # gen.setDescription(tr("An SVG drawing created by the SVG Generator ""Example provided with Qt.")); lambda_data = os.path.join(filepath, "Lambda_data.csv") with open(lambda_data, "w") as output: try: writer = csv.writer(output, lineterminator='\n') for val in np.array(glo_var.lambdas)[:, 1]: writer.writerow([val]) except: pass summary = os.path.join(filepath, "Summary.csv") with open(summary, "w") as output: try: writer = csv.writer(output, delimiter='\t', lineterminator='\n') writer.writerow(["alpha", glo_var.alpha]) writer.writerow(["beta", glo_var.beta]) writer.writerow(["l", glo_var.l]) writer.writerow(["Phase", self.phas.pointer.region_aft]) except: pass # for name, pitem in self.pltlist: # self.exportimg(pitem,os.path.join(filepath,name + ".svg")) # # self.exportimg(pitem,os.path.join(filepath,name + ".png")) currentanddensity = os.path.join(filepath, "Current & Density.csv") with open(currentanddensity, "w") as output: writer = csv.writer(output, delimiter='\t', lineterminator='\n') # combine alpha, beta pre and post. Do it here to lessen the computation alpha_x_data = np.concatenate([ np.array(self.jalph.alphas_pre), np.linspace(self.jalph.trans_point, 1, 2) ]) alpha_j_data = np.concatenate( [self.jalph.j_l_values, np.array([self.jalph.jpost] * 2)]) alpha_rho_data = np.array(self.jalph.rho_avg_pre + self.jalph.rho_avg_post) beta_x_data = np.concatenate([ np.array(self.jbet.betas_pre), np.linspace(self.jbet.trans_point, 1, 2) ]) beta_j_data = np.concatenate( [self.jbet.j_r_values, np.array([self.jbet.jpost] * 2)]) beta_rho_data = np.array(self.jbet.rho_avg_pre + self.jbet.rho_avg_post) alpha_data = np.vstack( [alpha_x_data, alpha_j_data, alpha_rho_data]) beta_data = np.vstack([beta_x_data, beta_j_data, beta_rho_data]) writer.writerow([ "alpha", "Current", "Average Density", '\t', '\t', "beta", "Current", "Average Density" ]) for i in range(len(alpha_x_data)): writer.writerow( list(alpha_data[:, i]) + [''] * 2 + list(beta_data[:, i])) writer.writerow('\n') writer.writerow( ['beta', glo_var.beta, '', '', '', 'alpha', glo_var.alpha, '']) writer.writerow([ 'transition point', self.jalph.trans_point, '', '', '', 'transition point', self.jbet.trans_point, '' ]) self.pdfgroup1 = ['Lambda_fig', 'Density_fig'] self.pdfgroup2 = ['Current_alpha_fig', 'Current_beta_fig'] for name, pitem in self.pltlist: # self.exportimg(pitem,os.path.join(filepath,name + ".png")) path = os.path.join(filepath, name + ".svg") # if name == 'Current_alpha_fig': # self.jalph.p3.setLabel('right',"\u2329\u03c1 \u232a",**glo_var.labelstyle) # self.jalph.p3main.plotItem.legend.items=[] # self.jalph.p3.plot(pen=self.jalph.jpen, name='J') # self.jalph.p3.plot(pen=self.jalph.rho_dash, name='\u2329\u03c1 \u232a') # elif name == 'Current_beta_fig': # self.jbet.p4.setLabel('right',"\u2329\u03c1 \u232a",**glo_var.labelstyle) # self.jbet.p4main.plotItem.legend.items=[] # self.jbet.p4.plot(pen=self.jbet.jpen, name='J') # self.jbet.p4.plot(pen=self.jbet.rho_dash, name='\u2329\u03c1 \u232a') self.exportimg(pitem, path) if name in self.pdfgroup1: self.makepdf(path, os.path.join(filepath, name + ".pdf"), 500, 260, 20, 240) elif name == 'Current_alpha_fig': self.makepdf(path, os.path.join(filepath, name + ".pdf"), 350, 280, 30, 230) # self.jalph.p3.setLabel('right',"\u2329 \u03c1 \u232a",**glo_var.labelstyle) # self.jalph.p3main.plotItem.legend.items=[] # self.jalph.p3.plot(pen=self.jalph.jpen, name='J') # self.jalph.p3.plot(pen=self.jalph.rho_dash, name='\u2329 \u03c1 \u232a') elif name == 'Current_beta_fig': self.makepdf(path, os.path.join(filepath, name + ".pdf"), 350, 280, 30, 230) # self.jbet.p4.setLabel('right',"\u2329 \u03c1 \u232a",**glo_var.labelstyle) # self.jbet.p4main.plotItem.legend.items=[] # self.jbet.p4.plot(pen=self.jbet.jpen, name='J') # self.jbet.p4.plot(pen=self.jbet.rho_dash, name='\u2329 \u03c1 \u232a') else: self.makepdf(path, os.path.join(filepath, name + ".pdf"), 410, 330, 10, 300) os.remove(path) def makepdf(self, path, name, x, y, z, q): from svglib.svglib import svg2rlg from reportlab.pdfgen import canvas from reportlab.graphics import renderPDF rlg = svg2rlg(path) rlg.scale(0.8, 0.8) c = canvas.Canvas(name) c.setPageSize((x, y)) renderPDF.draw(rlg, c, z, q) c.showPage() c.save() def exportimg(self, img, path): # self.img = pg.exporters.ImageExporter(img) # self.img.export(path) self.svg = pg.exporters.SVGExporter(img) self.svg.export(fileName=path) def maketoolbar(self): self.state = None self.titlebarstate = 1 self.toolbar = self.addToolBar("Toolbar") home = QtGui.QAction( QtGui.QIcon(QtGui.QApplication.style().standardIcon( QtGui.QStyle.SP_ComputerIcon)), 'Toggle Grid View', self) home.triggered.connect(self.toggle_grid_view) self.toolbar.addAction(home) fix = QtGui.QAction( QtGui.QIcon(QtGui.QApplication.style().standardIcon( QtGui.QStyle.SP_ArrowDown)), 'Fix current layout', self) fix.triggered.connect(self.fixdock) self.toolbar.addAction(fix) self.savedockandvaluesinit() save = QtGui.QAction( QtGui.QIcon(QtGui.QApplication.style().standardIcon( QtGui.QStyle.SP_FileDialogListView)), 'Save current layout', self) save.triggered.connect(self.popsavedockandvalues) self.toolbar.addAction(save) self.restoredockandvaluesinit() restore = QtGui.QAction( QtGui.QIcon(QtGui.QApplication.style().standardIcon( QtGui.QStyle.SP_BrowserReload)), 'Restore saved layout', self) restore.triggered.connect(self.poprestoredockandvalues) self.toolbar.addAction(restore) self.checkbox_init() checkbox = QtGui.QAction( QtGui.QIcon(QtGui.QApplication.style().standardIcon( QtGui.QStyle.SP_FileDialogDetailedView)), 'Open & close specific windows', self) checkbox.triggered.connect(self.checkbox) self.toolbar.addAction(checkbox) def fixdock(self): if self.titlebarstate == 1: self.dlamb.hideTitleBar() self.drho.hideTitleBar() self.dalpha.hideTitleBar() self.dbeta.hideTitleBar() self.dcontrols.hideTitleBar() self.dphase.hideTitleBar() self.titlebarstate = 0 else: self.dlamb.showTitleBar() self.drho.showTitleBar() self.dalpha.showTitleBar() self.dbeta.showTitleBar() self.dcontrols.showTitleBar() self.dphase.showTitleBar() self.titlebarstate = 1 def savedockandvaluesinit(self): self.swindow = QtGui.QMainWindow() self.swindow.setWindowIcon(QtGui.QIcon(logo)) self.swindow.setWindowTitle('Save') boxeswidget = QtGui.QWidget(self.swindow) checkbox_layout = QtGui.QGridLayout(boxeswidget) self.sa_dock = QtGui.QCheckBox(boxeswidget) self.sa_dock.setText('Grid View') self.sa_dock.setChecked(True) self.sa_values = QtGui.QCheckBox(boxeswidget) self.sa_values.setText('Values') self.sa_values.setChecked(True) self.save_button = QtGui.QPushButton('Save', self) self.save_button.clicked.connect(self.savedockandvalues) checkbox_layout.addWidget(self.sa_dock, 0, 0, 1, 1) checkbox_layout.addWidget(self.sa_values, 0, 1, 1, 1) checkbox_layout.addWidget(self.save_button, 1, 0, 1, 2) self.swindow.setCentralWidget(boxeswidget) def restoredockandvaluesinit(self): self.rwindow = QtGui.QMainWindow() self.rwindow.setWindowIcon(QtGui.QIcon(logo)) self.rwindow.setWindowTitle('Restore') boxeswidget = QtGui.QWidget(self.rwindow) checkbox_layout = QtGui.QGridLayout(boxeswidget) self.re_dock = QtGui.QCheckBox(boxeswidget) self.re_dock.setText('Grid View') self.re_dock.setChecked(True) self.re_values = QtGui.QCheckBox(boxeswidget) self.re_values.setText('Values') self.re_values.setChecked(True) self.restore_button = QtGui.QPushButton('Restore', self) self.restore_button.clicked.connect(self.restoredockandvalues) checkbox_layout.addWidget(self.re_dock, 0, 0, 1, 1) checkbox_layout.addWidget(self.re_values, 0, 1, 1, 1) checkbox_layout.addWidget(self.restore_button, 1, 0, 1, 2) self.rwindow.setCentralWidget(boxeswidget) def savedockandvalues(self): if self.sa_values.checkState: self.savedlambdas = deepcopy(glo_var.lambdas) self.savedalpha = glo_var.alpha self.savedbeta = glo_var.beta self.savedl = glo_var.l if self.sa_dock.checkState: self.saved_state = [] self.state = self.area.saveState() self.saved_state = self.check_current_docks()[:] self.sopen = 0 self.swindow.hide() def popsavedockandvalues(self): if self.sopen == 0: self.swindow.show() self.sopen = 1 else: self.swindow.hide() self.sopen = 0 def poprestoredockandvalues(self): if self.ropen == 0: self.rwindow.show() self.ropen = 1 else: self.rwindow.hide() self.ropen = 0 def restoredockandvalues(self): if self.re_values.checkState: glo_var.lambdas = deepcopy(self.savedlambdas) glo_var.alpha = self.savedalpha glo_var.beta = self.savedbeta glo_var.l = self.savedl self.lamb_po.update() self.rh.update() self.phas.update() self.jalph.update() self.jbet.update() self.slid.update_alpha_slid(self.slid.ws[0]) self.slid.update_beta_slid(self.slid.ws[1]) self.slid.update_l_slid(self.slid.ws[2]) if self.re_dock.checkState: if self.state != None: closed_docks = [] self.check_current_docks() # Notice after below, current dock != real current dock. But it doesn't matter since we call check current dock every time. for i in self.current_docks: if i not in self.saved_state: eval(i).close() for i in self.saved_state: if i not in self.current_docks: eval(i + "add")() self.area.restoreState(self.state) self.ropen = 0 self.rwindow.hide() def realinit(self): self.clear_buttons = [ "self.lamb_po.p1main.coordinate_label.setText(\"\")", "self.rh.p2main.coordinate_label.setText(\"\")", "self.jalph.p3main.coordinate_label.setText(\"\")", "self.jbet.p4main.coordinate_label.setText(\"\")", "self.phas.p5main.coordinate_label.setText(\"\")" ] glo_var.pass_main(self) self.docklist = [ 'self.drho', 'self.dlamb', 'self.dalpha', 'self.dbeta', 'self.dphase', 'self.dcontrols' ] self.area = DockArea() self.drho = Dock("Particle Density \u2374", closable=True) self.dlamb = Dock("Hopping Rate \u03bb", closable=True) self.dphase = Dock("Phase Diagram", closable=True) self.dcontrols = Dock("Controls", closable=True) self.dalpha = Dock( "Current J and average density \u27e8\u2374\u27e9 as a function of \u03b1", closable=True) self.dbeta = Dock( "Current J and average density \u27e8\u2374\u27e9 as a function of \u03b2", closable=True) self.layout = QtGui.QHBoxLayout() self.layout.addWidget(self.area) self.mainframe.setLayout(self.layout) self.mainframe.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Raised) self.mainframe.setLineWidth(8) pg.setConfigOptions(antialias=True) self.rh = rho.rho(self.drho) self.jalph = jalpha.jalpha(self.dalpha, self.rh) self.jbet = jbeta.jbeta(self.dbeta, self.rh) self.phas = phase.phase(self.dphase) self.lamb_po = lamb_pol.lamb_pol(self.dlamb) self.slid = slider.Widget(self.dcontrols, self.lamb_po, self.phas, self.rh, self.jbet, self.jalph) self.lamb_po.receive(self.slid) # default values to restore is input self.savedlambdas = glo_var.lambdas[:] self.savedalpha = glo_var.alpha self.savedbeta = glo_var.beta self.savedl = glo_var.l self.pltlist = [['Lambda_fig', self.lamb_po.p1], ['Density_fig', self.rh.p2], ['Current_alpha_fig', self.jalph.plotitemtoexport], ['Current_beta_fig', self.jbet.plotitemtoexport], ['Phase_fig', self.phas.p5]] self.grid_view_1() self.setCentralWidget(self.area) print(dir(self.area)) print(self.area.childrenRect(), "childrenRect") print(self.area.contentsRect(), "contentesRect") print(self.area.rect()) def check_current_docks(self): self.current_docks = [] for i in self.docklist: if self.area.getContainer(eval(i)): self.current_docks += [i] else: pass return self.current_docks def toggle_grid_view(self): if self.gridstate == 0: self.grid_view_2() self.gridstate = 1 else: self.grid_view_1() self.gridstate = 0 def grid_view_1(self): for i in self.docklist: try: eval(i).close() except: pass self.drho.setMinimumSize(652, 370) self.dlamb.setMinimumSize(652, 370) self.dphase.setMinimumSize(487, 465) self.dcontrols.setMinimumSize(487, 275) self.dalpha.setMinimumSize(435, 370) self.dbeta.setMinimumSize(435, 370) self.area.addDock(self.drho, 'left') self.area.addDock(self.dlamb, 'bottom', self.drho) self.area.addDock(self.dphase, 'right') self.area.addDock(self.dcontrols, 'bottom', self.dphase) self.area.addDock(self.dalpha, 'right') self.area.addDock(self.dbeta, 'bottom', self.dalpha) self.drho.setMinimumSize(0, 0) self.dlamb.setMinimumSize(0, 0) self.dphase.setMinimumSize(0, 0) self.dcontrols.setMinimumSize(0, 0) self.dalpha.setMinimumSize(0, 0) self.dbeta.setMinimumSize(0, 0) def grid_view_2(self): for i in self.docklist: try: eval(i).close() except: pass self.area.addDock(self.drho, 'top') self.area.addDock(self.dlamb, 'bottom', self.drho) self.area.addDock(self.dphase, 'bottom') self.area.addDock(self.dcontrols, 'left', self.dphase) self.area.addDock(self.dalpha, 'right') self.area.addDock(self.dbeta, 'bottom', self.dalpha) def opengraphs(self): self.mn = main.main_class(app) self.window.hide() def loaddata(self): name = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.', 'text (*.txt *.csv)') if name == "": pass else: self.read_file(name) self.realinit() def read_file(self, input): global ison temp = [] if ison == 0: ison = 1 else: self.setCentralWidget(None) glo_var.initialize() if input[-3:] == 'csv': with open(input, newline='') as csvfile: spamreader = csv.reader(csvfile, delimiter=',', quotechar='|') lis = [] for j in spamreader: lis += j num_of_inputs = len(lis) glo_var.lambdas_degree = num_of_inputs for i in range(num_of_inputs): glo_var.lambdas += [[ i / (num_of_inputs - 1), round(eval(lis[i]), 2) ]] glo_var.alpha = 0.04 glo_var.beta = 0.04 glo_var.l = 1 elif input[-3:] == 'txt': f = open(input, 'r') # Breaks the loop i try: temp = [] while (True): T = f.readline().strip() temp += [[glo_var.lambdas_degree, round(eval(T), 2)]] except: for x, y in temp: glo_var.lambdas += [[x / (glo_var.lambdas_degree - 1), y]] glo_var.l = 1 else: err = QtGui.QMessageBox(self.win) err.setIcon(QMessageBox().Warning) err.setText("Please select txt or csv file Format") err.setWindowTitle("File Format Error") err.setStandardButtons(QMessageBox.Ok) err.buttonClicked.connect() def eventFilter(self, source, event): if event.type() == QtCore.QEvent.MouseMove: if event.buttons() == QtCore.Qt.NoButton: pos = event.pos() if self.toolbar.rect().contains( pos) or not self.area.rect().contains(pos): self.clear_points() else: pass return QtGui.QMainWindow.eventFilter(self, source, event) def clear_points(self, exclude=None): if not exclude: for x in self.clear_buttons: eval(x) else: for x in self.clear_buttons[:exclude - 1] + self.clear_buttons[exclude:]: eval(x)
class MainWindow(QMainWindow): """ Main control window for showing the live captured images and initiating special tasks """ def __init__(self, experiment): """ Inits the camera window :param experiment: Experiment that is controlled by the GUI """ super().__init__() self.setWindowTitle('PyNTA: Python Nanoparticle Tracking Analysis') self.setMouseTracking(True) self.experiment = experiment self.area = DockArea() self.setCentralWidget(self.area) self.resize(1064, 840) self.area.setMouseTracking(True) # Main widget self.camWidget = MonitorMainWidget() self.camWidget.setup_cross_cut(self.experiment.max_height) self.camWidget.setup_cross_hair( [self.experiment.max_width, self.experiment.max_height]) self.camWidget.setup_roi_lines( [self.experiment.max_width, self.experiment.max_height]) self.camWidget.setup_mouse_tracking() self.messageWidget = messageWidget() self.cheatSheet = popOutWindow() self.dmainImage = Dock("Camera", size=(80, 35)) # sizes are in percentage self.dmainImage.addWidget(self.camWidget) self.area.addDock(self.dmainImage, 'right') self.dmessage = Dock("Messages", size=(40, 30)) self.dmessage.addWidget(self.messageWidget) self.area.addDock(self.dmessage, 'right') # # Widget for displaying information to the user # # Small window to display the results of the special task # self.trajectoryWidget = trajectoryWidget() # # Window for the camera viewer # # self.camViewer = cameraViewer(self._session, self.camera, parent=self) # # Configuration widget with a parameter tree # # self.config = ConfigWidget(self._session) # # Line cut widget # self.crossCut = crossCutWindow(parent=self) # self.popOut = popOutWindow(parent=self) #_future: for making long message pop-ups # # Select settings Window # # self.selectSettings = HDFLoader() # self.refreshTimer = QtCore.QTimer() self.refreshTimer.timeout.connect(self.updateGUI) # self.refreshTimer.timeout.connect(self.crossCut.update) # self.refreshTimer.start(self.experiment.config['GUI']['refresh_time']) # # self.acquiring = False # self.logmessage = [] # # ''' Initialize the camera and the camera related things ''' # self.max_sizex = self.camera.GetCCDWidth() # self.max_sizey = self.camera.GetCCDHeight() # self.current_width = self.max_sizex # self.current_height = self.max_sizey # # # if self._session.Camera['roi_x1'] == 0: # # self._session.Camera = {'roi_x1': 1} # # if self._session.Camera['roi_x2'] == 0 or self._session.Camera['roi_x2'] > self.max_sizex: # # self._session.Camera = {'roi_x2': self.max_sizex} # # if self._session.Camera['roi_y1'] == 0: # # self._session.Camera = {'roi_y1': 1} # # if self._session.Camera['roi_y2'] == 0 or self._session.Camera['roi_y2'] > self.max_sizey: # # self._session.Camera = {'roi_y2': self.max_sizey} # # # self.config.populateTree(self.experiment.config) # self.lastBuffer = time.time() # self.lastRefresh = time.time() # # # Program variables # self.tempimage = [] # self.bgimage = [] # self.trackinfo = np.zeros((1,5)) # real particle trajectory filled by "LocateParticle" analysis # # self.noiselvl = self._session.Tracking['noise_level'] # self.fps = 0 # self.buffertime = 0 # self.buffertimes = [] # self.refreshtimes = [] # self.totalframes = 0 # self.droppedframes = 0 # self.buffer_memory = 0 # self.waterfall_data = [] # self.watindex = 0 # Waterfall index # self.corner_roi = [] # Real coordinates of the corner of the ROI region. (Min_x and Min_y). # self.docks = [] # # self.corner_roi.append(self._session.Camera['roi_x1']) # # self.corner_roi.append(self._session.Camera['roi_y1']) # # # Program status controllers # self.continuous_saving = False # self.show_waterfall = False # self.subtract_background = False # self.save_running = False # self.accumulate_buffer = False # self.specialtask_running = False # self.dock_state = None # self.setupActions() self.setupToolbar() self.setupMenubar() # self.setupDocks() # self.setupSignals() ### This block should erased in due time and one must rely exclusively on Session variables. # self.filedir = self._session.Saving['directory'] # self.snap_filename = self._session.Saving['filename_photo'] # self.movie_filename = self._session.Saving['filename_video'] ### self.messageWidget.appendLog( 'i', 'Program started by %s' % self.experiment.config['User']['name']) def start_tracking(self): self.experiment.start_tracking() def showHelp(self): """To show the cheatsheet for shortcuts in a pop-up meassage box OBSOLETE, will be deleted after transferring info into a better message viewer! """ self.experiment.plot_histogram() # msgBox = QtGui.QMessageBox() # msgBox.setIcon(QtGui.QMessageBox.Information) # msgBox.setText("Keyboard shortcuts and Hotkeys") # msgBox.setInformativeText("Press details for a full list") # msgBox.setWindowTitle("pynta CheatSheet") # msgBox.setDetailedText(""" # F1, Show cheatsheet\n # F5, Snap image\n # F6, Continuous run\n # Alt+mouse: Select line \n # Ctrl+mouse: Crosshair \n # Ctrl+B: Toggle buffering\n # Ctrl+G: Toggle background subtraction\n # Ctrl+F: Empty buffer\n # Ctrl+C: Start tracking\n # Ctrl+V: Stop tracking\n # Ctrl+M: Autosave on\n # Ctrl+N: Autosave off\n # Ctrl+S: Save image\n # Ctrl+W: Start waterfall\n # Ctrl+Q: Exit application\n # Ctrl+Shift+W: Save waterfall data\n # Ctrl+Shift+T: Save trajectory\n # """) def setupActions(self): """Setups the actions that the program will have. It is placed into a function to make it easier to reuse in other windows. :rtype: None """ self.exitAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/power-icon.png'), '&Exit', self) self.exitAction.setShortcut('Ctrl+Q') self.exitAction.setStatusTip('Exit application') self.exitAction.triggered.connect(self.exitSafe) self.saveAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/floppy-icon.png'), '&Save image', self) self.saveAction.setShortcut('Ctrl+S') self.saveAction.setStatusTip('Save Image') self.saveAction.triggered.connect(self.saveImage) self.showHelpAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/info-icon.png'), 'Show cheatsheet', self) self.showHelpAction.setShortcut(QtCore.Qt.Key_F1) self.showHelpAction.setStatusTip('Show Cheatsheet') self.showHelpAction.triggered.connect(self.showHelp) self.saveWaterfallAction = QtGui.QAction("Save Waterfall", self) self.saveWaterfallAction.setShortcut('Ctrl+Shift+W') self.saveWaterfallAction.setStatusTip( 'Save waterfall data to new file') self.saveWaterfallAction.triggered.connect(self.saveWaterfall) self.saveTrajectoryAction = QtGui.QAction("Save Trajectory", self) self.saveTrajectoryAction.setShortcut('Ctrl+Shift+T') self.saveTrajectoryAction.setStatusTip( 'Save trajectory data to new file') self.saveTrajectoryAction.triggered.connect(self.saveTrajectory) self.snapAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/snap.png'), 'S&nap photo', self) self.snapAction.setShortcut(QtCore.Qt.Key_F5) self.snapAction.setStatusTip('Snap Image') self.snapAction.triggered.connect(self.snap) self.movieAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/video-icon.png'), 'Start &movie', self) self.movieAction.setShortcut(QtCore.Qt.Key_F6) self.movieAction.setStatusTip('Start Movie') self.movieAction.triggered.connect(self.startMovie) self.movieSaveStartAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Download-Database-icon.png'), 'Continuous saves', self) self.movieSaveStartAction.setShortcut('Ctrl+M') self.movieSaveStartAction.setStatusTip('Continuous save to disk') self.movieSaveStartAction.triggered.connect(self.movieSave) self.movieSaveStopAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Delete-Database-icon.png'), 'Stop continuous saves', self) self.movieSaveStopAction.setShortcut('Ctrl+N') self.movieSaveStopAction.setStatusTip('Stop continuous save to disk') self.movieSaveStopAction.triggered.connect(self.movieSaveStop) self.startWaterfallAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Blue-Waterfall-icon.png'), 'Start &Waterfall', self) self.startWaterfallAction.setShortcut('Ctrl+W') self.startWaterfallAction.setStatusTip('Start Waterfall') self.startWaterfallAction.triggered.connect(self.startWaterfall) self.toggleBGAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/noBg.png'), 'Toggle B&G-reduction', self) self.toggleBGAction.setShortcut('Ctrl+G') self.toggleBGAction.setStatusTip('Toggle Background Reduction') self.toggleBGAction.triggered.connect(self.start_tracking) self.setROIAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Zoom-In-icon.png'), 'Set &ROI', self) self.setROIAction.setShortcut('Ctrl+T') self.setROIAction.setStatusTip('Set ROI') self.setROIAction.triggered.connect(self.getROI) self.clearROIAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Zoom-Out-icon.png'), 'Set R&OI', self) self.clearROIAction.setShortcut('Ctrl+T') self.clearROIAction.setStatusTip('Clear ROI') self.clearROIAction.triggered.connect(self.clearROI) self.accumulateBufferAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/disk-save.png'), 'Accumulate buffer', self) self.accumulateBufferAction.setShortcut('Ctrl+B') self.accumulateBufferAction.setStatusTip( 'Start or stop buffer accumulation') self.accumulateBufferAction.triggered.connect(self.bufferStatus) self.clearBufferAction = QtGui.QAction('Clear Buffer', self) self.clearBufferAction.setShortcut('Ctrl+F') self.clearBufferAction.setStatusTip('Clears the buffer') self.clearBufferAction.triggered.connect(self.emptyQueue) self.viewerAction = QtGui.QAction('Start Viewer', self) # self.viewerAction.triggered.connect(self.camViewer.show) self.configAction = QtGui.QAction('Config Window', self) # self.configAction.triggered.connect(self.config.show) self.dockAction = QtGui.QAction('Restore Docks', self) self.dockAction.triggered.connect(self.setupDocks) self.crossCutAction = QtGui.QAction( QtGui.QIcon('pynta/View/GUI/Icons/Ruler-icon.png'), 'Show cross cut', self) # self.crossCutAction.triggered.connect(self.crossCut.show) self.settingsAction = QtGui.QAction('Load config', self) # self.settingsAction.triggered.connect(self.selectSettings.show) def setupToolbar(self): """Setups the toolbar with the desired icons. It's placed into a function to make it easier to reuse in other windows. """ self.toolbar = self.addToolBar('Exit') self.toolbar.addAction(self.exitAction) self.toolbar2 = self.addToolBar('Image') self.toolbar2.addAction(self.saveAction) self.toolbar2.addAction(self.snapAction) self.toolbar2.addAction(self.crossCutAction) self.toolbar3 = self.addToolBar('Movie') self.toolbar3.addAction(self.movieAction) self.toolbar3.addAction(self.movieSaveStartAction) self.toolbar3.addAction(self.movieSaveStopAction) self.toolbar4 = self.addToolBar('Extra') self.toolbar4.addAction(self.startWaterfallAction) self.toolbar4.addAction(self.setROIAction) self.toolbar4.addAction(self.clearROIAction) self.toolbar4.addAction(self.clearROIAction) self.toolbar4.addAction(self.toggleBGAction) self.toolbar5 = self.addToolBar('Help') self.toolbar5.addAction(self.showHelpAction) def setupMenubar(self): """Setups the menubar. """ menubar = self.menuBar() self.fileMenu = menubar.addMenu('&File') self.fileMenu.addAction(self.settingsAction) self.fileMenu.addAction(self.saveAction) self.fileMenu.addAction(self.exitAction) self.snapMenu = menubar.addMenu('&Snap') self.snapMenu.addAction(self.snapAction) self.snapMenu.addAction(self.saveAction) self.movieMenu = menubar.addMenu('&Movie') self.movieMenu.addAction(self.movieAction) self.movieMenu.addAction(self.movieSaveStartAction) self.movieMenu.addAction(self.movieSaveStopAction) self.movieMenu.addAction(self.startWaterfallAction) self.configMenu = menubar.addMenu('&Configure') self.configMenu.addAction(self.toggleBGAction) self.configMenu.addAction(self.setROIAction) self.configMenu.addAction(self.clearROIAction) self.configMenu.addAction(self.accumulateBufferAction) self.configMenu.addAction(self.clearBufferAction) self.configMenu.addAction(self.viewerAction) self.configMenu.addAction(self.configAction) self.configMenu.addAction(self.dockAction) self.saveMenu = menubar.addMenu('S&ave') self.snapMenu.addAction(self.saveAction) self.saveMenu.addAction(self.saveWaterfallAction) self.saveMenu.addAction(self.saveTrajectoryAction) self.helpMenu = menubar.addMenu('&Help') self.helpMenu.addAction(self.showHelpAction) def setupDocks(self): """Setups the docks in order to recover the initial configuration if one gets closed.""" for d in self.docks: try: d.close() except: pass self.docks = [] self.dmainImage = Dock("Camera", size=(80, 35)) #sizes are in percentage self.dwaterfall = Dock("Waterfall", size=(80, 35)) self.dparams = Dock("Parameters", size=(20, 100)) self.dtraj = Dock("Trajectory", size=(40, 30)) # self.dstatus = Dock("Status", size=(100, 3)) self.area.addDock(self.dmainImage, 'right') self.area.addDock(self.dparams, 'left', self.dmainImage) self.area.addDock(self.dtraj, 'bottom', self.dmainImage) self.area.addDock(self.dmessage, 'right', self.dtraj) self.docks.append(self.dmainImage) self.docks.append(self.dtraj) self.docks.append(self.dmessage) self.docks.append(self.dparams) self.docks.append(self.dwaterfall) # self.area.addDock(self.dstatus, 'bottom', self.dparams) self.dmainImage.addWidget(self.camWidget) self.dparams.addWidget(self.config) self.dtraj.addWidget(self.trajectoryWidget) self.dock_state = self.area.saveState() def setupSignals(self): """Setups all the signals that are going to be handled during the excution of the program.""" self.connect(self._session, QtCore.SIGNAL('updated'), self.config.populateTree) self.connect(self.config, QtCore.SIGNAL('updateSession'), self.updateSession) self.connect(self.camWidget, QtCore.SIGNAL('specialTask'), self.startSpecialTask) self.connect(self.camWidget, QtCore.SIGNAL('stopSpecialTask'), self.stopSpecialTask) self.connect(self.camViewer, QtCore.SIGNAL('stopMainAcquisition'), self.stopMovie) self.connect(self, QtCore.SIGNAL('stopChildMovie'), self.camViewer.stopCamera) self.connect(self, QtCore.SIGNAL('closeAll'), self.camViewer.closeViewer) self.connect(self.selectSettings, QtCore.SIGNAL("settings"), self.update_settings) self.connect(self, QtCore.SIGNAL('closeAll'), self.selectSettings.close) def snap(self): """Function for acquiring a single frame from the camera. It is triggered by the user. It gets the data the GUI will be updated at a fixed framerate. """ if self.experiment.acquiring: #If it is itself acquiring a message is displayed to the user warning him msgBox = QtGui.QMessageBox() msgBox.setIcon(QtGui.QMessageBox.Critical) msgBox.setText("You cant snap a photo while in free run") msgBox.setInformativeText("The program is already acquiring data") msgBox.setWindowTitle("Already acquiring") msgBox.setDetailedText( """When in free run, you can\'t trigger another acquisition. \n You should stop the free run acquisition and then snap a photo.""" ) msgBox.setStandardButtons(QtGui.QMessageBox.Ok) retval = msgBox.exec_() self.messageWidget.appendLog('e', 'Tried to snap while in free run') else: self.experiment.snap() # self.messageWidget.appendLog('i', 'Snapped photo') def toggleBGReduction(self): """Toggles between background cancellation modes. Takes a background snap if necessary """ if self.subtract_background: self.subtract_background = False self.messageWidget.appendLog('i', 'Background reduction deactivated') else: if len(self.tempimage) == 0: self.snap() self.messageWidget.appendLog('i', 'Snapped an image as background') else: self.subtract_background = True self.bgimage = self.tempimage.astype(float) self.messageWidget.appendLog('i', 'Background reduction active') def saveImage(self): """Saves the image that is being displayed to the user. """ if len(self.tempimage) >= 1: # Data will be appended to existing file fn = self._session.Saving['filename_photo'] filename = '%s.hdf5' % (fn) fileDir = self._session.Saving['directory'] if not os.path.exists(fileDir): os.makedirs(fileDir) f = h5py.File(os.path.join(fileDir, filename), "a") now = str(datetime.now()) g = f.create_group(now) dset = g.create_dataset('image', data=self.tempimage) meta = g.create_dataset('metadata', data=self._session.serialize()) f.flush() f.close() self.messageWidget.appendLog('i', 'Saved photo') def startMovie(self): self.experiment.start_free_run() def stopMovie(self): if self.acquiring: self.workerThread.keep_acquiring = False while self.workerThread.isRunning(): pass self.acquiring = False self.camera.stopAcq() self.messageWidget.appendLog('i', 'Continuous run stopped') if self.continuous_saving: self.movieSaveStop() def movieData(self): """Function just to trigger and read the camera in the separate thread. """ self.workerThread.start() def movieSave(self): """Saves the data accumulated in the queue continuously. """ if not self.continuous_saving: # Child process to save the data. It runs continuously until and exit flag # is passed through the Queue. (self.q.put('exit')) self.accumulate_buffer = True if len(self.tempimage) > 1: im_size = self.tempimage.nbytes max_element = int(self._session.Saving['max_memory'] / im_size) #self.q = Queue(0) fn = self._session.Saving['filename_video'] filename = '%s.hdf5' % (fn) fileDir = self._session.Saving['directory'] if not os.path.exists(fileDir): os.makedirs(fileDir) to_save = os.path.join(fileDir, filename) metaData = self._session.serialize( ) # This prints a YAML-ready version of the session. self.p = Process(target=workerSaver, args=( to_save, metaData, self.q, )) # self.p.start() self.continuous_saving = True self.messageWidget.appendLog('i', 'Continuous autosaving started') else: self.messageWidget.appendLog( 'w', 'Continuous savings already triggered') def movieSaveStop(self): """Stops the saving to disk. It will however flush the queue. """ if self.continuous_saving: self.q.put('Stop') self.accumulate_buffer = False #self.p.join() self.messageWidget.appendLog('i', 'Continuous autosaving stopped') self.continuous_saving = False def emptyQueue(self): """Clears the queue. """ # Worker thread for clearing the queue. self.clearWorker = Process(target=clearQueue, args=(self.q, )) self.clearWorker.start() def startWaterfall(self): """Starts the waterfall. The waterfall can be accelerated if camera supports hardware binning in the appropriate direction. If not, has to be done via software but the acquisition time cannot be improved. TODO: Fast waterfall should have separate window, since the acquisition of the full CCD will be stopped. """ if not self.show_waterfall: self.watWidget = waterfallWidget() self.area.addDock(self.dwaterfall, 'bottom', self.dmainImage) self.dwaterfall.addWidget(self.watWidget) self.show_waterfall = True Sx, Sy = self.camera.getSize() self.waterfall_data = np.zeros( (self._session.GUI['length_waterfall'], Sx)) self.watWidget.img.setImage(np.transpose(self.waterfall_data), autoLevels=False, autoRange=False, autoHistogramRange=False) self.messageWidget.appendLog('i', 'Waterfall opened') else: self.closeWaterfall() def stopWaterfall(self): """Stops the acquisition of the waterfall. """ pass def closeWaterfall(self): """Closes the waterfall widget. """ if self.show_waterfall: self.watWidget.close() self.dwaterfall.close() self.show_waterfall = False del self.waterfall_data self.messageWidget.appendLog('i', 'Waterfall closed') def setROI(self, X, Y): """ Gets the ROI from the lines on the image. It also updates the GUI to accommodate the changes. :param X: :param Y: :return: """ if not self.acquiring: self.corner_roi[0] = X[0] self.corner_roi[1] = Y[0] if self._session.Debug['to_screen']: print('Corner: %s, %s' % (self.corner_roi[0], self.corner_roi[1])) self._session.Camera = {'roi_x1': int(X[0])} self._session.Camera = {'roi_x2': int(X[1])} self._session.Camera = {'roi_y1': int(Y[0])} self._session.Camera = {'roi_y2': int(Y[1])} self.messageWidget.appendLog('i', 'Updated roi_x1: %s' % int(X[0])) self.messageWidget.appendLog('i', 'Updated roi_x2: %s' % int(X[1])) self.messageWidget.appendLog('i', 'Updated roi_y1: %s' % int(Y[0])) self.messageWidget.appendLog('i', 'Updated roi_y2: %s' % int(Y[1])) Nx, Ny = self.camera.setROI(X, Y) Sx, Sy = self.camera.getSize() self.current_width = Sx self.current_height = Sy self.tempimage = np.zeros((Nx, Ny)) self.camWidget.hline1.setValue(1) self.camWidget.hline2.setValue(Ny) self.camWidget.vline1.setValue(1) self.camWidget.vline2.setValue(Nx) self.trackinfo = np.zeros((1, 5)) #self.camWidget.img2.clear() if self.show_waterfall: self.waterfall_data = np.zeros( (self._session.GUI['length_waterfall'], self.current_width)) self.watWidget.img.setImage(np.transpose(self.waterfall_data)) self.config.populateTree(self._session) self.messageWidget.appendLog('i', 'Updated the ROI') else: self.messageWidget.appendLog('e', 'Cannot change ROI while acquiring.') def getROI(self): """Gets the ROI coordinates from the GUI and updates the values.""" y1 = np.int(self.camWidget.hline1.value()) y2 = np.int(self.camWidget.hline2.value()) x1 = np.int(self.camWidget.vline1.value()) x2 = np.int(self.camWidget.vline2.value()) X = np.sort((x1, x2)) Y = np.sort((y1, y2)) # Updates to the real values X += self.corner_roi[0] - 1 Y += self.corner_roi[1] - 1 self.setROI(X, Y) def clearROI(self): """Resets the roi to the full image. """ if not self.acquiring: self.camWidget.hline1.setValue(1) self.camWidget.vline1.setValue(1) self.camWidget.vline2.setValue(self.max_sizex) self.camWidget.hline2.setValue(self.max_sizey) self.corner_roi = [1, 1] self.getROI() else: self.messageWidget.appendLog('e', 'Cannot change ROI while acquiring.') def bufferStatus(self): """Starts or stops the buffer accumulation. """ if self.accumulate_buffer: self.accumulate_buffer = False self.messageWidget.appendLog('i', 'Buffer accumulation stopped') else: self.accumulate_buffer = True self.messageWidget.appendLog('i', 'Buffer accumulation started') def getData(self, data, origin): """Gets the data that is being gathered by the working thread. .. _getData: .. data: single image or a list of images (saved in buffer) .. origin: indicates which command has trigerred execution of this method (e.g. 'snap' of 'movie') both input variables are handed it through QThread signal that is "emit"ted """ s = 0 if origin == 'snap': # Single snap. self.acquiring = False self.workerThread.origin = None self.workerThread.keep_acquiring = False # This already happens in the worker thread itself. self.camera.stopAcq() if isinstance(data, list): for d in data: if self.accumulate_buffer: s = float(self.q.qsize()) * int(d.nbytes) / 1024 / 1024 if s < self._session.Saving['max_memory']: self.q.put(d) else: self.droppedframes += 1 if self.show_waterfall: if self.watindex == self._session.GUI['length_waterfall']: if self._session.Saving['autosave_trajectory']: self.saveWaterfall() self.waterfall_data = np.zeros( (self._session.GUI['length_waterfall'], self.current_width)) self.watindex = 0 centerline = np.int(self.current_height / 2) vbinhalf = np.int(self._session.GUI['vbin_waterfall']) if vbinhalf >= self.current_height / 2 - 1: wf = np.array([np.sum(d, 1)]) else: wf = np.array([ np.sum( d[:, centerline - vbinhalf:centerline + vbinhalf], 1) ]) self.waterfall_data[self.watindex, :] = wf self.watindex += 1 self.totalframes += 1 self.tempimage = d else: self.tempimage = data if self.accumulate_buffer: s = float(self.q.qsize()) * int(data.nbytes) / 1024 / 1024 if s < self._session.Saving['max_memory']: self.q.put(data) else: self.droppedframes += 1 if self.show_waterfall: if self.watindex == self._session.GUI['length_waterfall']: # checks if the buffer variable for waterfall image is full, saves it if requested, and sets it to zero. if self._session.Saving['autosave_trajectory']: self.saveWaterfall() self.waterfall_data = np.zeros( (self._session.GUI['length_waterfall'], self.current_width)) self.watindex = 0 centerline = np.int(self.current_height / 2) vbinhalf = np.int(self._session.GUI['vbin_waterfall'] / 2) if vbinhalf >= self.current_height - 1: wf = np.array([np.sum(data, 1)]) else: wf = np.array([ np.sum( data[:, centerline - vbinhalf:centerline + vbinhalf], 1) ]) self.waterfall_data[self.watindex, :] = wf self.watindex += 1 self.totalframes += 1 new_time = time.time() self.buffertime = new_time - self.lastBuffer self.lastBuffer = new_time self.buffer_memory = s if self._session.Debug['queue_memory']: print('Queue Memory: %3.2f MB' % self.buffer_memory) def updateGUI(self): """Updates the image displayed to the user. """ if self.experiment.temp_image is not None: img = self.experiment.temp_image self.camWidget.img.setImage(img.astype(int), autoLevels=False, autoRange=False, autoHistogramRange=False) if self.experiment.link_particles_running: self.camWidget.draw_target_pointer( self.experiment.localize_particles_image(img)) def saveWaterfall(self): """Saves the waterfall data, if any. """ if len(self.waterfall_data) > 1: fn = self._session.Saving['filename_waterfall'] filename = '%s.hdf5' % (fn) fileDir = self._session.Saving['directory'] if not os.path.exists(fileDir): os.makedirs(fileDir) f = h5py.File(os.path.join(fileDir, filename), "a") now = str(datetime.now()) g = f.create_group(now) dset = g.create_dataset('waterfall', data=self.waterfall_data) meta = g.create_dataset('metadata', data=self._session.serialize().encode( "ascii", "ignore")) f.flush() f.close() self.messageWidget.appendLog('i', 'Saved Waterfall') def saveTrajectory(self): """Saves the trajectory data, if any. """ if len(self.trackinfo) > 1: fn = self._session.Saving['filename_trajectory'] filename = '%s.hdf5' % (fn) fileDir = self._session.Saving['directory'] if not os.path.exists(fileDir): os.makedirs(fileDir) f = h5py.File(os.path.join(fileDir, filename), "a") now = str(datetime.now()) g = f.create_group(now) dset = g.create_dataset('trajectory', data=[self.trackinfo]) meta = g.create_dataset('metadata', data=self._session.serialize().encode( "ascii", "ignore")) f.flush() f.close() self.messageWidget.appendLog('i', 'Saved Trajectory') def update_settings(self, settings): new_session = _session(settings) self.updateSession(new_session) self.config.populateTree(self._session) def updateSession(self, session): """Updates the session variables passed by the config window. """ update_cam = False update_roi = False update_exposure = False update_binning = True for k in session.params['Camera']: new_prop = session.params['Camera'][k] old_prop = self._session.params['Camera'][k] if new_prop != old_prop: update_cam = True if k in ['roi_x1', 'roi_x2', 'roi_y1', 'roi_y2']: update_roi = True if self._session.Debug['to_screen']: print('Update ROI') elif k == 'exposure_time': update_exposure = True elif k in ['binning_x', 'binning_y']: update_binning = True if session.GUI['length_waterfall'] != self._session.GUI[ 'length_waterfall']: if self.show_waterfall: self.closeWaterfall() self.restart_waterfall = True self.messageWidget.appendLog('i', 'Parameters updated') self.messageWidget.appendLog( 'i', 'Measurement: %s' % session.User['measurement']) self._session = session.copy() if update_cam: if self.acquiring: self.stopMovie() if update_roi: X = np.sort( [session.Camera['roi_x1'], session.Camera['roi_x2']]) Y = np.sort( [session.Camera['roi_y1'], session.Camera['roi_y2']]) self.setROI(X, Y) if update_exposure: new_exp = self.camera.setExposure( session.Camera['exposure_time']) self._session.Camera = {'exposure_time': new_exp} self.messageWidget.appendLog('i', 'Updated exposure: %s' % new_exp) if self._session.Debug['to_screen']: print("New Exposure: %s" % new_exp) print(self._session) if update_binning: self.camera.setBinning(session.Camera['binning_x'], session.Camera['binning_y']) self.refreshTimer.stop() self.refreshTimer.start(session.GUI['refresh_time']) def startSpecialTask(self): """Starts a special task. This is triggered by the user with a special combination of actions, for example clicking with the mouse on a plot, draggin a crosshair, etc.""" if not self.specialtask_running: if self.acquiring: self.stopMovie() self.acquiring = False locy = self.camWidget.crosshair[0].getPos()[1] locx = self.camWidget.crosshair[1].getPos()[0] self.trackinfo = np.zeros((1, 5)) self.trajectoryWidget.plot.clear() imgsize = self.tempimage.shape iniloc = [locx, locy] self.specialTaskWorker = specialTaskTracking( self._session, self.camera, self.noiselvl, imgsize, iniloc) self.connect(self.specialTaskWorker, QtCore.SIGNAL('image'), self.getData) self.connect(self.specialTaskWorker, QtCore.SIGNAL('coordinates'), self.getParticleLocation) self.specialTaskWorker.start() self.specialtask_running = True self.messageWidget.appendLog('i', 'Live tracking started') else: print('Special task already running') def stopSpecialTask(self): """Stops the special task""" if self.specialtask_running: self.specialTaskWorker.keep_running = False self.specialtask_running = False if self._session.Saving['autosave_trajectory'] == True: self.saveTrajectory() self.messageWidget.appendLog('i', 'Live tracking stopped') def done(self): #self.saveRunning = False self.acquiring = False def exitSafe(self): self.close() def closeEvent(self, evnt): """ Triggered at closing. Checks that the save is complete and closes the dataFile """ self.experiment.finalize() # self.messageWidget.appendLog('i', 'Closing the program') # if self.acquiring: # self.stopMovie() # if self.specialtask_running: # self.stopSpecialTask() # while self.specialTaskWorker.isRunning(): # pass # self.emit(QtCore.SIGNAL('closeAll')) # self.camera.stopCamera() # self.movieSaveStop() # try: # # Checks if the process P exists and tries to close it. # if self.p.is_alive(): # qs = self.q.qsize() # with ProgressDialog("Finish saving data...", 0, qs) as dlg: # while self.q.qsize() > 1: # dlg.setValue(qs - self.q.qsize()) # time.sleep(0.5) # self.p.join() # except AttributeError: # pass # if self.q.qsize() > 0: # self.messageWidget.appendLog('i', 'The queue was not empty') # print('Freeing up memory...') # self.emptyQueue() # # # Save LOG. # fn = self._session.Saving['filename_log'] # timestamp = datetime.now().strftime('%H%M%S') # filename = '%s%s.log' % (fn, timestamp) # fileDir = self._session.Saving['directory'] # if not os.path.exists(fileDir): # os.makedirs(fileDir) # # f = open(os.path.join(fileDir,filename), "a") # for line in self.messageWidget.logText: # f.write(line+'\n') # f.flush() # f.close() # print('Saved LOG') super(MainWindow, self).closeEvent(evnt)
class Spectrometer(QObject): """ Defines a Spectrometer object, unified interface for many spectrometers Parameters that could be set in the selected detector plugin (should be defined there): 'laser_wl' : value of the configured laser (could eventually be changed, case of Xplora, Labram...) 'spectro_center_freq': value of the configured grating center wavelength (could eventually be changed, case of Shamrock, Xplora...) """ #custom signal that will be fired sometimes. Could be connected to an external object method or an internal method log_signal = Signal(str) #list of dicts enabling the settings tree on the user interface params = [ { 'title': 'Configuration settings:', 'name': 'config_settings', 'type': 'group', 'children': [ { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl', 'type': 'float', 'value': 515. }, { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl_list', 'type': 'list', 'limits': [''] }, { 'title': 'Current Detector:', 'name': 'curr_det', 'type': 'str', 'value': '' }, { 'title': 'Show detector:', 'name': 'show_det', 'type': 'bool', 'value': False }, ], }, { 'title': 'Calibration settings:', 'name': 'calib_settings', 'type': 'group', 'children': [ { 'title': 'Use calibration:', 'name': 'use_calib', 'type': 'bool', 'value': False }, { 'title': 'Save calibration', 'name': 'save_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Load calibration', 'name': 'load_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Calibration coeffs:', 'name': 'calib_coeffs', 'type': 'group', 'children': [ { 'title': 'Center wavelength (nm):', 'name': 'center_calib', 'type': 'float', 'value': 515. }, { 'title': 'Slope (nm/pxl):', 'name': 'slope_calib', 'type': 'float', 'value': 1. }, { 'title': 'Second order :', 'name': 'second_calib', 'type': 'float', 'value': 0 }, { 'title': 'third:', 'name': 'third_calib', 'type': 'float', 'value': 0 }, ] }, { 'title': 'Perform calibration:', 'name': 'do_calib', 'type': 'bool', 'value': False }, ] }, { 'title': 'Acquisition settings:', 'name': 'acq_settings', 'type': 'group', 'children': [ { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq', 'type': 'float', 'value': 800, }, { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq_txt', 'type': 'str', 'value': '????', 'readonly': True }, { 'title': 'Units:', 'name': 'units', 'type': 'list', 'value': 'nm', 'limits': ['nm', 'cm-1', 'eV'] }, { 'title': 'Exposure (ms):', 'name': 'exposure_ms', 'type': 'float', 'value': 100, }, ] }, ] def __init__(self, parent): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super().__init__() if not isinstance(parent, DockArea): raise Exception('no valid parent container, expected a DockArea') self.wait_time = 2000 #ms self.offline = True self.dockarea = parent self.mainwindow = parent.parent() self.spectro_widget = QtWidgets.QWidget() self.data_dict = None """ List of the possible plugins that could be used with Spectrometer module type : dimensionality of the detector name: name of the plugin calib = True means there is a builtin calibration of the frequency axis movable : tells if the dispersion can be set (for instance by moving a grating) unit: valid only if calib is True. Unit of the calibration axis (x_axis of the detector), most often in nanometers. Possible values are 'nm', 'radfs' (rad/femtosecond), 'eV' laser: if False, laser cannot be changed by the program, do it manually laser_list: if laser is True, laser_list gives a list of selectable lasers """ self.current_det = None # will be after initialization self.laser_set_manual = True #init the object parameters self.detector = None self.save_file_pathname = None self._spectro_wl = 550 # center wavelngth of the spectrum self.viewer_freq_axis = utils.Axis(data=None, label='Photon energy', units='') self.raw_data = [] #init the user interface self.dashboard = self.set_dashboard() self.dashboard.preset_loaded_signal.connect( lambda: self.show_detector(False)) self.dashboard.preset_loaded_signal.connect(self.set_detector) self.dashboard.preset_loaded_signal.connect(self.initialized) self.set_GUI() self.dashboard.new_preset_created.connect( lambda: self.create_menu(self.menubar)) self.show_detector(False) self.dockarea.setEnabled(False) def set_dashboard(self): params = [ { 'title': 'Spectro Settings:', 'name': 'spectro_settings', 'type': 'group', 'children': [ { 'title': 'Is calibrated?', 'name': 'iscalibrated', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has internal frequency calibration or not.' }, { 'title': 'Movable?', 'name': 'ismovable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its central frequency: as a movable grating' ' for instance.' }, { 'title': 'Laser selectable?', 'name': 'laser_selectable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its excitation ray' }, { 'title': 'Laser ray:', 'name': 'laser_ray', 'type': 'list', 'value': '', 'show_pb': True, 'tooltip': 'List of settable laser rays (not manual ones)' }, ] }, ] dashboard = DashBoard(self.dockarea.addTempArea()) dashboard.set_preset_path(spectro_path) options = [ dict(path='saving_options', options_dict=dict(visible=False)), dict(path='use_pid', options_dict=dict(visible=False)), dict(path='Moves', options_dict=dict(visible=False)) ] dashboard.set_extra_preset_params(params, options) dashboard.dockarea.window().setVisible(False) return dashboard def set_GUI(self): ########################################### ########################################### #init the docks containing the main widgets ####################################################################################################################### #create a dock containing a viewer object, displaying the data for the spectrometer self.dock_viewer = Dock('Viewer dock', size=(350, 350)) self.dockarea.addDock(self.dock_viewer, 'left') target_widget = QtWidgets.QWidget() self.viewer = Viewer1D(target_widget) self.dock_viewer.addWidget(target_widget) ################################################################ #create a logger dock where to store info senf from the programm self.dock_logger = Dock("Logger") self.logger_list = QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.dock_logger.addWidget(self.logger_list) self.dockarea.addDock(self.dock_logger, 'right') self.log_signal[str].connect(self.add_log) ############################################ # creating a menubar self.menubar = self.mainwindow.menuBar() self.create_menu(self.menubar) #creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() self.mainwindow.addToolBar(self.toolbar) #creating a status bar self.statusbar = QtWidgets.QStatusBar() self.statusbar.setMaximumHeight(25) self.status_laser = QtWidgets.QLabel('????') self.status_laser.setAlignment(Qt.AlignCenter) #self.status_laser.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) #self.status_laser.setReadOnly(True) self.status_laser.setMaximumWidth(80) self.status_laser.setMinimumWidth(80) self.status_laser.setToolTip('Current laser wavelength') self.status_laser.setStyleSheet("background-color: red") self.status_center = QtWidgets.QLabel('????') self.status_center.setAlignment(Qt.AlignCenter) #self.status_center.setReadOnly(True) #self.status_center.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) self.status_center.setMaximumWidth(80) self.status_center.setMinimumWidth(80) self.status_center.setToolTip( 'center frequency of the spectrum, either in nm or cm-1') self.status_center.setStyleSheet("background-color: red") self.status_init = QLED() self.status_init.setToolTip('Initialization state of the detector') self.status_init.set_as_false() self.status_init.clickable = False self.statusbar.addPermanentWidget(self.status_laser) self.statusbar.addPermanentWidget(self.status_center) self.statusbar.addPermanentWidget(self.status_init) self.dockarea.window().setStatusBar(self.statusbar) ############################################# self.settings = Parameter.create(name='settings', type='group', children=self.params) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) dock_config_settings = Dock('Configuration', size=(300, 350)) self.dockarea.addDock(dock_config_settings, 'above', self.dock_logger) # create main parameter tree self.config_settings_tree = ParameterTree() dock_config_settings.addWidget(self.config_settings_tree, 10) self.config_settings_tree.setMinimumWidth(300) self.config_settings_tree.setParameters(self.settings.child( ('config_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied dock_calib_settings = Dock('Calibration', size=(300, 350)) self.dockarea.addDock(dock_calib_settings, 'above', self.dock_logger) # create main parameter tree self.calib_settings_tree = ParameterTree() dock_calib_settings.addWidget(self.calib_settings_tree, 10) self.calib_settings_tree.setMinimumWidth(300) self.calib_settings_tree.setParameters(self.settings.child( ('calib_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied #this one for the custom application settings dock_acq_settings = Dock('Acquisition', size=(300, 350)) self.dockarea.addDock(dock_acq_settings, 'above', dock_config_settings) # create main parameter tree self.acq_settings_tree = ParameterTree() dock_acq_settings.addWidget(self.acq_settings_tree, 10) self.acq_settings_tree.setMinimumWidth(300) self.acq_settings_tree.setParameters(self.settings.child( ('acq_settings')), showTop=False) @Slot(ThreadCommand) def cmd_from_det(self, status): try: if status.command == 'spectro_wl': self.status_center.setStyleSheet("background-color: green") self.spectro_wl_is(status.attributes[0]) elif status.command == 'laser_wl': #self.laser_set_manual = False self.settings.child('config_settings', 'laser_wl_list').setValue( status.attributes[0]) self.status_laser.setText('{:}nm'.format(status.attributes[0])) self.status_laser.setStyleSheet("background-color: green") self.update_center_frequency(self.spectro_wl) elif status.command == 'exposure_ms': self.settings.child('acq_settings', 'exposure_ms').setValue( status.attributes[0]) elif status.command == "x_axis": x_axis = status.attributes[0] if np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.update_axis() except Exception as e: logger.exception(str(e)) def update_status(self, txt, wait_time=1000, log_type=None): """ """ self.statusbar.showMessage(txt, wait_time) if log_type is not None: self.log_signal.emit(txt) def set_detector(self): self.detector = self.dashboard.detector_modules[0] self.settings.child('config_settings', 'curr_det').setValue( f"{self.detector.settings.child('main_settings','DAQ_type').value()} / " f"{self.detector.settings.child('main_settings','detector_type').value()} / {self.detector.title}" ) self.detector.custom_sig[ThreadCommand].connect(self.cmd_from_det) self.current_det = \ dict(laser=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_selectable').value(), laser_list=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_ray').opts['limits'], movable=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'ismovable').value(), calib=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'iscalibrated').value(), ) self.detector.grab_done_signal.connect(self.show_data) self.settings.sigTreeStateChanged.disconnect( self.parameter_tree_changed) if self.current_det['laser']: self.settings.child('config_settings', 'laser_wl_list').show() self.settings.child('config_settings', 'laser_wl').hide() self.settings.child( 'config_settings', 'laser_wl_list').setOpts(limits=self.current_det['laser_list']) else: self.settings.child('config_settings', 'laser_wl').show() self.settings.child('config_settings', 'laser_wl_list').hide() self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) #apply current detector particularities #self.settings.child('acq_settings', 'spectro_center_freq').setOpts(readonly=not self.current_det['movable']) self.get_spectro_wl() QtWidgets.QApplication.processEvents() self.get_laser_wl() QtWidgets.QApplication.processEvents() self.get_exposure_ms() QtWidgets.QApplication.processEvents() def get_exposure_ms(self): self.detector.command_detector.emit(ThreadCommand('get_exposure_ms')) def set_exposure_ms(self, data): self.detector.command_detector.emit( ThreadCommand('set_exposure_ms', [data])) @Slot(bool) def initialized(self, state, offline=False): self.offline = offline self.grab_action.setEnabled(state) self.snap_action.setEnabled(state) if state or offline: self.status_init.set_as_true() self.dockarea.setEnabled(True) else: self.status_init.set_as_false() def update_center_frequency(self, spectro_wl): self._spectro_wl = spectro_wl if self.settings.child('acq_settings', 'units').value() == 'nm': self.settings.child('acq_settings', 'spectro_center_freq').setValue(spectro_wl) elif self.settings.child('acq_settings', 'units').value() == 'cm-1': self.settings.child('acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif self.settings.child('acq_settings', 'units').value() == 'eV': self.settings.child('acq_settings', 'spectro_center_freq').setValue( nm2eV(spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) def set_status_center(self, val, unit, precision=3): self.status_center.setText(f'{val:.{precision}f} {unit}') def spectro_wl_is(self, spectro_wl): """ this slot receives a signal from the detector telling it what's the current spectro_wl Parameters ---------- spectro_wl """ self._spectro_wl = spectro_wl self.update_center_frequency(spectro_wl) def set_spectro_wl(self, spectro_wl): try: if self.current_det['movable']: self.detector.command_detector.emit( ThreadCommand('set_spectro_wl', [spectro_wl])) except Exception as e: logger.exception(str(e)) def get_spectro_wl(self): if self.current_det['calib']: self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() self.detector.command_detector.emit( ThreadCommand('get_spectro_wl')) self.detector.command_detector.emit(ThreadCommand('get_axis')) else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.viewer_freq_axis['units'] = 'Pxls' def get_laser_wl(self): if self.current_det['laser']: self.detector.command_detector.emit(ThreadCommand('get_laser_wl')) else: self.settings.child('config_settings', 'laser_wl').setValue(0) @property def spectro_wl(self): # try to get the param value from detector (if it has been added in the plugin) return self._spectro_wl @spectro_wl.setter def spectro_wl(self, spec_wl): # try to get the param value from detector (if it has been added in the plugin) self.set_spectro_wl(spec_wl) def show_detector(self, show=True): self.dashboard.mainwindow.setVisible(show) for area in self.dashboard.dockarea.tempAreas: area.window().setVisible(show) def parameter_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'show_det': self.show_detector(data) elif param.name() == 'spectro_center_freq': unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': center_wavelength = data elif unit == 'cm-1': center_wavelength = Ecmrel2Enm( data, self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': center_wavelength = eV2nm(data) if int(self.spectro_wl * 100) != int( 100 * center_wavelength): #comprison at 1e-2 self.spectro_wl = center_wavelength self.update_axis() elif param.name() == 'units': if self.settings.child( 'acq_settings', 'spectro_center_freq').value() > 0.000000001: if data == 'nm': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( self._spectro_wl) elif data == 'cm-1': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( self._spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif data == 'eV': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( nm2eV(self._spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) elif param.name() == 'laser_wl_list': if data is not None: self.move_laser_wavelength(data) elif param.name() == 'laser_wl': if data is not None: self.move_laser_wavelength(data) if int(data) == 0: self.settings.child('acq_settings', 'units').setValue('nm') self.settings.child('acq_settings', 'units').setOpts(readonly=True) else: self.settings.child( 'acq_settings', 'units').setOpts(readonly=False) if data != 0: self.set_manual_laser_wl(data) elif param.name() == 'exposure_ms': self.set_exposure_ms(data) elif param.name() == 'do_calib': if len(self.raw_data) != 0: if data: self.calib_dock = Dock('Calibration module') self.dockarea.addDock(self.calib_dock) self.calibration = Calibration(self.dockarea) self.calib_dock.addWidget(self.calibration) self.calibration.coeffs_calib.connect( self.update_calibration) else: self.calib_dock.close() elif param.name() == 'save_calib': filename = select_file(start_path=self.save_file_pathname, save=True, ext='xml') if filename != '': custom_tree.parameter_to_xml_file( self.settings.child('calib_settings', 'calib_coeffs'), filename) elif param.name() == 'load_calib': filename = select_file(start_path=self.save_file_pathname, save=False, ext='xml') if filename != '': children = custom_tree.XML_file_to_parameter(filename) self.settings.child( 'calib_settings', 'calib_coeffs').restoreState( Parameter.create( title='Calibration coeffs:', name='calib_coeffs', type='group', children=children).saveState()) elif param.name() in custom_tree.iter_children(self.settings.child('calib_settings', 'calib_coeffs')) \ or param.name() == 'use_calib': if self.settings.child('calib_settings', 'use_calib').value(): calib_coeffs = [ self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value() ] self.update_center_frequency( self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value()) self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child( 'acq_settings', 'spectro_center_freq').setOpts(readonly=True) self.status_center.setStyleSheet( "background-color: green") self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() x_axis_pxls = np.linspace(0, self.raw_data[0].size - 1, self.raw_data[0].size) self.viewer_freq_axis['data'] = np.polyval( calib_coeffs, x_axis_pxls - np.max(x_axis_pxls) / 2) self.update_axis() else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.status_center.setStyleSheet( "background-color: red") elif change == 'parent': pass @Slot(list) def update_calibration(self, coeffs): self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').setValue(coeffs[0]) self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').setValue(coeffs[1]) if len(coeffs) > 2: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(coeffs[2]) else: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(0) if len(coeffs) > 3: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(coeffs[3]) else: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(0) def set_manual_laser_wl(self, laser_wl): messg = QtWidgets.QMessageBox() messg.setText( 'You manually changed the laser wavelength to {:}nm!'.format( laser_wl)) messg.setInformativeText("Is that correct?") messg.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) ret = messg.exec() if ret == QtWidgets.QMessageBox.Yes: self.status_laser.setText('{:}nm'.format(laser_wl)) self.status_laser.setStyleSheet("background-color: green") self.settings.child('acq_settings', 'units').setOpts(readonly=False) def move_laser_wavelength(self, laser_wavelength): #do hardware stuff if possible (Mock, labspec...) try: if self.current_det['laser']: self.detector.command_detector.emit( ThreadCommand('set_laser_wl', [laser_wavelength])) except Exception as e: logger.exception(str(e)) @Slot(OrderedDict) def show_data(self, data): """ do stuff with data from the detector if its grab_done_signal has been connected Parameters ---------- data: (OrderedDict) #OrderedDict(name=self.title,x_axis=None,y_axis=None,z_axis=None,data0D=None,data1D=None,data2D=None) """ self.data_dict = data if 'data1D' in data: self.raw_data = [] for key in data['data1D']: self.raw_data.append(data['data1D'][key]['data']) if 'x_axis' in data['data1D'][key]: x_axis = data['data1D'][key]['x_axis'] else: x_axis = utils.Axis(data=np.linspace( 0, len(data['data1D'][key]['data']) - 1, len(data['data1D'][key]['data'])), units='pxls', label='') if self.viewer_freq_axis['data'] is None: self.viewer_freq_axis.update(x_axis) elif np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.viewer.show_data(self.raw_data) self.update_axis() def update_axis(self): axis = utils.Axis() unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': axis['data'] = self.viewer_freq_axis['data'] elif unit == 'cm-1': axis['data'] = Enm2cmrel( self.viewer_freq_axis['data'], self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': axis['data'] = nm2eV(self.viewer_freq_axis['data']) axis['units'] = unit axis['label'] = 'Photon energy' self.viewer.x_axis = axis def create_menu(self, menubar): """ """ menubar.clear() # %% create file menu file_menu = menubar.addMenu('File') load_action = file_menu.addAction('Load file') load_action.triggered.connect(self.load_file) save_action = file_menu.addAction('Save file') save_action.triggered.connect(self.save_data) export_action = file_menu.addAction('Export as ascii') export_action.triggered.connect(lambda: self.save_data(export=True)) file_menu.addSeparator() file_menu.addAction('Show log file', self.show_log) file_menu.addSeparator() quit_action = file_menu.addAction('Quit') quit_action.triggered.connect(self.quit_function) settings_menu = menubar.addMenu('Settings') settings_menu.addAction('Show Units Converter', self.show_units_converter) docked_menu = settings_menu.addMenu('Docked windows') docked_menu.addAction('Load Layout', self.load_layout_state) docked_menu.addAction('Save Layout', self.save_layout_state) self.preset_menu = menubar.addMenu(self.dashboard.preset_menu) self.preset_menu.menu().addSeparator() self.preset_menu.menu().addAction( 'Offline Mode', lambda: self.initialized(state=False, offline=True)) def load_layout_state(self, file=None): """ Load and restore a layout state from the select_file obtained pathname file. See Also -------- utils.select_file """ try: if file is None: file = select_file(save=False, ext='dock') if file is not None: with open(str(file), 'rb') as f: dockstate = pickle.load(f) self.dockarea.restoreState(dockstate) file = file.name self.settings.child('loaded_files', 'layout_file').setValue(file) except Exception as e: logger.exception(str(e)) def save_layout_state(self, file=None): """ Save the current layout state in the select_file obtained pathname file. Once done dump the pickle. See Also -------- utils.select_file """ try: dockstate = self.dockarea.saveState() if 'float' in dockstate: dockstate['float'] = [] if file is None: file = select_file(start_path=None, save=True, ext='dock') if file is not None: with open(str(file), 'wb') as f: pickle.dump(dockstate, f, pickle.HIGHEST_PROTOCOL) except Exception as e: logger.exception(str(e)) def show_log(self): import webbrowser webbrowser.open(logging.getLogger('pymodaq').handlers[0].baseFilename) def show_units_converter(self): self.units_converter = UnitsConverter() dock_converter = Dock('Units Converter', size=(300, 350)) self.dockarea.addDock(dock_converter, 'bottom', self.dock_logger) dock_converter.addWidget(self.units_converter.parent) def load_file(self): data, fname, node_path = browse_data(ret_all=True) if data is not None: h5utils = H5BrowserUtil() h5utils.open_file(fname) data, axes, nav_axes, is_spread = h5utils.get_h5_data(node_path) data_node = h5utils.get_node(node_path) if data_node.attrs['type'] == 'data': if data_node.attrs['data_dimension'] == '1D': data_dict = OrderedDict(data1D=dict( raw=dict(data=data, x_axis=axes['x_axis']))) self.show_data(data_dict) h5utils.close_file() def quit_function(self): #close all stuff that need to be if self.detector is not None: self.detector.quit_fun() QtWidgets.QApplication.processEvents() self.mainwindow.close() def create_toolbar(self): self.toolbar.addWidget(QtWidgets.QLabel('Acquisition:')) iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.quit_action = QtWidgets.QAction(iconquit, "Quit program", None) self.toolbar.addAction(self.quit_action) self.quit_action.triggered.connect(self.quit_function) iconload = QtGui.QIcon() iconload.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Open.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadaction = QtWidgets.QAction( iconload, "Load target file (.h5, .png, .jpg) or data from camera", None) self.toolbar.addAction(self.loadaction) self.loadaction.triggered.connect(self.load_file) iconsave = QtGui.QIcon() iconsave.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/SaveAs.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.saveaction = QtWidgets.QAction(iconsave, "Save current data", None) self.toolbar.addAction(self.saveaction) self.saveaction.triggered.connect(self.save_data) iconrun = QtGui.QIcon() iconrun.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.grab_action = QtWidgets.QAction(iconrun, 'Grab', None) self.grab_action.setCheckable(True) self.toolbar.addAction(self.grab_action) self.grab_action.triggered.connect(self.grab_detector) iconsnap = QtGui.QIcon() iconsnap.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/snap.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.snap_action = QtWidgets.QAction(iconsnap, 'Snap', None) self.snap_action.triggered.connect(self.snap_detector) self.toolbar.addAction(self.snap_action) self.grab_action.setEnabled(False) self.snap_action.setEnabled(False) def grab_detector(self): self.detector.ui.grab_pb.click() def snap_detector(self): self.detector.ui.single_pb.click() def save_data(self, export=False): try: if export: ext = 'dat' else: ext = 'h5' path = select_file(start_path=self.save_file_pathname, save=True, ext=ext) if not (not (path)): if not export: h5saver = H5Saver(save_type='detector') h5saver.init_file(update_h5=True, custom_naming=False, addhoc_file_path=path) settings_str = b'<All_settings>' + custom_tree.parameter_to_xml_string( self.settings) if self.detector is not None: settings_str += custom_tree.parameter_to_xml_string( self.detector.settings) if hasattr(self.detector.ui.viewers[0], 'roi_manager'): settings_str += custom_tree.parameter_to_xml_string( self.detector.ui.viewers[0].roi_manager. settings) settings_str += custom_tree.parameter_to_xml_string( h5saver.settings) settings_str += b'</All_settings>' det_group = h5saver.add_det_group(h5saver.raw_group, "Data", settings_str) try: self.channel_arrays = OrderedDict([]) data_dim = 'data1D' if not h5saver.is_node_in_group(det_group, data_dim): self.channel_arrays['data1D'] = OrderedDict([]) data_group = h5saver.add_data_group( det_group, data_dim) for ind_channel, data in enumerate( self.raw_data): # list of numpy arrays channel = f'CH{ind_channel:03d}' channel_group = h5saver.add_CH_group( data_group, title=channel) self.channel_arrays[data_dim][ 'parent'] = channel_group self.channel_arrays[data_dim][ channel] = h5saver.add_data( channel_group, dict(data=data, x_axis=self.viewer_freq_axis), scan_type='', enlargeable=False) h5saver.close_file() except Exception as e: logger.exception(str(e)) else: data_to_save = [self.viewer_freq_axis['data']] data_to_save.extend([dat for dat in self.raw_data]) np.savetxt(path, data_to_save, delimiter='\t') except Exception as e: logger.exception(str(e)) @Slot(str) def add_log(self, txt): """ Add a log to the logger list from the given text log and the current time ================ ========= ====================== **Parameters** **Type** **Description** *txt* string the log to be added ================ ========= ====================== """ now = datetime.datetime.now() new_item = QtWidgets.QListWidgetItem(str(now) + ": " + txt) self.logger_list.addItem(new_item) ##to do ##self.save_parameters.logger_array.append(str(now)+": "+txt) @Slot(str) def emit_log(self, txt): """ Emit a log-signal from the given log index =============== ======== ======================= **Parameters** **Type** **Description** *txt* string the log to be emitted =============== ======== ======================= """ self.log_signal.emit(txt)