def __init__(self, parent=None): self.parent = parent super(OpenCsv, self).__init__() mdi_area = QMdiArea() # noinspection PyArgumentList dialog = QgsProviderRegistry.instance().selectWidget('delimitedtext') dialog.setWindowTitle(tr('Open a CSV')) layout = QVBoxLayout(self) layout.addWidget(mdi_area) mdi_area.addSubWindow(dialog) dialog.addVectorLayer[str, str, str].connect(self.success) dialog.rejected.connect(self.signalAskCloseWindow.emit)
class OrbitPlotMainWindow(QMainWindow): """ the main window has three major widgets: current, orbit tabs and element editor. """ def __init__(self, parent=None, machines=[], **kwargs): QMainWindow.__init__(self, parent) self.iqtApp = kwargs.get("iqt", None) self.setIconSize(QSize(32, 32)) self.error_bar = True self._dlgOrbitCor = None # logging self.logdock = QDockWidget("Log") self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) textedit = QPlainTextEdit(self.logdock) self.logger = logging.getLogger(__name__) self.guilogger = logging.getLogger("aphla.gui") # the "aphla" include lib part logging. When the lib is inside # QThread, logging message will be sent to TextEdit which is cross # thread. # self.guilogger = logging.getLogger("aphla") handler = QTextEditLoggingHandler(textedit) self.guilogger.addHandler(handler) self.guilogger.setLevel(logging.INFO) self.logdock.setWidget(textedit) self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea) self.logdock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.logdock.setFloating(False) self.logdock.setMinimumHeight(20) self.logdock.setMaximumHeight(100) self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.logdock.resize(200, 60) #print self.logdock.sizeHint() self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock) #print self.logdock.sizeHint() #print self.logdock.minimumSize() #print self.logdock.maximumSize() #self.logger.info("INFO") #self.logdock.setMinimumHeight(40) #self.logdock.setMaximumHeight(160) for msg in kwargs.get("infos", []): self.logger.info(msg) # dict of (machine, (lattice dict, default_lat, pvm)) self._mach = dict([(v[0], (v[1], v[2], v[3])) for v in machines]) for m, (lats, lat0, pvm) in self._mach.items(): self.logger.info( "machine '%s' initialized: [%s]" % (m, ", ".join([lat.name for k, lat in lats.items()]))) if pvm: for pv in pvm.dead(): self.logger.warn("'{0}' is disconnected.".format(pv)) ## DCCT current plot #self.dcct = DcctCurrentPlot() #self.dcct.setMinimumHeight(100) #self.dcct.setMaximumHeight(150) #t0 = time.time() #t = np.linspace(t0 - 8*3600*24, t0, 100) #self.dcct.curve.t = t #v = 500*np.exp((t[0] - t[:50])/(4*3600*24)) #self.dcct.curve.v = v.tolist()+v.tolist() #self.dcct.updatePlot() ## MDI area self.mdiarea = QMdiArea() self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"), self.updateMachineLatticeNames) self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp) self.live_orbit = True self.setCentralWidget(self.mdiarea) #self._elemed = ElementPropertyTabs() #self.elemeditor = ElementEditorDock(parent=self) #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea) #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable| # QDockWidget.DockWidgetClosable) #self.elemeditor.setFloating(False) #self.elemeditor.setEnabled(False) #self.elemeditor.setMinimumWidth(400) #self.elemeditor.setWidget(self._elemed) #self.elemeditor.show() #self.elemeditor.hide() #self.connect(self.elemeditor, # SIGNAL("elementChecked(PyQt_PyObject, bool)"), # self.physics.elementChecked) #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor) self.createMenuToolBar() # the first machine is the default self.machBox.addItems([v for v in self._mach.keys()]) self.reloadLatticeNames(self.machBox.currentText()) self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"), self.reloadLatticeNames) # update at 1/2Hz self.dt, self.itimer = 1500, 0 #self.timerId = None self.timerId = self.startTimer(self.dt) self.vbpm = None self.statusBar().showMessage("Welcome") #self.initMachine("nsls2v2") #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x', # "H Orbit", c = None) #print "Thread started", self.machinit.isRunning() #self.newElementPlots("BPM", "x, y") #self.newElementPlot("BPM", "y") #self.newElementPlot("HCOR", "x") #self.newElementPlot("VCOR", "y") #self.newElementPlot("QUAD", "b1") #self.newElementPlot("SEXT", "b2") def updateMachineLatticeNames(self, wsub): i = self.machBox.findText(wsub.machlat[0]) self.machBox.setCurrentIndex(i) self.reloadLatticeNames(wsub.machlat[0]) def reloadLatticeNames(self, mach): self.latBox.clear() cur_mach = str(self.machBox.currentText()) lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None)) self.latBox.addItems([lat for lat in lats.keys()]) if lat0: i = self.latBox.findText(lat0.name) self.latBox.setCurrentIndex(i) def closeEvent(self, event): self.physics.close() self.mdiarea.closeAllSubWindows() event.accept() def createMenuToolBar(self): # # file menu # #self.machMenu = self.menuBar().addMenu("&Machines") #self.connect(self.machMenu, SIGNAL("aboutToShow()"), # self.updateMachMenu) self.openMenu = self.menuBar().addMenu("&Open") self.openMenu.addAction("New Plot ...", self.openNewPlot) self.openMenu.addAction("New Tune Plot", self.openTunePlot) self.openMenu.addAction("New BPM Plot", partial(self.newElementPlots, "BPM", "x,y")) self.openMenu.addAction("New HCOR Plot", partial(self.newElementPlots, "HCOR", "x")) self.openMenu.addAction("New VCOR Plot", partial(self.newElementPlots, "VCOR", "y")) self.openMenu.addSeparator() self.openMenu.addAction("Open ORM", self.loadOrm) self.openMenu.addSeparator() self.openMenu.addAction("Save Lattice ...", self.saveSnapshot) fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self) fileQuitAction.setShortcut("Ctrl+Q") fileQuitAction.setToolTip("Quit the application") fileQuitAction.setStatusTip("Quit the application") #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png")) self.connect(fileQuitAction, SIGNAL("triggered()"), self.close) self.openMenu.addAction(fileQuitAction) # view self.viewMenu = self.menuBar().addMenu("&View") mkmenu = QMenu("&Mark", self.viewMenu) for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]: famAct = QAction(fam, self) famAct.setCheckable(True) self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam) mkmenu.addAction(famAct) # # errorbar #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"), # "Errorbar", self) #viewErrorBarAction.setCheckable(True) #viewErrorBarAction.setChecked(True) #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"), # self.errorBar) # #zoomM = QMenu("Zoom", self.viewMenu) # # #drift_from_now = QAction("Drift from Now", self) #drift_from_now.setCheckable(True) #drift_from_now.setShortcut("Ctrl+N") #drift_from_golden = QAction("Drift from Golden", self) #drift_from_golden.setCheckable(True) #drift_from_none = QAction("None", self) #drift_from_none.setCheckable(True) #self.viewMenu.addAction(viewLiveAction) #self.viewMenu.addAction(viewSingleShotAction) #self.viewMenu.addSeparator() #self.viewMenu.addAction(drift_from_now) #self.viewMenu.addAction(drift_from_golden) #self.viewMenu.addAction(drift_from_none) #self.viewMenu.addAction(viewAutoScale) #self.viewMenu.addAction(viewErrorBarAction) #self.viewMenu.addSeparator() self.viewMenu.addMenu(mkmenu) #drift_group = QActionGroup(self) #drift_group.addAction(drift_from_none) #drift_group.addAction(drift_from_now) #drift_group.addAction(drift_from_golden) #drift_from_none.setChecked(True) sep = self.viewMenu.addSeparator() #sep.setText("Drift") #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow) #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone) #self.connect(drift_from_golden, SIGNAL("triggered()"), # self.setDriftGolden) #viewStyle = QMenu("Line Style", self.viewMenu) #for act in ["Increase Point Size", "Decrease Point Size", None, # "NoCurve", "Lines", "Sticks", None, # "Solid Line", "Dashed Line", "Dotted Line", None, # "Increase Line Width", "Decrease Line Width", None, # "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle", # "Cross", "XCross", "HLine", "VLine", # "Star1", "Star2", "Hexagon", None, # "Red", "Blue", "Green"]: # if act is None: # viewStyle.addSeparator() # else: # viewStyle.addAction(act, self.setPlotStyle) #self.viewMenu.addMenu(viewStyle) #self.viewMenu.addSeparator() #self.viewMenu.addAction(viewZoomOut15Action) #self.viewMenu.addAction(viewZoomIn15Action) #self.viewMenu.addAction(viewZoomAutoAction) #self.viewMenu.addSeparator() self.viewMenu.addAction("ORM SV", self.plotSVD) # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7 #self.viewMenu.addAction(viewDcct) #for ac in self.viewMenu.actions(): ac.setDisabled(True) # self.controlMenu = self.menuBar().addMenu("&Tools") self.controlMenu.addAction(QIcon(":/control_choosebpm.png"), "En-/Disable BPM", partial(chooseElement, 'BPM')) self.controlMenu.addAction("En-/Disable COR", partial(chooseElement, 'COR')) #self.controlMenu.addAction(controlResetPvDataAction) self.controlMenu.addSeparator() self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot) #self.controlMenu.addAction(controlZoomInPlot1Action) #self.controlMenu.addAction(controlZoomOutPlot1Action) #self.controlMenu.addAction(controlZoomInPlot2Action) #self.controlMenu.addAction(controlZoomOutPlot2Action) self.controlMenu.addSeparator() self.controlMenu.addAction("Correct Hor. orbit", partial(aphla.correctOrbit, plane="H")) self.controlMenu.addAction("Correct Vert. orbit", partial(aphla.correctOrbit, plane="V")) self.controlMenu.addAction(QIcon(":/control_corrorbit.png"), "Correct orbit", partial(aphla.correctOrbit, plane="HV")) #steer_orbit.setDisabled(True) self.controlMenu.addAction("Local Bump ...", self.createLocalBump) self.controlMenu.addAction("Element Editor ...", self.showElementEditor) self.controlMenu.addSeparator() self.controlMenu.addAction("meas Beta", self.physics.measBeta) self.controlMenu.addAction("meas Dispersion", self.physics.measDispersion) self.controlMenu.addAction("beam based alignment", self.runBba) #for ac in self.controlMenu.actions(): ac.setDisabled(True) # Window self.windowMenu = self.menuBar().addMenu("&Windows") #self.windowMenu.addAction(self.elemeditor.toggleViewAction()) self.windowMenu.addAction(self.logdock.toggleViewAction()) #viewDcct = QAction("Beam Current", self) #viewDcct.setCheckable(True) #viewDcct.setChecked(True) #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible) #self.windowMenu.addAction(viewDcct) self.windowMenu.addSeparator() self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows) self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows) self.windowMenu.addAction("Tile Horizontally", self.tileSubWindowsHorizontally) # "ctrl+page up", "ctrl+page down" self.windowMenu.addAction("Previous", self.mdiarea.activatePreviousSubWindow, "Ctrl+Left") self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow, "Ctrl+Right") self.windowMenu.addSeparator() # debug self.debugMenu = self.menuBar().addMenu("&Debug") self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors) self.debugMenu.addAction("_Reset Quadrupoles_", self._reset_quadrupoles) self.debugMenu.addAction("_Random V Kick_", self._random_vkick) self.debugMenu.addAction("_Random H Kick_", self._random_hkick) #for ac in self.debugMenu.actions(): ac.setDisabled(True) # help self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("About mleap", self.showAbout) #toolbar machToolBar = self.addToolBar("Machines") self.machBox = QtGui.QComboBox() self.latBox = QtGui.QComboBox() #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"), # self.__setLattice) machToolBar.addWidget(self.machBox) machToolBar.addWidget(self.latBox) #toolbar = QToolBar(self) #self.addToolBar(toolbar) #fileToolBar = self.addToolBar("File") #fileToolBar.setObjectName("FileToolBar") #fileToolBar.addAction(fileQuitAction) # viewToolBar1 = self.addToolBar("Live View") #viewToolBar.setObjectName("ViewToolBar") #viewToolBar.addAction(viewZoomOut15Action) #viewToolBar.addAction(viewZoomIn15Action) #viewToolBar.addAction(viewZoomAutoAction) #viewToolBar1.addAction(viewLiveAction) #viewToolBar1.addAction(viewSingleShotAction) #viewToolBar1.addSeparator() viewToolBar1.addAction(QIcon(":/new_bpm.png"), "Orbits", partial(self.newElementPlots, "BPM", "x,y")) viewToolBar1.addAction(QIcon(":/new_cor.png"), "Correctors", partial(self.newElementPlots, "COR", "x,y")) viewToolBar1.addAction(QIcon(":/new_quad.png"), "Quadrupoles", partial(self.newElementPlots, "QUAD", "b1")) viewToolBar1.addAction(QIcon(":/new_sext.png"), "Sextupoles", partial(self.newElementPlots, "SEXT", "b2")) #viewToolBar.addAction(viewErrorBarAction) #viewToolBar.addAction(QWhatsThis.createAction(self)) #viewToolBar2 = self.addToolBar("Scale Plot") #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot), # (None, None, None), # (":/view_zoom_y.png", "Fit In Y", self.scalePlot), # (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot), # (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot), # (":/view_move_up.png", "Move Up", self.scalePlot), # (":/view_move_down.png", "Move Down", self.scalePlot), # (None, None, None), # (":/view_zoom_x.png", "Fit In X", self.scalePlot), # (":/view_zoomin_x.png", "Zoom In X", self.scalePlot), # (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot), # (":/view_move_left.png", "Move Left", self.scalePlot), # (":/view_move_right.png", "Move Right", self.scalePlot), # ] #for ico,name,hdl in zoomActions: # if hdl is None: continue # viewToolBar2.addAction(QIcon(ico), name, hdl) controlToolBar = self.addToolBar("Control") controlToolBar.addAction(QIcon(":/control_orbitcor.png"), "Correct Orbit", aphla.correctOrbit) controlToolBar.addAction(QIcon(":/control_localbump.png"), "Local Bump ...", self.createLocalBump) #controlToolBar.addAction(controlResetPvDataAction) def showAbout(self): QMessageBox.about( self, self.tr("mleap"), (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1 <p>Copyright © Lingyun Yang, BNL, 2013-2014. All rights reserved. <p>This application can be used to perform high level accelerator controls. <p>Python %2 - Qt %3 - PyQt %4 on %5""").arg(aphla.version.version).arg( platform.python_version()).arg(QtCore.QT_VERSION_STR).arg( QtCore.PYQT_VERSION_STR).arg(platform.system()))) def showElementEditor(self): mach, lat = self.getCurrentMachLattice() ed = ElementEditor(lat, parent=self) ed.setWindowFlags(Qt.Window) ed.setAttribute(Qt.WA_DeleteOnClose) ed.show() def getCurrentMachLattice(self, cadata=False): """return the current machine name and lattice object""" mach = str(self.machBox.currentText()) latname = str(self.latBox.currentText()) lat_dict, lat0, pvm = self._mach[mach] if not cadata: return mach, lat_dict[latname] else: return mach, lat_dict[latname], pvm def newElementPlots(self, elem, fields, **kw): self.logger.info("new plots: %s %s" % (elem, fields)) _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True) mach, lat = kw.get("machlat", (_mach, _lat)) handle = kw.get("handle", "readback") elems = lat.getElementList(elem) x, pvs = [], [] field_list = re.findall(r'[^ ,]+', fields) for fld in field_list: si, pvsi = [], [] for e in elems: if not e.isEnabled(): continue epv = e.pv(field=fld, handle=handle) if not epv: continue pvsi.append(epv[0]) si.append(e.sb) x.append(si) pvs.append(pvsi) if not pvs: self.logger.error("no data found for elements '{0}' " "and field '{1}'".format(elem, field)) return p = ApMdiSubPlot(pvs=pvs, x=x, labels=["%s.%s" % (elem, fld) for fld in field_list], magprof=lat.getBeamlineProfile(), iqt=self.iqtApp, **kw) #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) str_elem = "{0}".format(elem) if len(str_elem) > 12: str_elem = str_elem[:9] + "..." str_field = "{0}".format(fields) if len(str_field) > 12: str_field = str_field[:9] + "..." p.setWindowTitle("[%s.%s] %s %s" % (mach, lat.name, str_elem, str_field)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() ##print "Enable the buttons" #if len(self.mdiarea.subWindowList()) > 0: # self.elemeditor.setEnabled(True) def subPlotDestroyed(self): #if len(self.mdiarea.subWindowList()) == 0: # self.elemeditor.setEnabled(False) pass def loadOrm(self): fileName = QtGui.QFileDialog.getOpenFileName( self, "Open Orbit Response Matrix", "", "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)") fileName = str(fileName) try: m = np.loadtxt(fileName) except: QMessageBox.critical(self, "Abort", "Invalid matrix data") return mach, lat = self.getCurrentMachLattice() # assuming we already have the PV, name, field but just want to # replace the matrix elements. assert np.shape(m) == np.shape(lat.ormdata.m) nx, ny = np.shape(lat.ormdata.m) for i in range(nx): for j in range(ny): lat.ormdata.m[i, j] = m[i, j] def saveSnapshot(self): latdict = dict([(k, v[0]) for k, v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() snapdlg = SaveSnapshotDialog(latdict, mach) snapdlg.exec_() def saveLatSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join( dpath, dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5") fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") fileName = str(fileName) if not fileName: return aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("snapshot created '%s'" % fileName) def saveMachSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5")) fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") if not fileName: return fileName = str(fileName) import h5py f = h5py.File(str(fileName), 'w') f.close() self.logger.info("clean snapshot file created: '%s'" % fileName) for k, lat in self._mach[mach][0].items(): aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("lattice snapshot appended for '%s'" % lat.name) def openSnapshot(self): #self.logger.info("loading snapshot?") latdict = dict([(k, v[0]) for k, v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() lv = LatSnapshotMain(self, latdict, mach, self.logger) lv.setWindowFlags(Qt.Window) #self.logger.info("initialized") #lv.loadLatSnapshotH5() lv.exec_() def openTunePlot(self): mach, lat = self.getCurrentMachLattice() nu = lat.getElementList('tune') pvs = [(e.pv(field="x", handle="readback")[0], e.pv(field="y", handle="readback")[0]) for e in nu] labels = [e.name for e in nu] twiss = lat.getElementList("VA") pvs.extend([(e.pv(field="nux", handle="readback")[0], e.pv(field="nuy", handle="readback")[0]) for e in twiss]) labels.extend([e.name for e in twiss]) p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype="Tunes") #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() def openNewPlot(self): mach, lat = self.getCurrentMachLattice() fl = QtGui.QFormLayout() fl.addRow("Machine", QtGui.QLabel("%s" % mach)) fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name)) elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit() fl.addRow("Elements", elem) fl.addRow("Field", fld) dtype = QtGui.QComboBox() for tx in ["Array", "Waveform", "Time Series"]: dtype.addItem(tx) fl.addRow("Data Type", dtype) dlg = QtGui.QDialog() bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.connect(bx, SIGNAL("accepted()"), dlg.accept) self.connect(bx, SIGNAL("rejected()"), dlg.reject) h1 = QtGui.QHBoxLayout() h1.addStretch() h1.addWidget(bx) v1 = QtGui.QVBoxLayout() v1.addLayout(fl) v1.addLayout(h1) dlg.setLayout(v1) if dlg.exec_(): self.newElementPlots(str(elem.text()), str(fld.text()), machlat=(mach, lat), dtype=str(dtype.currentText())) def click_markfam(self, on): famname = self.sender().text() mks = [] mach, lat = self.getCurrentMachLattice() # need to convert to python str for elem in lat.getElementList(str(famname)): if elem.family != famname: continue if elem.virtual: continue mks.append([elem.name, 0.5 * (elem.sb + elem.se)]) for w in self.mdiarea.subWindowList(): w.setMarkers(mks, on) #print self._machlat.keys() def _reset_correctors(self): self.logger.info("reset correctors") aphla.hlalib._reset_trims() def _reset_quadrupoles(self): self.logger.info("reset quadrupoles") aphla.hlalib._reset_quad() def _random_hkick(self): mach, lat = self.getCurrentMachLattice() hcors = lat.getElementList('HCOR') for k in range(3): i = np.random.randint(len(hcors)) self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors))) hcors[i].x += np.random.rand() * 2e-6 def _random_vkick(self): mach, lat = self.getCurrentMachLattice() cors = lat.getElementList('VCOR') for k in range(3): i = np.random.randint(len(cors)) cors[i].y += np.random.rand() * 1e-6 self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format( cors[i].name, cors[i].y, cors[i].getUnit('y', None))) def viewDcctPlot(self, on): self.dcct.setVisible(on) def liveData(self, on): """Switch on/off live data taking""" self.live_orbit = on def scalePlot(self): w = self.mdiarea.currentSubWindow() if not w: return st, p = self.sender().text(), w.aplot if st == "Fit": p.scaleXBottom() p.scaleYLeft() # a hack bound = p.curvesBound() p.zoomer1.setZoomStack([bound]) elif st == "Fit In Y": p.scaleYLeft() elif st == "Zoom In Y": p.scaleYLeft(1. / 1.5) elif st == "Zoom Out Y": p.scaleYLeft(1.5) elif st == "Move Up": p.moveCurves(Qwt.QwtPlot.yLeft, 0.8) elif st == "Move Down": p.moveCurves(Qwt.QwtPlot.yLeft, -0.8) elif st == "Fit In X": p.scaleXBottom() elif st == "Zoom In X": p.scaleXBottom(1.0 / 1.5) elif st == "Zoom Out X": p.scaleXBottom(1.5) elif st == "Move Left": p.moveCurves(Qwt.QwtPlot.xBottom, 0.8) elif st == "Move Right": p.moveCurves(Qwt.QwtPlot.xBottom, -0.8) else: self.logger.error("unknow action '{0}'".format(st)) def getVisibleRange(self): w = self.mdiarea.currentSubWindow() if not w: mach, lat = self.getCurrentMachLattice() self.logger.warn( "no active plot, use full range of {0}.{1}".format( mach, lat.name)) return lat.getLocationRange() else: return w.currentXlim() def getVisibleElements(self, elemname, sb=None, se=None): w = self.mdiarea.currentSubWindow() mach, lat = self.getCurrentMachLattice() elems = lat.getElementList(elemname) if sb is not None: elems = [e for e in elems if e.sb >= sb] if se is not None: elems = [e for e in elems if e.se <= se] self.logger.info("searching for '{0}' in range [{1}, {2}]".format( elemname, sb, se)) return elems def timerEvent(self, e): if e.timerId() != self.timerId: return #if not self.elemeditor.isHidden(): # self.elemeditor.updateModelData() #if self.live_orbit: # self.itimer += 1 # #self.updatePlots() # #self.updateStatus() # for w in self.mdiarea.subWindowList(): # if not isinstance(w, ApMdiSubPlot): continue # if not w.live: continue # w.updatePlot() # self.statusBar().showMessage("plot updated: {0}".format( # time.strftime("%F %T"))) #else: # self.statusBar().showMessage("live update disabled") def singleShot(self): for w in self.mdiarea.subWindowList(): if not isinstance(w, ApMdiSubPlot): continue w.updatePlot() self.statusBar().showMessage("plot updated: {0}".format( time.strftime("%F %T"))) def elementSelected(self, elems): """this action is ignored""" mach, lat, elemnames = elems #_lat = self._machlat[mach][lat] self.logger.info("element selected") #elemobjs = _lat.getElementList(elemnames) #self._elemed.addElements(elemobjs) def activeOrbitPlot(self, field): mach = str(self.machBox.currentText()) lat = str(self.latBox.currentText()) for w in self.mdiarea.subWindowList(): #print w.machine(), w.lattice(), w.data.yfield if not isinstance(w, ApMdiSubPlot): continue if w.machine() != mach: continue if w.lattice() != lat: continue if w.data.yfield != field: continue return w return None def createLocalBump_(self): """create local bump""" if self._dlgOrbitCor is None: bpms = ap.getElements("BPM") cors = ap.getElements("COR") self._dlgOrbitCor = OrbitCorrDlg(bpms, cors) #corbitdlg.resize(600, 500) self._dlgOrbitCor.setWindowTitle("Create Local Bump") self._dlgOrbitCor.show() self._dlgOrbitCor.raise_() self._dlgOrbitCor.activateWindow() def createLocalBump(self): """create local bump""" bpms = ap.getElements("BPM") cors = ap.getElements("COR") dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self) #corbitdlg.resize(600, 500) dlgOrbitCor.setWindowTitle("Create Local Bump") dlgOrbitCor.show() dlgOrbitCor.raise_() dlgOrbitCor.activateWindow() dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose) def runBba(self): mach, lat = self.getCurrentMachLattice() bpms = [ e for e in lat.getElementList('BPM') if e not in self.physics.deadelems ] self.physics.runBba(bpms) def plotSVD(self): mach, lat = self.getCurrentMachLattice() if not lat.ormdata: QMessageBox.critical(self, "ORM SVD", "machine '%s' ORM data is not available" % \ mach, QMessageBox.Ok) return m, brec, trec = lat.ormdata.getMatrix(None, None, full=False, ignore=self.getDeadElements()) U, s, V = np.linalg.svd(m, full_matrices=True) #print np.shape(s), s self.sp = ApSvdPlot(s) self.sp.show() def tileSubWindowsHorizontally(self): pos = QtCore.QPoint(0, 0) subwins = self.mdiarea.subWindowList() for w in subwins: height = self.mdiarea.height() / len(subwins) rect = QtCore.QRect(0, 0, self.mdiarea.width(), height) w.setGeometry(rect) w.move(pos) pos.setY(pos.y() + w.height())
app = QApplication(sys.argv) pixmap = QPixmap(16, 16) pixmap.fill(QColor(0, 0, 0, 0)) icon = QIcon(pixmap) app.setWindowIcon(icon) mdiArea = QMdiArea() textEdit = QTextEdit() textEdit.setPlainText("Qt Quarterly is a paper-based newsletter " "exclusively available to Qt customers. Every " "quarter we mail out an issue that we hope will " "bring added insight and pleasure to your Qt " "programming, with high-quality technical articles " "written by Qt experts.") textWindow = mdiArea.addSubWindow(textEdit) textWindow.setWindowTitle("A Text Editor") label = QLabel() label.setPixmap(QPixmap("../../images/qt-logo.png")) labelWindow = mdiArea.addSubWindow(label) labelWindow.setWindowTitle("A Label") items = (("Henry", 23), ("Bill", 56), ("Susan", 19), ("Jane", 47)) table = QTableWidget(len(items), 2) for i in range(len(items)): name, age = items[i] item = QTableWidgetItem(name) table.setItem(i, 0, item) item = QTableWidgetItem(str(age))
class DocumentViewManager(QMainWindow): """ MDI area for displaying supporting documents within a given context e.g. supporting documents for a specific household based on the lifetime of the 'SourceDocumentManager' instance. """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowFlags(Qt.Window) self._mdi_area = QMdiArea() self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self._mdi_area) # set the size of mid_area and DocumentViewManager based on the # screen size. screen = QDesktopWidget().availableGeometry() self._mdi_area.resize(screen.width() - 30, screen.height() - 80) self.resize(self._mdi_area.size()) self._mdi_area.subWindowActivated.connect(self.update_actions) self._viewer_mapper = QSignalMapper(self) self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window) win_title = QApplication.translate( "DocumentViewManager", "Document Viewer" ) self.setWindowTitle(win_title) self.setUnifiedTitleAndToolBarOnMac(True) self.statusBar().showMessage( QApplication.translate( "DocumentViewManager", "Ready" ) ) self._doc_viewers = {} self._create_menu_actions() self.update_actions() def center(self): """ Move the Document viewer to the center of the screen. """ # Get the current screens' dimensions... screen = QDesktopWidget().availableGeometry() # ... and get this windows' dimensions mdi_area_size = self.frameGeometry() # The horizontal position hpos = (screen.width() - mdi_area_size.width()) / 2 # vertical position vpos = (screen.height() - mdi_area_size.height()) / 2 # repositions the window self.move(hpos, vpos) def _create_menu_actions(self): self._window_menu = self.menuBar().addMenu( QApplication.translate( "DocumentViewManager","&Windows")) self._close_act = QAction( QApplication.translate("DocumentViewManager", "Cl&ose"), self) self._close_act.setStatusTip( QApplication.translate("DocumentViewManager", "Close the active document viewer")) self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow) self._close_all_act = QAction(QApplication.translate( "DocumentViewManager", "Close &All"), self ) self._close_all_act.setStatusTip( QApplication.translate("DocumentViewManager", "Close all the document viewers") ) self._close_all_act.triggered.connect( self._mdi_area.closeAllSubWindows ) self._tile_act = QAction(QApplication.translate( "DocumentViewManager", "&Tile"), self ) self._tile_act.setStatusTip( QApplication.translate("DocumentViewManager", "Tile the document viewers")) self._tile_act.triggered.connect(self.tile_windows) self._cascade_act = QAction(QApplication.translate( "DocumentViewManager", "&Cascade"), self) self._cascade_act.setStatusTip(QApplication.translate( "DocumentViewManager", "Cascade the document viewers")) self._cascade_act.triggered.connect(self.cascade_windows) self._next_act = QAction(QApplication.translate( "DocumentViewManager", "Ne&xt"), self) self._next_act.setStatusTip( QApplication.translate( "DocumentViewManager", "Move the focus to the next document viewer" ) ) self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow) self._previous_act = QAction(QApplication.translate( "DocumentViewManager", "Pre&vious"), self) self._previous_act.setStatusTip( QApplication.translate( "DocumentViewManager", "Move the focus to the previous document viewer" ) ) self._previous_act.triggered.connect( self._mdi_area.activatePreviousSubWindow ) self._separator_act = QAction(self) self._separator_act.setSeparator(True) self.update_window_menu() self._window_menu.aboutToShow.connect(self.update_window_menu) def cascade_windows(self): #Cascade document windows self._mdi_area.cascadeSubWindows() def tile_windows(self): #Arrange document windows to occupy the available space in mdi area self._mdi_area.tileSubWindows() def update_actions(self): if self._mdi_area.activeSubWindow(): has_mdi_child = True else: has_mdi_child = False self._close_act.setEnabled(has_mdi_child) self._close_all_act.setEnabled(has_mdi_child) self._tile_act.setEnabled(has_mdi_child) self._cascade_act.setEnabled(has_mdi_child) self._previous_act.setEnabled(has_mdi_child) self._next_act.setEnabled(has_mdi_child) self._separator_act.setVisible(has_mdi_child) def update_window_menu(self): self._window_menu.clear() self._window_menu.addAction(self._close_act) self._window_menu.addAction(self._close_all_act) self._window_menu.addSeparator() self._window_menu.addAction(self._tile_act) self._window_menu.addAction(self._cascade_act) self._window_menu.addSeparator() self._window_menu.addAction(self._next_act) self._window_menu.addAction(self._previous_act) self._window_menu.addAction(self._separator_act) windows = self._mdi_area.subWindowList() self._separator_act.setVisible(len(windows) != 0) for i, window in enumerate(windows): text = "%d. %s" % (i + 1, window.windowTitle()) win_action = self._window_menu.addAction(text) win_action.setCheckable(True) win_action.setChecked(window is self._mdi_area.activeSubWindow()) win_action.triggered.connect(self._viewer_mapper.map) self._viewer_mapper.setMapping(win_action, window) def load_viewer(self, document_widget, visible=True): """ Open a new instance of the viewer or activate an existing one if the document had been previously loaded. :param document_widget: Contains all the necessary information required to load the specific document. :type document_widget: DocumentWidget :param visible: True to show the view manager after the viewer has been loaded, otherwise it will be the responsibility of the caller to enable visibility. :type visible: bool :returns: True if the document was successfully loaded, else False. :rtype: bool """ doc_identifier = document_widget.file_identifier() if doc_identifier in self._doc_viewers: doc_sw = self._doc_viewers[doc_identifier] self._mdi_area.setActiveSubWindow(doc_sw) doc_sw.showNormal() else: doc_viewer = self._create_viewer(document_widget) abs_doc_path = self.absolute_document_path(document_widget) if not QFile.exists(abs_doc_path): msg = QApplication.translate( "DocumentViewManager", "The selected document does not exist." "\nPlease check the supporting documents' " "repository setting." ) QMessageBox.critical( self, QApplication.translate( "DocumentViewManager","Invalid Document" ), msg ) return False doc_viewer.load_document(abs_doc_path) self._doc_viewers[doc_identifier] = doc_viewer self._mdi_area.addSubWindow(doc_viewer) doc_viewer.show() if not self.isVisible() and visible: self.setVisible(True) if self.isMinimized(): self.showNormal() self.center() return True def set_active_sub_window(self, viewer): if viewer: self._mdi_area.setActiveSubWindow(viewer) def absolute_document_path(self, document_widget): """ Build the absolute document path using info from the document widget. :param document_widget: Instance of document widget. :return: Absolute path of the supporting document. :rtype: str """ abs_path = '' file_manager = document_widget.fileManager if not file_manager is None: network_repository = file_manager.networkPath file_id = document_widget.file_identifier() source_entity = document_widget.doc_source_entity() profile_name = current_profile().name doc_type = document_widget.doc_type_value().lower().replace( ' ', '_' ) file_name, file_extension = guess_extension( document_widget.displayName() ) abs_path = network_repository + "/" +profile_name + '/' +\ unicode(source_entity) + "/" + unicode(doc_type) + "/" +\ unicode(file_id) + unicode(file_extension) return abs_path def reset(self): """ Removes all document viewers in the view area. The QCloseEvent sent to each sub-window will decrement the register. """ self._mdi_area.closeAllSubWindows() def _create_viewer(self, document_widget): """ Creates a new instance of a document viewer. :param document_widget: Contains all the necessary information required to load the specific document. :return: Document viewer object :rtype: DocumentViewer """ doc_viewer = DocumentViewer( self._mdi_area, document_widget.file_identifier() ) doc_viewer.setAttribute(Qt.WA_DeleteOnClose) doc_viewer.setWindowTitle( document_widget.displayName() ) # TODO: Incorporate logic for determining # TODO: viewer based on document type ph_viewer = PhotoViewer() # v_layout = QVBoxLayout() # v_layout.addWidget(ph_viewer) # doc_viewer.setLayout(v_layout) doc_viewer.set_view_widget(ph_viewer) doc_viewer.closed.connect(self._on_viewer_closed) return doc_viewer def remove_viewer(self, viewer_id): """ Close and remove the viewer with the specified viewer ID. """ if viewer_id in self._doc_viewers: viewer = self._doc_viewers[viewer_id] self._mdi_area.setActiveSubWindow(viewer) self._mdi_area.closeActiveSubWindow() self._on_viewer_closed(viewer_id) def _on_viewer_closed(self,file_id): """ Slot raised when a document viewer is closed. """ if file_id in self._doc_viewers: del self._doc_viewers[file_id]
class DocumentViewManager(QMainWindow): """ MDI area for displaying supporting documents within a given context e.g. supporting documents for a specific household based on the lifetime of the 'SourceDocumentManager' instance. """ def __init__(self, parent = None): QMainWindow.__init__(self, parent) self.setWindowFlags(Qt.Window) self._mdi_area = QMdiArea() self.setCentralWidget(self._mdi_area) self._mdi_area.subWindowActivated.connect(self.update_actions) self._viewer_mapper = QSignalMapper(self) self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window) win_title = QApplication.translate("DocumentViewManager","Document Viewer") self.setWindowTitle(win_title) self.setUnifiedTitleAndToolBarOnMac(True) self.statusBar().showMessage(QApplication.translate("DocumentViewManager","Ready")) self._doc_viewers = {} self._create_menu_actions() self.update_actions() def _create_menu_actions(self): self._window_menu = self.menuBar().addMenu(QApplication.translate("DocumentViewManager","&Windows")) self._close_act = QAction(QApplication.translate("DocumentViewManager", "Cl&ose"), self) self._close_act.setStatusTip(QApplication.translate("DocumentViewManager", "Close the active document viewer")) self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow) self._close_all_act = QAction(QApplication.translate("DocumentViewManager", "Close &All"), self) self._close_all_act.setStatusTip(QApplication.translate("DocumentViewManager", "Close all the document viewers")) self._close_all_act.triggered.connect(self._mdi_area.closeAllSubWindows) self._tile_act = QAction(QApplication.translate("DocumentViewManager", "&Tile"), self) self._tile_act.setStatusTip(QApplication.translate("DocumentViewManager", "Tile the document viewers")) self._tile_act.triggered.connect(self._mdi_area.tileSubWindows) self._cascade_act = QAction(QApplication.translate("DocumentViewManager", "&Cascade"), self) self._cascade_act.setStatusTip(QApplication.translate("DocumentViewManager", "Cascade the document viewers")) self._cascade_act.triggered.connect(self._mdi_area.cascadeSubWindows) self._next_act = QAction(QApplication.translate("DocumentViewManager", "Ne&xt"), self) self._next_act.setStatusTip(QApplication.translate("DocumentViewManager", "Move the focus to the next document viewer")) self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow) self._previous_act = QAction(QApplication.translate("DocumentViewManager", "Pre&vious"), self) self._previous_act.setStatusTip(QApplication.translate("DocumentViewManager", "Move the focus to the previous document viewer")) self._previous_act.triggered.connect(self._mdi_area.activatePreviousSubWindow) self._separator_act = QAction(self) self._separator_act.setSeparator(True) self.update_window_menu() self._window_menu.aboutToShow.connect(self.update_window_menu) def update_actions(self): if self._mdi_area.activeSubWindow(): has_mdi_child = True else: has_mdi_child = False self._close_act.setEnabled(has_mdi_child) self._close_all_act.setEnabled(has_mdi_child) self._tile_act.setEnabled(has_mdi_child) self._cascade_act.setEnabled(has_mdi_child) self._previous_act.setEnabled(has_mdi_child) self._next_act.setEnabled(has_mdi_child) self._separator_act.setVisible(has_mdi_child) def update_window_menu(self): self._window_menu.clear() self._window_menu.addAction(self._close_act) self._window_menu.addAction(self._close_all_act) self._window_menu.addSeparator() self._window_menu.addAction(self._tile_act) self._window_menu.addAction(self._cascade_act) self._window_menu.addSeparator() self._window_menu.addAction(self._next_act) self._window_menu.addAction(self._previous_act) self._window_menu.addAction(self._separator_act) windows = self._mdi_area.subWindowList() self._separator_act.setVisible(len(windows) != 0) for i, window in enumerate(windows): text = "%d. %s" % (i + 1, window.windowTitle()) win_action = self._window_menu.addAction(text) win_action.setCheckable(True) win_action.setChecked(window is self._mdi_area.activeSubWindow()) win_action.triggered.connect(self._viewer_mapper.map) self._viewer_mapper.setMapping(win_action, window) def load_viewer(self, document_widget): """ Open a new instance of the viewer or activate an existing one if the document had been previously loaded. :param document_widget: Contains all the necessary information required to load the specific document. """ doc_identifier = document_widget.file_identifier() if doc_identifier in self._doc_viewers: doc_sw = self._doc_viewers[doc_identifier] self._mdi_area.setActiveSubWindow(doc_sw) doc_sw.showNormal() else: doc_viewer = self._create_viewer(document_widget) abs_doc_path = self.absolute_document_path(document_widget) if not QFile.exists(abs_doc_path): msg = QApplication.translate("DocumentViewManager", "The selected document does not exist." "\nPlease check the supporting documents' " "repository setting.") QMessageBox.critical(self, QApplication.translate("DocumentViewManager","Invalid Document"), msg) return doc_viewer.load_document(abs_doc_path) self._doc_viewers[doc_identifier] = doc_viewer self._mdi_area.addSubWindow(doc_viewer) doc_viewer.show() if not self.isVisible(): self.setVisible(True) if self.isMinimized(): self.showNormal() def set_active_sub_window(self, viewer): if viewer: self._mdi_area.setActiveSubWindow(viewer) def absolute_document_path(self,document_widget): """ Build the absolute document path using info from the document widget. :param document_widget: Instance of document widget. :return: Absolute path of the supporting document. :rtype: str """ abs_path = "" file_manager = document_widget.fileManager if not file_manager is None: network_repository = file_manager.networkPath file_id = document_widget.file_identifier() doc_type = "%d"%(document_widget.documentType()) file_name, file_extension = guess_extension(document_widget.displayName()) abs_path = network_repository + "/" + doc_type + "/" +\ file_id + file_extension return abs_path def reset(self): """ Removes all document viewers in the view area. The QCloseEvent sent to each sub-window will decrement the register. """ self._mdi_area.closeAllSubWindows() def _create_viewer(self, document_widget): """ Creates a new instance of a document viewer. :param document_widget: Contains all the necessary information required to load the specific document. :return: Document viewer object :rtype: DocumentViewer """ doc_viewer = DocumentViewer(self._mdi_area, document_widget.file_identifier()) doc_viewer.setAttribute(Qt.WA_DeleteOnClose) doc_viewer.setWindowTitle(document_widget.displayName()) #TODO: Incorporate logic for determining viewer based on document type ph_viewer = PhotoViewer() doc_viewer.set_view_widget(ph_viewer) doc_viewer.closed.connect(self._on_viewer_closed) return doc_viewer def remove_viewer(self, viewer_id): """ Close and remove the viewer with the specified viewer ID. """ if viewer_id in self._doc_viewers: viewer = self._doc_viewers[viewer_id] self._mdi_area.setActiveSubWindow(viewer) self._mdi_area.closeActiveSubWindow() self._on_viewer_closed(viewer_id) def _on_viewer_closed(self,file_id): """ Slot raised when a document viewer is closed. """ if file_id in self._doc_viewers: del self._doc_viewers[file_id]
class MainWindow(QMainWindow): subWindows = [] def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle("Seabiscuit2") self.menuBar = QMenuBar() self.setMenuBar(self.menuBar) self.fileMenu = QMenu("&File") self.menuBar.addMenu(self.fileMenu) self.newWindowAction = QAction("&New Window", self) self.newWindowAction.setShortcut("Ctrl+N") self.newWindowAction.triggered.connect(self.newWindow) self.fileMenu.addAction(self.newWindowAction) self.editMenu = QMenu("&Edit") self.menuBar.addMenu(self.editMenu) self.toolsMenu = QMenu("&Tools") self.menuBar.addMenu(self.toolsMenu) self.historyAction = QAction("View &History", self) self.historyAction.setShortcut("Ctrl+H") self.historyAction.triggered.connect(self.loadHistory) self.toolsMenu.addAction(self.historyAction) self.clearHistoryAction = QAction("&Clear History", self) self.clearHistoryAction.setShortcut("Ctrl+Shift+Del") self.clearHistoryAction.triggered.connect(self.clearHistory) self.toolsMenu.addAction(self.clearHistoryAction) self.helpMenu = QMenu("&Help") self.menuBar.addMenu(self.helpMenu) self.readmeAction = QAction("View &README", self) self.readmeAction.setShortcut("F1") self.readmeAction.triggered.connect(self.loadReadme) self.helpMenu.addAction(self.readmeAction) self.aboutAction = QAction("&About Seabiscuit2", self) self.aboutAction.setShortcut("F2") self.aboutAction.triggered.connect(lambda: self.render("Seabiscuit version " + version + " (running on " + platform + ")")) self.helpMenu.addAction(self.aboutAction) self.toolBar = QToolBar() self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(self.toolBar) self.wv = QWebView() self.backAction = self.wv.pageAction(QWebPage.Back) self.backAction.setEnabled(True) self.backAction.setShortcut("Alt+Left") self.backAction.triggered.connect(lambda: self.mdiArea.currentSubWindow().widget().back()) self.toolBar.addAction(self.backAction) self.forwardAction = self.wv.pageAction(QWebPage.Forward) self.forwardAction.setEnabled(True) self.forwardAction.setShortcut("Alt+Right") self.forwardAction.triggered.connect(lambda: self.mdiArea.currentSubWindow().widget().forward()) self.toolBar.addAction(self.forwardAction) self.reloadAction = self.wv.pageAction(QWebPage.Reload) self.reloadAction.setShortcuts(["F5", "Ctrl+R"]) self.reloadAction.triggered.connect(lambda: self.mdiArea.currentSubWindow().widget().reload()) self.toolBar.addAction(self.reloadAction) self.locationBar = QLineEdit() self.locationBar.returnPressed.connect(self.loadCommand) self.toolBar.addWidget(self.locationBar) self.focusLocationBarAction = QAction(self) self.focusLocationBarAction.setShortcuts(["Ctrl+L", "Alt+D"]) self.focusLocationBarAction.triggered.connect(self.locationBar.setFocus) self.focusLocationBarAction.triggered.connect(self.locationBar.selectAll) self.addAction(self.focusLocationBarAction) self.mdiArea = QMdiArea() self.setCentralWidget(self.mdiArea) def newWindow(self): s = SWebView(self) self.subWindows.append(s) self.mdiArea.addSubWindow(s) s.activateWindow() s.show() return s def loadHistory(self): self.newWindow() self.updateWeb(os.path.join(seabiscuit_home, "history.html")) def loadReadme(self): self.newWindow() self.updateWeb("file://" + os.path.join(sys.path[0], "README.html")) def closeEvent(self, ev): confirm = yes_no_question("Query", "Are you sure you want to quit?") if confirm: ev.accept() sys.exit() else: ev.ignore() def render(self, text): QMessageBox.question(None, "Seabiscuit2 says...", text.replace("\n", "<br>")) def updateWeb(self, url=None): if url != None: self.mdiArea.currentSubWindow().widget().load(QUrl.fromUserInput(url)) def clearHistory(self): confirm = yes_no_question("Query", "Are you sure you want to clear the browser history?") if confirm: historyfile = open(os.path.join(seabiscuit_home, "history.html"), "w") historyfile.write("<html><head><script type='text/javascript'>function breakPage(url) {top.location = url}</script></head><body style=\"color: black; background: white;\"><h2>Browsing History</h2>\n") historyfile.close() def loadCommand(self): command = unicode(self.locationBar.text()) if command.lower().startswith("browser:"): command = command.replace("browser:", "") webbrowser.open(command) self.render("The page has been loaded in your default Web browser.") elif command.lower().startswith("download:"): command = command.replace("download:", "") location = "" while not os.path.exists(location): location = unicode(QInputDialog.getText("Query", "Enter a valid folder to save your file under.")) if not os.path.exists(location): render("Invalid path!") os.chdir(location) os.system("wget '" + command + "'") os.chdir(path[0]) else: self.updateWeb(command)
class Environment(QWidget): def __init__(self, port, parent=None): QWidget.__init__(self, parent=parent) self.port = port self.sharedMemory = {} memFilePath = '{}.mem'.format(port) if os.path.isfile(memFilePath): try: with open(memFilePath) as f: self.sharedMemory = pickle.load(f) sys.stdout.write(u"[INFO] Wczytano zapisaną pamięć.\n", name=port) except: sys.stdout.write(u"[BŁĄD] Błąd odczytywania pamięci. Resetuję!\n", name=port) #self.sharedMemory = SharedDict(self.sharedMemory) try: self.client = Client(port) except: #print "Brak połączenia z serwerem!" #self.client = None raise NoServerConnection() self.workerThread = None self.uiUpdateEvent = threading.Event() self.snippetQueue = [] self.context = {} self.context['mem'] = self.sharedMemory self.context['active'] = None self.context['client'] = self.client # TODO: polaczenie z klientem self.timer = QTimer() self.timer.setInterval(0) self.timer.timeout.connect(self.tick) self.timer.start() self.actions = [] self.setupUI() # Autoload modules #for modulePath in filter(lambda x: x[-3:]=='.py', os.listdir('..')): for modulePath in [ x for x in os.listdir('..') if x[-3:] == '.py' ]: self.strategies.load(os.path.join("..", modulePath)) def setupUI(self): self.setWindowTitle("%d" % self.port) self.mdi = QMdiArea() self.mdi.setHorizontalScrollBarPolicy( Qt.ScrollBarAsNeeded ) self.mdi.setVerticalScrollBarPolicy( Qt.ScrollBarAsNeeded ) self.inter = Interpreter(self.context) self.mdi.addSubWindow(self.inter, Qt.WindowTitleHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowMaximized) #self.mdi.addSubWindow(FileBrowser()) # Dock windows self.strategies = Strategies() #stratDock = QDockWidget("Strategies", self) #stratDock.setWidget(self.strategies) #self.addDockWidget(Qt.RightDockWidgetArea, stratDock) # Actions newDocIcon = QIcon(QPixmap("img/newdoc.png")) playIcon = QIcon(QPixmap("img/Play.png")) newSnippetAction = QAction(newDocIcon, "Nowy skrawek", self) newSnippetAction.setShortcut('F1') self.actions.append(newSnippetAction) runSnippetAction = QAction(playIcon, "Uruchom skrawek", self) runSnippetAction.setShortcut('F9') self.actions.append(runSnippetAction) layout = QHBoxLayout() layout.addWidget(self.mdi) layout.addWidget(self.strategies) self.setLayout(layout) # Connecting QWidget.connect(self.strategies, SIGNAL('strategyChanged'), self.strategyChanged) QWidget.connect(newSnippetAction, SIGNAL('triggered()'), self.newSnippet) QWidget.connect(runSnippetAction, SIGNAL('triggered()'), self.runSnippet) def strategyChanged(self, old, new): if old != None: with output(self.port, True): old.remove() self.workerThread.work = False self.workerThread.join() # wait for QThread self.workerThread = None if hasattr(old, '__widget__'): self.mdi.removeSubWindow(old.__widget__.parent()) self.tick() self.context['active'] = new if new: with output(self.port): print "Starting new worker..." widget = QWidget() widget.setWindowTitle(new.name()) self.mdi.addSubWindow(widget, Qt.WindowTitleHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowMaximized) new.__widget__ = widget new.mem = self.sharedMemory new.widget = widget new.client = self.client try: with output(self.port): new.install() except: traceback.print_exc() widget.parent().adjustSize() self.workerThread = WorkerThread(self.client, new, self.uiUpdateEvent) self.workerThread.daemon = True self.workerThread.start() # czyta stdout dla danego interpretera i robi update GUI def tick(self): if self.context['active'] and self.uiUpdateEvent.isSet(): self.uiUpdateEvent.clear() with output(self.port, True): self.context['active'].updateUI() data = sys.stdout.getOutput(self.port) if data: count = len( self.inter.out.toPlainText().split('\n') ) - 100 if count > 0: cursor = self.inter.out.textCursor() cursor.movePosition( QTextCursor.Start ) cursor.movePosition( QTextCursor.Down, QTextCursor.KeepAnchor, count) cursor.removeSelectedText() self.inter.write(data) def newSnippet(self): w = SnippetEditor() QWidget.connect(w, SIGNAL('codeOutput'), self.inter.write) self.mdi.addSubWindow(w) w.show() def runSnippet(self): w = self.mdi.activeSubWindow().widget() if hasattr(w, 'runCode') and callable(w.runCode): w.runCode(self.context) def queueSnippet(self): w = self.mdi.activeSubWindow().widget() if hasattr(w, 'runCode') and callable(w.runCode): self.workerThread.snippetQueue.append( (w, self.context) ) def renameSnippet(self, title): #print self.mdi.activeSubWindow().widget() if self.mdi.activeSubWindow(): self.mdi.activeSubWindow().setWindowTitle(title) #if hasattr(w, 'runCode') and callable(w.runCode): def cleanup(self): self.client.close() if self.workerThread is not None: self.workerThread.work = False
class QtMdiArea(QtConstraintsWidget, ProxyMdiArea): """ A Qt implementation of an Enaml ProxyMdiArea. """ #: A reference to the widget created by the proxy. widget = Typed(QMdiArea) #-------------------------------------------------------------------------- # Initialization API #-------------------------------------------------------------------------- def create_widget(self): """ Create the underlying QMdiArea widget. """ self.widget = QMdiArea(self.parent_widget()) def init_layout(self): """ Initialize the layout for the underlying control. """ super(QtMdiArea, self).init_layout() widget = self.widget for window in self.mdi_windows(): widget.addSubWindow(window) widget.subWindowActivated.connect(self.on_subwindow_activated) #-------------------------------------------------------------------------- # Utility Methods #-------------------------------------------------------------------------- def mdi_windows(self): """ Get the mdi windows defined for the area. """ for window in self.declaration.mdi_windows(): widget = window.proxy.widget if widget: yield widget #-------------------------------------------------------------------------- # Signal Handlers #-------------------------------------------------------------------------- def on_subwindow_activated(self, window): """ The handler for the 'subWindowActivated' signal. """ # On OSX there is painting bug where a subwindow is not repainted # properly when it is activated. This handler ensures an update. if window: window.update() #-------------------------------------------------------------------------- # Child Events #-------------------------------------------------------------------------- def child_added(self, child): """ Handle the child added event for a QtMdiArea. """ # The size hint of a QMdiArea is typically quite large and the # size hint constraints are usually ignored. There is no need # to notify of a change in size hint here. super(QtMdiArea, self).child_added(child) if isinstance(child, QtMdiWindow): self.widget.addSubWindow(child.widget) def child_removed(self, child): """ Handle the child removed event for a QtMdiArea. """ if isinstance(child, QtMdiWindow): self.widget.removeSubWindow(child.widget) else: super(QtMdiArea, self).child_removed(child)
class OrbitPlotMainWindow(QMainWindow): """ the main window has three major widgets: current, orbit tabs and element editor. """ def __init__(self, parent = None, machines=[], **kwargs): QMainWindow.__init__(self, parent) self.iqtApp = kwargs.get("iqt", None) self.setIconSize(QSize(32, 32)) self.error_bar = True self._dlgOrbitCor = None # logging self.logdock = QDockWidget("Log") self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) textedit = QPlainTextEdit(self.logdock) self.logger = logging.getLogger(__name__) self.guilogger = logging.getLogger("aphla.gui") # the "aphla" include lib part logging. When the lib is inside # QThread, logging message will be sent to TextEdit which is cross # thread. # self.guilogger = logging.getLogger("aphla") handler = QTextEditLoggingHandler(textedit) self.guilogger.addHandler(handler) self.guilogger.setLevel(logging.INFO) self.logdock.setWidget(textedit) self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea) self.logdock.setFeatures(QDockWidget.DockWidgetMovable| QDockWidget.DockWidgetClosable) self.logdock.setFloating(False) self.logdock.setMinimumHeight(20) self.logdock.setMaximumHeight(100) self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.logdock.resize(200, 60) #print self.logdock.sizeHint() self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock) #print self.logdock.sizeHint() #print self.logdock.minimumSize() #print self.logdock.maximumSize() #self.logger.info("INFO") #self.logdock.setMinimumHeight(40) #self.logdock.setMaximumHeight(160) for msg in kwargs.get("infos", []): self.logger.info(msg) # dict of (machine, (lattice dict, default_lat, pvm)) self._mach = dict([(v[0], (v[1],v[2],v[3])) for v in machines]) for m,(lats,lat0,pvm) in self._mach.items(): self.logger.info("machine '%s' initialized: [%s]" % ( m, ", ".join([lat.name for k,lat in lats.items()]))) if pvm: for pv in pvm.dead(): self.logger.warn("'{0}' is disconnected.".format(pv)) ## DCCT current plot #self.dcct = DcctCurrentPlot() #self.dcct.setMinimumHeight(100) #self.dcct.setMaximumHeight(150) #t0 = time.time() #t = np.linspace(t0 - 8*3600*24, t0, 100) #self.dcct.curve.t = t #v = 500*np.exp((t[0] - t[:50])/(4*3600*24)) #self.dcct.curve.v = v.tolist()+v.tolist() #self.dcct.updatePlot() ## MDI area self.mdiarea = QMdiArea() self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"), self.updateMachineLatticeNames) self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp) self.live_orbit = True self.setCentralWidget(self.mdiarea) #self._elemed = ElementPropertyTabs() #self.elemeditor = ElementEditorDock(parent=self) #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea) #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable| # QDockWidget.DockWidgetClosable) #self.elemeditor.setFloating(False) #self.elemeditor.setEnabled(False) #self.elemeditor.setMinimumWidth(400) #self.elemeditor.setWidget(self._elemed) #self.elemeditor.show() #self.elemeditor.hide() #self.connect(self.elemeditor, # SIGNAL("elementChecked(PyQt_PyObject, bool)"), # self.physics.elementChecked) #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor) self.createMenuToolBar() # the first machine is the default self.machBox.addItems([v for v in self._mach.keys()]) self.reloadLatticeNames(self.machBox.currentText()) self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"), self.reloadLatticeNames) # update at 1/2Hz self.dt, self.itimer = 1500, 0 #self.timerId = None self.timerId = self.startTimer(self.dt) self.vbpm = None self.statusBar().showMessage("Welcome") #self.initMachine("nsls2v2") #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x', # "H Orbit", c = None) #print "Thread started", self.machinit.isRunning() #self.newElementPlots("BPM", "x, y") #self.newElementPlot("BPM", "y") #self.newElementPlot("HCOR", "x") #self.newElementPlot("VCOR", "y") #self.newElementPlot("QUAD", "b1") #self.newElementPlot("SEXT", "b2") def updateMachineLatticeNames(self, wsub): i = self.machBox.findText(wsub.machlat[0]) self.machBox.setCurrentIndex(i) self.reloadLatticeNames(wsub.machlat[0]) def reloadLatticeNames(self, mach): self.latBox.clear() cur_mach = str(self.machBox.currentText()) lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None)) self.latBox.addItems([lat for lat in lats.keys()]) if lat0: i = self.latBox.findText(lat0.name) self.latBox.setCurrentIndex(i) def closeEvent(self, event): self.physics.close() self.mdiarea.closeAllSubWindows() event.accept() def createMenuToolBar(self): # # file menu # #self.machMenu = self.menuBar().addMenu("&Machines") #self.connect(self.machMenu, SIGNAL("aboutToShow()"), # self.updateMachMenu) self.openMenu = self.menuBar().addMenu("&Open") self.openMenu.addAction("New Plot ...", self.openNewPlot) self.openMenu.addAction("New Tune Plot", self.openTunePlot) self.openMenu.addAction("New BPM Plot", partial( self.newElementPlots, "BPM", "x,y")) self.openMenu.addAction("New HCOR Plot", partial( self.newElementPlots, "HCOR", "x")) self.openMenu.addAction("New VCOR Plot", partial( self.newElementPlots, "VCOR", "y")) self.openMenu.addSeparator() self.openMenu.addAction("Open ORM", self.loadOrm) self.openMenu.addSeparator() self.openMenu.addAction("Save Lattice ...", self.saveSnapshot) fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self) fileQuitAction.setShortcut("Ctrl+Q") fileQuitAction.setToolTip("Quit the application") fileQuitAction.setStatusTip("Quit the application") #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png")) self.connect(fileQuitAction, SIGNAL("triggered()"), self.close) self.openMenu.addAction(fileQuitAction) # view self.viewMenu = self.menuBar().addMenu("&View") mkmenu = QMenu("&Mark", self.viewMenu) for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]: famAct = QAction(fam, self) famAct.setCheckable(True) self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam) mkmenu.addAction(famAct) # # errorbar #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"), # "Errorbar", self) #viewErrorBarAction.setCheckable(True) #viewErrorBarAction.setChecked(True) #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"), # self.errorBar) # #zoomM = QMenu("Zoom", self.viewMenu) # # #drift_from_now = QAction("Drift from Now", self) #drift_from_now.setCheckable(True) #drift_from_now.setShortcut("Ctrl+N") #drift_from_golden = QAction("Drift from Golden", self) #drift_from_golden.setCheckable(True) #drift_from_none = QAction("None", self) #drift_from_none.setCheckable(True) #self.viewMenu.addAction(viewLiveAction) #self.viewMenu.addAction(viewSingleShotAction) #self.viewMenu.addSeparator() #self.viewMenu.addAction(drift_from_now) #self.viewMenu.addAction(drift_from_golden) #self.viewMenu.addAction(drift_from_none) #self.viewMenu.addAction(viewAutoScale) #self.viewMenu.addAction(viewErrorBarAction) #self.viewMenu.addSeparator() self.viewMenu.addMenu(mkmenu) #drift_group = QActionGroup(self) #drift_group.addAction(drift_from_none) #drift_group.addAction(drift_from_now) #drift_group.addAction(drift_from_golden) #drift_from_none.setChecked(True) sep = self.viewMenu.addSeparator() #sep.setText("Drift") #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow) #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone) #self.connect(drift_from_golden, SIGNAL("triggered()"), # self.setDriftGolden) #viewStyle = QMenu("Line Style", self.viewMenu) #for act in ["Increase Point Size", "Decrease Point Size", None, # "NoCurve", "Lines", "Sticks", None, # "Solid Line", "Dashed Line", "Dotted Line", None, # "Increase Line Width", "Decrease Line Width", None, # "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle", # "Cross", "XCross", "HLine", "VLine", # "Star1", "Star2", "Hexagon", None, # "Red", "Blue", "Green"]: # if act is None: # viewStyle.addSeparator() # else: # viewStyle.addAction(act, self.setPlotStyle) #self.viewMenu.addMenu(viewStyle) #self.viewMenu.addSeparator() #self.viewMenu.addAction(viewZoomOut15Action) #self.viewMenu.addAction(viewZoomIn15Action) #self.viewMenu.addAction(viewZoomAutoAction) #self.viewMenu.addSeparator() self.viewMenu.addAction("ORM SV", self.plotSVD) # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7 #self.viewMenu.addAction(viewDcct) #for ac in self.viewMenu.actions(): ac.setDisabled(True) # self.controlMenu = self.menuBar().addMenu("&Tools") self.controlMenu.addAction( QIcon(":/control_choosebpm.png"), "En-/Disable BPM", partial(chooseElement, 'BPM')) self.controlMenu.addAction( "En-/Disable COR", partial(chooseElement, 'COR')) #self.controlMenu.addAction(controlResetPvDataAction) self.controlMenu.addSeparator() self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot) #self.controlMenu.addAction(controlZoomInPlot1Action) #self.controlMenu.addAction(controlZoomOutPlot1Action) #self.controlMenu.addAction(controlZoomInPlot2Action) #self.controlMenu.addAction(controlZoomOutPlot2Action) self.controlMenu.addSeparator() self.controlMenu.addAction("Correct Hor. orbit", partial(aphla.correctOrbit, plane="H")) self.controlMenu.addAction("Correct Vert. orbit", partial(aphla.correctOrbit, plane="V")) self.controlMenu.addAction( QIcon(":/control_corrorbit.png"), "Correct orbit", partial(aphla.correctOrbit, plane="HV")) #steer_orbit.setDisabled(True) self.controlMenu.addAction("Local Bump ...", self.createLocalBump) self.controlMenu.addAction("Element Editor ...", self.showElementEditor) self.controlMenu.addSeparator() self.controlMenu.addAction("meas Beta", self.physics.measBeta) self.controlMenu.addAction("meas Dispersion", self.physics.measDispersion) self.controlMenu.addAction("beam based alignment", self.runBba) #for ac in self.controlMenu.actions(): ac.setDisabled(True) # Window self.windowMenu = self.menuBar().addMenu("&Windows") #self.windowMenu.addAction(self.elemeditor.toggleViewAction()) self.windowMenu.addAction(self.logdock.toggleViewAction()) #viewDcct = QAction("Beam Current", self) #viewDcct.setCheckable(True) #viewDcct.setChecked(True) #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible) #self.windowMenu.addAction(viewDcct) self.windowMenu.addSeparator() self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows) self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows) self.windowMenu.addAction("Tile Horizontally", self.tileSubWindowsHorizontally) # "ctrl+page up", "ctrl+page down" self.windowMenu.addAction("Previous", self.mdiarea.activatePreviousSubWindow, "Ctrl+Left") self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow, "Ctrl+Right") self.windowMenu.addSeparator() # debug self.debugMenu = self.menuBar().addMenu("&Debug") self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors) self.debugMenu.addAction("_Reset Quadrupoles_", self._reset_quadrupoles) self.debugMenu.addAction("_Random V Kick_", self._random_vkick) self.debugMenu.addAction("_Random H Kick_", self._random_hkick) #for ac in self.debugMenu.actions(): ac.setDisabled(True) # help self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("About mleap", self.showAbout) #toolbar machToolBar = self.addToolBar("Machines") self.machBox = QtGui.QComboBox() self.latBox = QtGui.QComboBox() #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"), # self.__setLattice) machToolBar.addWidget(self.machBox) machToolBar.addWidget(self.latBox) #toolbar = QToolBar(self) #self.addToolBar(toolbar) #fileToolBar = self.addToolBar("File") #fileToolBar.setObjectName("FileToolBar") #fileToolBar.addAction(fileQuitAction) # viewToolBar1 = self.addToolBar("Live View") #viewToolBar.setObjectName("ViewToolBar") #viewToolBar.addAction(viewZoomOut15Action) #viewToolBar.addAction(viewZoomIn15Action) #viewToolBar.addAction(viewZoomAutoAction) #viewToolBar1.addAction(viewLiveAction) #viewToolBar1.addAction(viewSingleShotAction) #viewToolBar1.addSeparator() viewToolBar1.addAction( QIcon(":/new_bpm.png"), "Orbits", partial(self.newElementPlots, "BPM", "x,y")) viewToolBar1.addAction( QIcon(":/new_cor.png"), "Correctors", partial(self.newElementPlots, "COR", "x,y")) viewToolBar1.addAction( QIcon(":/new_quad.png"), "Quadrupoles", partial(self.newElementPlots, "QUAD", "b1")) viewToolBar1.addAction( QIcon(":/new_sext.png"), "Sextupoles", partial(self.newElementPlots, "SEXT", "b2")) #viewToolBar.addAction(viewErrorBarAction) #viewToolBar.addAction(QWhatsThis.createAction(self)) #viewToolBar2 = self.addToolBar("Scale Plot") #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot), # (None, None, None), # (":/view_zoom_y.png", "Fit In Y", self.scalePlot), # (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot), # (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot), # (":/view_move_up.png", "Move Up", self.scalePlot), # (":/view_move_down.png", "Move Down", self.scalePlot), # (None, None, None), # (":/view_zoom_x.png", "Fit In X", self.scalePlot), # (":/view_zoomin_x.png", "Zoom In X", self.scalePlot), # (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot), # (":/view_move_left.png", "Move Left", self.scalePlot), # (":/view_move_right.png", "Move Right", self.scalePlot), # ] #for ico,name,hdl in zoomActions: # if hdl is None: continue # viewToolBar2.addAction(QIcon(ico), name, hdl) controlToolBar = self.addToolBar("Control") controlToolBar.addAction( QIcon(":/control_orbitcor.png"), "Correct Orbit", aphla.correctOrbit) controlToolBar.addAction( QIcon(":/control_localbump.png"), "Local Bump ...", self.createLocalBump) #controlToolBar.addAction(controlResetPvDataAction) def showAbout(self): QMessageBox.about( self, self.tr("mleap"), (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1 <p>Copyright © Lingyun Yang, BNL, 2013-2014. All rights reserved. <p>This application can be used to perform high level accelerator controls. <p>Python %2 - Qt %3 - PyQt %4 on %5""").arg(aphla.version.version) .arg(platform.python_version()).arg(QtCore.QT_VERSION_STR) .arg(QtCore.PYQT_VERSION_STR).arg(platform.system()))) def showElementEditor(self): mach, lat = self.getCurrentMachLattice() ed = ElementEditor(lat, parent=self) ed.setWindowFlags(Qt.Window) ed.setAttribute(Qt.WA_DeleteOnClose) ed.show() def getCurrentMachLattice(self, cadata = False): """return the current machine name and lattice object""" mach = str(self.machBox.currentText()) latname = str(self.latBox.currentText()) lat_dict, lat0, pvm = self._mach[mach] if not cadata: return mach, lat_dict[latname] else: return mach, lat_dict[latname], pvm def newElementPlots(self, elem, fields, **kw): self.logger.info("new plots: %s %s" % (elem, fields)) _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True) mach, lat = kw.get("machlat", (_mach, _lat)) handle = kw.get("handle", "readback") elems = lat.getElementList(elem) x, pvs = [], [] field_list = re.findall(r'[^ ,]+', fields) for fld in field_list: si, pvsi = [], [] for e in elems: if not e.isEnabled(): continue epv = e.pv(field=fld, handle=handle) if not epv: continue pvsi.append(epv[0]) si.append(e.sb) x.append(si) pvs.append(pvsi) if not pvs: self.logger.error("no data found for elements '{0}' " "and field '{1}'".format(elem, field)) return p = ApMdiSubPlot(pvs=pvs, x = x, labels=["%s.%s" % (elem,fld) for fld in field_list], magprof = lat.getBeamlineProfile(), iqt = self.iqtApp, **kw) #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) str_elem = "{0}".format(elem) if len(str_elem) > 12: str_elem = str_elem[:9] + "..." str_field = "{0}".format(fields) if len(str_field) > 12: str_field = str_field[:9] + "..." p.setWindowTitle("[%s.%s] %s %s" % ( mach, lat.name, str_elem, str_field)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() ##print "Enable the buttons" #if len(self.mdiarea.subWindowList()) > 0: # self.elemeditor.setEnabled(True) def subPlotDestroyed(self): #if len(self.mdiarea.subWindowList()) == 0: # self.elemeditor.setEnabled(False) pass def loadOrm(self): fileName = QtGui.QFileDialog.getOpenFileName( self, "Open Orbit Response Matrix", "", "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)") fileName = str(fileName) try: m = np.loadtxt(fileName) except: QMessageBox.critical(self, "Abort", "Invalid matrix data") return mach, lat = self.getCurrentMachLattice() # assuming we already have the PV, name, field but just want to # replace the matrix elements. assert np.shape(m) == np.shape(lat.ormdata.m) nx, ny = np.shape(lat.ormdata.m) for i in range(nx): for j in range(ny): lat.ormdata.m[i,j] = m[i,j] def saveSnapshot(self): latdict = dict([(k,v[0]) for k,v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() snapdlg = SaveSnapshotDialog(latdict, mach) snapdlg.exec_() def saveLatSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5") fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") fileName = str(fileName) if not fileName: return aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("snapshot created '%s'" % fileName) def saveMachSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5")) fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") if not fileName: return fileName = str(fileName) import h5py f = h5py.File(str(fileName), 'w') f.close() self.logger.info("clean snapshot file created: '%s'" % fileName) for k,lat in self._mach[mach][0].items(): aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("lattice snapshot appended for '%s'" % lat.name) def openSnapshot(self): #self.logger.info("loading snapshot?") latdict = dict([(k,v[0]) for k,v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() lv = LatSnapshotMain(self, latdict, mach, self.logger) lv.setWindowFlags(Qt.Window) #self.logger.info("initialized") #lv.loadLatSnapshotH5() lv.exec_() def openTunePlot(self): mach, lat = self.getCurrentMachLattice() nu = lat.getElementList('tune') pvs = [(e.pv(field="x", handle="readback")[0], e.pv(field="y", handle="readback")[0]) for e in nu] labels = [e.name for e in nu] twiss = lat.getElementList("VA") pvs.extend([(e.pv(field="nux", handle="readback")[0], e.pv(field="nuy", handle="readback")[0]) for e in twiss]) labels.extend([e.name for e in twiss]) p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype = "Tunes") #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() def openNewPlot(self): mach, lat = self.getCurrentMachLattice() fl = QtGui.QFormLayout() fl.addRow("Machine", QtGui.QLabel("%s" % mach)) fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name)) elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit() fl.addRow("Elements", elem) fl.addRow("Field", fld) dtype = QtGui.QComboBox() for tx in ["Array", "Waveform", "Time Series"]: dtype.addItem(tx) fl.addRow("Data Type", dtype) dlg = QtGui.QDialog() bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.connect(bx, SIGNAL("accepted()"), dlg.accept) self.connect(bx, SIGNAL("rejected()"), dlg.reject) h1 = QtGui.QHBoxLayout() h1.addStretch() h1.addWidget(bx) v1 = QtGui.QVBoxLayout() v1.addLayout(fl) v1.addLayout(h1) dlg.setLayout(v1) if dlg.exec_(): self.newElementPlots(str(elem.text()), str(fld.text()), machlat=(mach, lat), dtype = str(dtype.currentText())) def click_markfam(self, on): famname = self.sender().text() mks = [] mach, lat = self.getCurrentMachLattice() # need to convert to python str for elem in lat.getElementList(str(famname)): if elem.family != famname: continue if elem.virtual: continue mks.append([elem.name, 0.5*(elem.sb+elem.se)]) for w in self.mdiarea.subWindowList(): w.setMarkers(mks, on) #print self._machlat.keys() def _reset_correctors(self): self.logger.info("reset correctors") aphla.hlalib._reset_trims() def _reset_quadrupoles(self): self.logger.info("reset quadrupoles") aphla.hlalib._reset_quad() def _random_hkick(self): mach, lat = self.getCurrentMachLattice() hcors = lat.getElementList('HCOR') for k in range(3): i = np.random.randint(len(hcors)) self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors))) hcors[i].x += np.random.rand() * 2e-6 def _random_vkick(self): mach, lat = self.getCurrentMachLattice() cors = lat.getElementList('VCOR') for k in range(3): i = np.random.randint(len(cors)) cors[i].y += np.random.rand() * 1e-6 self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format( cors[i].name, cors[i].y, cors[i].getUnit('y', None))) def viewDcctPlot(self, on): self.dcct.setVisible(on) def liveData(self, on): """Switch on/off live data taking""" self.live_orbit = on def scalePlot(self): w = self.mdiarea.currentSubWindow() if not w: return st, p = self.sender().text(), w.aplot if st == "Fit": p.scaleXBottom() p.scaleYLeft() # a hack bound = p.curvesBound() p.zoomer1.setZoomStack([bound]) elif st == "Fit In Y": p.scaleYLeft() elif st == "Zoom In Y": p.scaleYLeft(1./1.5) elif st == "Zoom Out Y": p.scaleYLeft(1.5) elif st == "Move Up": p.moveCurves(Qwt.QwtPlot.yLeft, 0.8) elif st == "Move Down": p.moveCurves(Qwt.QwtPlot.yLeft, -0.8) elif st == "Fit In X": p.scaleXBottom() elif st == "Zoom In X": p.scaleXBottom(1.0/1.5) elif st == "Zoom Out X": p.scaleXBottom(1.5) elif st == "Move Left": p.moveCurves(Qwt.QwtPlot.xBottom, 0.8) elif st == "Move Right": p.moveCurves(Qwt.QwtPlot.xBottom, -0.8) else: self.logger.error("unknow action '{0}'".format(st)) def getVisibleRange(self): w = self.mdiarea.currentSubWindow() if not w: mach, lat = self.getCurrentMachLattice() self.logger.warn("no active plot, use full range of {0}.{1}".format( mach, lat.name)) return lat.getLocationRange() else: return w.currentXlim() def getVisibleElements(self, elemname, sb = None, se = None): w = self.mdiarea.currentSubWindow() mach, lat = self.getCurrentMachLattice() elems = lat.getElementList(elemname) if sb is not None: elems = [e for e in elems if e.sb >= sb] if se is not None: elems = [e for e in elems if e.se <= se] self.logger.info("searching for '{0}' in range [{1}, {2}]".format( elemname, sb, se)) return elems def timerEvent(self, e): if e.timerId() != self.timerId: return #if not self.elemeditor.isHidden(): # self.elemeditor.updateModelData() #if self.live_orbit: # self.itimer += 1 # #self.updatePlots() # #self.updateStatus() # for w in self.mdiarea.subWindowList(): # if not isinstance(w, ApMdiSubPlot): continue # if not w.live: continue # w.updatePlot() # self.statusBar().showMessage("plot updated: {0}".format( # time.strftime("%F %T"))) #else: # self.statusBar().showMessage("live update disabled") def singleShot(self): for w in self.mdiarea.subWindowList(): if not isinstance(w, ApMdiSubPlot): continue w.updatePlot() self.statusBar().showMessage("plot updated: {0}".format( time.strftime("%F %T"))) def elementSelected(self, elems): """this action is ignored""" mach, lat, elemnames = elems #_lat = self._machlat[mach][lat] self.logger.info("element selected") #elemobjs = _lat.getElementList(elemnames) #self._elemed.addElements(elemobjs) def activeOrbitPlot(self, field): mach = str(self.machBox.currentText()) lat = str(self.latBox.currentText()) for w in self.mdiarea.subWindowList(): #print w.machine(), w.lattice(), w.data.yfield if not isinstance(w, ApMdiSubPlot): continue if w.machine() != mach: continue if w.lattice() != lat: continue if w.data.yfield != field: continue return w return None def createLocalBump_(self): """create local bump""" if self._dlgOrbitCor is None: bpms = ap.getElements("BPM") cors = ap.getElements("COR") self._dlgOrbitCor = OrbitCorrDlg(bpms, cors) #corbitdlg.resize(600, 500) self._dlgOrbitCor.setWindowTitle("Create Local Bump") self._dlgOrbitCor.show() self._dlgOrbitCor.raise_() self._dlgOrbitCor.activateWindow() def createLocalBump(self): """create local bump""" bpms = ap.getElements("BPM") cors = ap.getElements("COR") dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self) #corbitdlg.resize(600, 500) dlgOrbitCor.setWindowTitle("Create Local Bump") dlgOrbitCor.show() dlgOrbitCor.raise_() dlgOrbitCor.activateWindow() dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose) def runBba(self): mach, lat = self.getCurrentMachLattice() bpms = [e for e in lat.getElementList('BPM') if e not in self.physics.deadelems] self.physics.runBba(bpms) def plotSVD(self): mach, lat = self.getCurrentMachLattice() if not lat.ormdata: QMessageBox.critical(self, "ORM SVD", "machine '%s' ORM data is not available" % \ mach, QMessageBox.Ok) return m, brec, trec = lat.ormdata.getMatrix(None, None, full=False, ignore=self.getDeadElements()) U, s, V = np.linalg.svd(m, full_matrices=True) #print np.shape(s), s self.sp = ApSvdPlot(s) self.sp.show() def tileSubWindowsHorizontally(self): pos = QtCore.QPoint(0, 0) subwins = self.mdiarea.subWindowList() for w in subwins: height = self.mdiarea.height()/len(subwins) rect = QtCore.QRect(0, 0, self.mdiarea.width(), height) w.setGeometry(rect) w.move(pos) pos.setY(pos.y() + w.height())
class App(QMainWindow): def __init__(self, parent=None): super(App, self).__init__(parent) self.resize(700, 700) self.mdi = QMdiArea(self) self.setCentralWidget(self.mdi) self.future = future(self) self.mdi.addSubWindow(self.future) self.generator = generator(self) self.generator.show() self.mdi.addSubWindow(self.generator) self.About = About(self) self.About.hide() self.mdi.addSubWindow(self.About) self.mdi.addSubWindow(Errors) exitAction = QtGui.QAction(QtGui.QIcon('generator.png'), 'generator', self) exitAction.setShortcut('Ctrl+M') exitAction.triggered.connect(self.generator.show) self.toolbar = self.addToolBar('main') self.toolbar.addAction(exitAction) exitAction = QtGui.QAction(QtGui.QIcon('future.png'), 'future', self) exitAction.setShortcut('Ctrl+F') exitAction.triggered.connect(self.future.show) self.toolbar = self.addToolBar('future') self.toolbar.addAction(exitAction) exitAction = QtGui.QAction(QtGui.QIcon('Errors.png'), 'Errors', self) exitAction.setShortcut('Ctrl+E') exitAction.triggered.connect(self.About.show) self.toolbar = self.addToolBar('Errors') self.toolbar.addAction(exitAction) exitAction = QtGui.QAction(QtGui.QIcon('About_us.png'), 'About', self) exitAction.setShortcut('Ctrl+A') exitAction.triggered.connect(self.About.show) self.toolbar = self.addToolBar('About') self.toolbar.addAction(exitAction) self.future.show() self.future.setFocus() def load(self): fname = [ str(f) for f in QFileDialog.getOpenFileNames(self, 'Open file', '.') ] #set_database(fname) #def export(self): # fname=QFileDialog.getSaveFileName(self, 'Save file', '.') def closeEvent(self, event): quit_msg = "Are you sure you want to exit the program?" event.accept() return reply = QMessageBox.question(self, 'Message', quit_msg, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore()