예제 #1
0
 def setupUI(self):
     
     self.browser = browser = SimpleServerBrowser()
     self.player = player = video.Player() #VideoPlayer()        
     
     dock = QDockWidget(u'Przeglądarka serwerów', self)
     dock.setContentsMargins(0, 0, 0, 0)
     
     dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
     dock.setWidget(browser)        
     
     self.addDockWidget(Qt.LeftDockWidgetArea, dock)        
             
     #self.setCentralWidget(player.videoWidget())
     self.setCentralWidget(player.videoWidget)
     
     dock = QDockWidget(self)
     dock.setAllowedAreas(Qt.BottomDockWidgetArea)
     dock.setWidget(player.controlPanel)
     
     dock.setFloating(False)
     dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
     self.addDockWidget(Qt.BottomDockWidgetArea, dock)
 
     QWidget.connect(self.browser, SIGNAL("play"), self.play)
     self.resize(800, 480)
예제 #2
0
파일: mleap.py 프로젝트: ChannelFinder/hla
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 &copy; 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())
예제 #3
0
class MenuBuilderDialog(QDialog, Ui_Dialog):
    def __init__(self, uiparent):
        super(MenuBuilderDialog, self).__init__()

        self.setupUi(self)

        # reference to caller
        self.uiparent = uiparent

        self.combo_profile.lineEdit().setPlaceholderText(
            self.tr("Profile name"))

        # add icons
        self.button_add_menu.setIcon(
            QIcon(":/plugins/MenuBuilder/resources/plus.svg"))
        self.button_delete_profile.setIcon(
            QIcon(":/plugins/MenuBuilder/resources/delete.svg"))

        # custom qtreeview
        self.target = CustomQtTreeView(self)
        self.target.setGeometry(QRect(440, 150, 371, 451))
        self.target.setAcceptDrops(True)
        self.target.setDragEnabled(True)
        self.target.setDragDropMode(QAbstractItemView.DragDrop)
        self.target.setObjectName("target")
        self.target.setDropIndicatorShown(True)
        self.target.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.target.setHeaderHidden(True)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.target.sizePolicy().hasHeightForWidth())
        self.target.setSizePolicy(sizePolicy)
        self.target.setAutoFillBackground(True)
        self.verticalLayout_2.addWidget(self.target)

        self.browser = QgsBrowserModel()
        self.source.setModel(self.browser)
        self.source.setHeaderHidden(True)
        self.source.setDragEnabled(True)
        self.source.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.menumodel = MenuTreeModel(self)
        self.target.setModel(self.menumodel)
        self.target.setAnimated(True)

        # add a dock widget
        self.dock_widget = QDockWidget("Menus")
        self.dock_widget.resize(400, 300)
        self.dock_widget.setFloating(True)
        self.dock_widget.setObjectName(self.tr("Menu Tree"))
        self.dock_widget_content = QWidget()
        self.dock_widget.setWidget(self.dock_widget_content)
        dock_layout = QVBoxLayout()
        self.dock_widget_content.setLayout(dock_layout)
        self.dock_view = DockQtTreeView(self.dock_widget_content)
        self.dock_view.setDragDropMode(QAbstractItemView.DragOnly)
        self.dock_menu_filter = QLineEdit()
        self.dock_menu_filter.setPlaceholderText(
            self.tr("Filter by table description (postgis only)"))
        dock_layout.addWidget(self.dock_menu_filter)
        dock_layout.addWidget(self.dock_view)
        self.dock_view.setHeaderHidden(True)
        self.dock_view.setDragEnabled(True)
        self.dock_view.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.dock_view.setAnimated(True)
        self.dock_view.setObjectName("treeView")
        self.proxy_model = LeafFilterProxyModel(self)
        self.proxy_model.setFilterRole(Qt.ToolTipRole)
        self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)

        self.profile_list = []
        self.table = 'qgis_menubuilder_metadata'

        self.layer_handler = {
            'vector': self.load_vector,
            'raster': self.load_raster
        }

        # connect signals and handlers
        self.combo_database.activated.connect(
            partial(self.set_connection, dbname=None))
        self.combo_schema.activated.connect(self.update_profile_list)
        self.combo_profile.activated.connect(
            partial(self.update_model_idx, self.menumodel))
        self.button_add_menu.released.connect(self.add_menu)
        self.button_delete_profile.released.connect(self.delete_profile)
        self.dock_menu_filter.textEdited.connect(self.filter_update)
        self.dock_view.doubleClicked.connect(self.load_from_index)

        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(
            self.apply)

    def filter_update(self):
        text = self.dock_menu_filter.displayText()
        self.proxy_model.setFilterRegExp(text)

    def show_dock(self, state, profile=None, schema=None):
        if not state:
            # just hide widget
            self.dock_widget.setVisible(state)
            return
        # dock must be read only and deepcopy of model is not supported (c++ inside!)
        self.dock_model = MenuTreeModel(self)
        if profile:
            # bypass combobox
            self.update_model(self.dock_model, schema, profile)
        else:
            self.update_model_idx(self.dock_model,
                                  self.combo_profile.currentIndex())
        self.dock_model.setHorizontalHeaderLabels(["Menus"])
        self.dock_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.proxy_model.setSourceModel(self.dock_model)
        self.dock_view.setModel(self.proxy_model)
        self.dock_widget.setVisible(state)

    def show_menus(self, state, profile=None, schema=None):
        if state:
            self.load_menus(profile=profile, schema=schema)
            return
        # remove menus
        for menu in self.uiparent.menus:
            self.uiparent.iface.mainWindow().menuBar().removeAction(
                menu.menuAction())

    def add_menu(self):
        """
        Add a menu inside qtreeview
        """
        item = QStandardItem('NewMenu')
        item.setIcon(QIcon(':/plugins/MenuBuilder/resources/menu.svg'))
        # select current index selected and insert as a sibling
        brother = self.target.selectedIndexes()

        if not brother or not brother[0].parent():
            # no selection, add menu at the top level
            self.menumodel.insertRow(self.menumodel.rowCount(), item)
            return

        parent = self.menumodel.itemFromIndex(brother[0].parent())
        if not parent:
            self.menumodel.insertRow(self.menumodel.rowCount(), item)
            return
        parent.appendRow(item)

    def update_database_list(self):
        """update list of defined postgres connections"""
        settings = QSettings()
        settings.beginGroup("/PostgreSQL/connections")
        keys = settings.childGroups()
        self.combo_database.clear()
        self.combo_schema.clear()
        self.menumodel.clear()
        self.combo_database.addItems(keys)
        self.combo_database.setCurrentIndex(-1)
        settings.endGroup()
        # clear profile list
        self.combo_profile.clear()
        self.combo_profile.setCurrentIndex(-1)

    def set_connection(self, databaseidx, dbname=None):
        """
        Connect to selected postgresql database
        """
        selected = self.combo_database.itemText(databaseidx) or dbname
        if not selected:
            return

        settings = QSettings()
        settings.beginGroup("/PostgreSQL/connections/{}".format(selected))

        if not settings.contains("database"):
            # no entry?
            QMessageBox.critical(self, "Error",
                                 "There is no defined database connection")
            return

        uri = QgsDataSourceURI()

        settingsList = [
            "service", "host", "port", "database", "username", "password"
        ]
        service, host, port, database, username, password = map(
            lambda x: settings.value(x, "", type=str), settingsList)

        useEstimatedMetadata = settings.value("estimatedMetadata",
                                              False,
                                              type=bool)
        sslmode = settings.value("sslmode",
                                 QgsDataSourceURI.SSLprefer,
                                 type=int)

        settings.endGroup()

        if service:
            uri.setConnection(service, database, username, password, sslmode)
        else:
            uri.setConnection(host, port, database, username, password,
                              sslmode)

        uri.setUseEstimatedMetadata(useEstimatedMetadata)

        # connect to db
        self.connect_to_uri(uri)
        # update schema list
        self.update_schema_list()

    @contextmanager
    def transaction(self):
        try:
            yield
            self.connection.commit()
        except self.pg_error_types() as e:
            self.connection.rollback()
            raise e

    def check_connected(func):
        """
        Decorator that checks if a database connection is active before executing function
        """
        @wraps(func)
        def wrapped(inst, *args, **kwargs):
            if not getattr(inst, 'connection', False):
                QMessageBox(
                    QMessageBox.Warning, "Menu Builder",
                    inst.tr(
                        "Not connected to any database, please select one"),
                    QMessageBox.Ok, inst).exec_()
                return
            if inst.connection.closed:
                QMessageBox(
                    QMessageBox.Warning, "Menu Builder",
                    inst.tr(
                        "Not connected to any database, please select one"),
                    QMessageBox.Ok, inst).exec_()
                return
            return func(inst, *args, **kwargs)

        return wrapped

    def connect_to_uri(self, uri):
        self.close_connection()
        self.host = uri.host() or os.environ.get('PGHOST')
        self.port = uri.port() or os.environ.get('PGPORT')

        username = uri.username() or os.environ.get(
            'PGUSER') or os.environ.get('USER')
        password = uri.password() or os.environ.get('PGPASSWORD')

        try:
            self.connection = psycopg2.connect(uri.connectionInfo())
        except self.pg_error_types() as e:
            err = str(e)
            conninfo = uri.connectionInfo()

            ok, username, password = QgsCredentials.instance().get(
                conninfo, username, password, err)
            if not ok:
                raise Exception(e)

            if username:
                uri.setUsername(username)

            if password:
                uri.setPassword(password)

            self.connection = psycopg2.connect(uri.connectionInfo())

        self.pgencoding = self.connection.encoding

        return True

    def pg_error_types(self):
        return (psycopg2.InterfaceError, psycopg2.OperationalError,
                psycopg2.ProgrammingError)

    @check_connected
    def update_schema_list(self):
        self.combo_schema.clear()
        with self.transaction():
            cur = self.connection.cursor()
            cur.execute("""
                select nspname
                from pg_namespace
                where nspname not ilike 'pg_%'
                and nspname not in ('pg_catalog', 'information_schema')
                """)
            schemas = [row[0] for row in cur.fetchall()]
            self.combo_schema.addItems(schemas)

    @check_connected
    def update_profile_list(self, schemaidx):
        """
        update profile list from database
        """
        schema = self.combo_schema.itemText(schemaidx)
        with self.transaction():
            cur = self.connection.cursor()
            cur.execute("""
                select 1
                from pg_tables
                    where schemaname = '{0}'
                    and tablename = '{1}'
                union
                select 1
                from pg_matviews
                    where schemaname = '{0}'
                    and matviewname = '{1}'
                """.format(schema, self.table))
            tables = cur.fetchone()
            if not tables:
                box = QMessageBox(
                    QMessageBox.Warning, "Menu Builder",
                    self.tr("Table '{}.{}' not found in this database, "
                            "would you like to create it now ?").format(
                                schema, self.table),
                    QMessageBox.Cancel | QMessageBox.Yes, self)
                ret = box.exec_()
                if ret == QMessageBox.Cancel:
                    return False
                elif ret == QMessageBox.Yes:
                    cur.execute("""
                        create table {}.{} (
                            id serial,
                            name varchar,
                            profile varchar,
                            model_index varchar,
                            datasource_uri text
                        )
                        """.format(schema, self.table))
                    self.connection.commit()
                    return False

            cur.execute("""
                select distinct(profile) from {}.{}
                """.format(schema, self.table))
            profiles = [row[0] for row in cur.fetchall()]
            saved_profile = self.combo_profile.currentText()
            self.combo_profile.clear()
            self.combo_profile.addItems(profiles)
            self.combo_profile.setCurrentIndex(
                self.combo_profile.findText(saved_profile))

    @check_connected
    def delete_profile(self):
        """
        Delete profile currently selected
        """
        idx = self.combo_profile.currentIndex()
        schema = self.combo_schema.currentText()
        profile = self.combo_profile.itemText(idx)
        box = QMessageBox(QMessageBox.Warning, "Menu Builder",
                          self.tr("Delete '{}' profile ?").format(profile),
                          QMessageBox.Cancel | QMessageBox.Yes, self)
        ret = box.exec_()
        if ret == QMessageBox.Cancel:
            return False
        elif ret == QMessageBox.Yes:
            self.combo_profile.removeItem(idx)
            with self.transaction():
                cur = self.connection.cursor()
                cur.execute("""
                    delete from {}.{}
                    where profile = '{}'
                    """.format(schema, self.table, profile))
        self.menumodel.clear()
        self.combo_profile.setCurrentIndex(-1)

    def update_model_idx(self, model, profile_index):
        """
        wrapper that checks combobox
        """
        profile = self.combo_profile.itemText(profile_index)
        schema = self.combo_schema.currentText()
        self.update_model(model, schema, profile)

    def sortby_modelindex(self, rows):
        return sorted(
            rows,
            key=lambda line: '/'.join(
                ['{:04}'.format(elem[0]) for elem in json.loads(line[2])]))

    @check_connected
    def update_model(self, model, schema, profile):
        """
        Update the model by retrieving the profile given in database
        """
        menudict = {}

        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select name, profile, model_index, datasource_uri
                from {}.{}
                where profile = '{}'
                """.format(schema, self.table, profile)
            cur.execute(select)
            rows = cur.fetchall()
            model.clear()
            for name, profile, model_index, datasource_uri in self.sortby_modelindex(
                    rows):
                menu = model.invisibleRootItem()
                indexes = json.loads(model_index)
                parent = ''
                for idx, subname in indexes[:-1]:
                    parent += '{}-{}/'.format(idx, subname)
                    if parent in menudict:
                        # already created entry
                        menu = menudict[parent]
                        continue
                    # create menu
                    item = QStandardItem(subname)
                    uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
                    item.setData(uri_struct)
                    item.setIcon(
                        QIcon(':/plugins/MenuBuilder/resources/menu.svg'))
                    item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
                                  | Qt.ItemIsEnabled | Qt.ItemIsDropEnabled
                                  | Qt.ItemIsEditable)
                    item.setWhatsThis("menu")
                    menu.appendRow(item)
                    menudict[parent] = item
                    # set current menu to the new created item
                    menu = item

                # add leaf (layer item)
                item = QStandardItem(name)
                uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
                # fix layer name instead of table name
                # usefull when the layer has been renamed in menu
                uri_struct.name = name
                if uri_struct.providerKey in ICON_MAPPER:
                    item.setIcon(QIcon(ICON_MAPPER[uri_struct.providerKey]))
                item.setData(uri_struct)
                # avoid placing dragged layers on it
                item.setDropEnabled(False)
                if uri_struct.providerKey == 'postgres':
                    # set tooltip to postgres comment
                    comment = self.get_table_comment(uri_struct.uri)
                    item.setToolTip(comment)
                menudict[parent].appendRow(item)

    @check_connected
    def save_changes(self, save_to_db=True):
        """
        Save changes in the postgres table
        """
        schema = self.combo_schema.currentText()
        profile = self.combo_profile.currentText()
        if not profile:
            QMessageBox(QMessageBox.Warning, "Menu Builder",
                        self.tr("Profile cannot be empty"), QMessageBox.Ok,
                        self).exec_()
            return False

        if save_to_db:
            try:
                with self.transaction():
                    cur = self.connection.cursor()
                    cur.execute(
                        "delete from {}.{} where profile = '{}'".format(
                            schema, self.table, profile))
                    for item, data in self.target.iteritems():
                        if not data:
                            continue
                        cur.execute(
                            """
                        insert into {}.{} (name,profile,model_index,datasource_uri)
                        values (%s, %s, %s, %s)
                        """.format(schema, self.table),
                            (item[-1][1], profile, json.dumps(item),
                             data.data()))
            except Exception as exc:
                QMessageBox(QMessageBox.Warning, "Menu Builder",
                            exc.message.decode(self.pgencoding),
                            QMessageBox.Ok, self).exec_()
                return False

        self.save_session(self.combo_database.currentText(), schema, profile,
                          self.activate_dock.isChecked(),
                          self.activate_menubar.isChecked())
        self.update_profile_list(self.combo_schema.currentIndex())
        self.show_dock(self.activate_dock.isChecked())
        self.show_menus(self.activate_menubar.isChecked())
        return True

    @check_connected
    def load_menus(self, profile=None, schema=None):
        """
        Load menus in the main windows qgis bar
        """
        if not schema:
            schema = self.combo_schema.currentText()
        if not profile:
            profile = self.combo_profile.currentText()
        # remove previous menus
        for menu in self.uiparent.menus:
            self.uiparent.iface.mainWindow().menuBar().removeAction(
                menu.menuAction())

        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select name, profile, model_index, datasource_uri
                from {}.{}
                where profile = '{}'
                """.format(schema, self.table, profile)
            cur.execute(select)
            rows = cur.fetchall()
        # item accessor ex: '0-menu/0-submenu/1-item/'
        menudict = {}
        # reference to parent item
        parent = ''
        # reference to qgis main menu bar
        menubar = self.uiparent.iface.mainWindow().menuBar()

        for name, profile, model_index, datasource_uri in self.sortby_modelindex(
                rows):
            uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
            indexes = json.loads(model_index)
            # root menu
            parent = '{}-{}/'.format(indexes[0][0], indexes[0][1])
            if parent not in menudict:
                menu = QMenu(self.uiparent.iface.mainWindow())
                self.uiparent.menus.append(menu)
                menu.setObjectName(indexes[0][1])
                menu.setTitle(indexes[0][1])
                menubar.insertMenu(
                    self.uiparent.iface.firstRightStandardMenu().menuAction(),
                    menu)
                menudict[parent] = menu
            else:
                # menu already there
                menu = menudict[parent]

            for idx, subname in indexes[1:-1]:
                # intermediate submenus
                parent += '{}-{}/'.format(idx, subname)
                if parent not in menudict:
                    submenu = menu.addMenu(subname)
                    submenu.setObjectName(subname)
                    submenu.setTitle(subname)
                    menu = submenu
                    # store it for later use
                    menudict[parent] = menu
                    continue
                # already treated
                menu = menudict[parent]

            # last item = layer
            layer = QAction(name, self.uiparent.iface.mainWindow())

            if uri_struct.providerKey in ICON_MAPPER:
                layer.setIcon(QIcon(ICON_MAPPER[uri_struct.providerKey]))

            if uri_struct.providerKey == 'postgres':
                # set tooltip to postgres comment
                comment = self.get_table_comment(uri_struct.uri)
                layer.setStatusTip(comment)
                layer.setToolTip(comment)

            layer.setData(uri_struct.uri)
            layer.setWhatsThis(uri_struct.providerKey)
            layer.triggered.connect(self.layer_handler[uri_struct.layerType])
            menu.addAction(layer)

    def get_table_comment(self, uri):
        schema, table = re.match('.*table=(.*)\(.*',
                                 uri).group(1).strip().replace('"',
                                                               '').split('.')
        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select description from pg_description
                join pg_class on pg_description.objoid = pg_class.oid
                join pg_namespace on pg_class.relnamespace = pg_namespace.oid
                where relname = '{}' and nspname='{}'
                """.format(table, schema)
            cur.execute(select)
            row = cur.fetchone()
            if row:
                return row[0]
        return ''

    def load_from_index(self, index):
        """Load layers from selected item index"""
        item = self.dock_model.itemFromIndex(
            self.proxy_model.mapToSource(index))
        if item.whatsThis() == 'menu':
            return
        if item.data().layerType == 'vector':
            layer = QgsVectorLayer(
                item.data().uri,  # uri
                item.text(),  # layer name
                item.data().providerKey  # provider name
            )
        elif item.data().layerType == 'raster':
            layer = QgsRasterLayer(
                item.data().uri,  # uri
                item.text(),  # layer name
                item.data().providerKey  # provider name
            )
        if not layer:
            return
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def load_vector(self):
        action = self.sender()
        layer = QgsVectorLayer(
            action.data(),  # uri
            action.text(),  # layer name
            action.whatsThis()  # provider name
        )
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def load_raster(self):
        action = self.sender()
        layer = QgsRasterLayer(
            action.data(),  # uri
            action.text(),  # layer name
            action.whatsThis()  # provider name
        )
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def accept(self):
        if self.save_changes():
            QDialog.reject(self)
            self.close_connection()

    def apply(self):
        if self.save_changes(save_to_db=False):
            QDialog.reject(self)

    def reject(self):
        self.close_connection()
        QDialog.reject(self)

    def close_connection(self):
        """close current pg connection if exists"""
        if getattr(self, 'connection', False):
            if self.connection.closed:
                return
            self.connection.close()

    def save_session(self, database, schema, profile, dock, menubar):
        """save current profile for next session"""
        settings = QSettings()
        settings.setValue("MenuBuilder/database", database)
        settings.setValue("MenuBuilder/schema", schema)
        settings.setValue("MenuBuilder/profile", profile)
        settings.setValue("MenuBuilder/dock", dock)
        settings.setValue("MenuBuilder/menubar", menubar)

    def restore_session(self):
        settings = QSettings()
        database = settings.value("MenuBuilder/database", False)
        schema = settings.value("MenuBuilder/schema", 'public')
        profile = settings.value("MenuBuilder/profile", False)
        dock = settings.value("MenuBuilder/dock", False)
        menubar = settings.value("MenuBuilder/menubar", False)
        if not any([database, profile]):
            return

        self.set_connection(0, dbname=database)
        self.show_dock(bool(dock), profile=profile, schema=schema)
        if bool(dock):
            self.uiparent.iface.addDockWidget(Qt.LeftDockWidgetArea,
                                              self.dock_widget)
        self.show_menus(bool(menubar), profile=profile, schema=schema)
예제 #4
0
class CreateProjectWidget(QWidget):
    def __init__(self, parent=None):

        super(CreateProjectWidget, self).__init__(parent)

        # load the user interface# load the user interface
        if getattr(sys, 'frozen', None):
            #print (sys._MEIPASS+"/ui/createprojectwidget.ui");
            projexui.loadUi(sys._MEIPASS,
                            self,
                            uifile=(sys._MEIPASS +
                                    "/ui/createprojectwidget.ui"))

        else:
            projexui.loadUi(__file__, self)
        #projexui.loadUi(__file__, self)

        # define custom properties

        self.dockWidget = QDockWidget()
        parent.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dockWidget)
        self.dockWidget.setFloating(1)
        self.dockWidget.setWindowTitle("Create Project")
        self.dockWidget.setWidget(self)

        #self.setWindowFlags(QtCore.Qt.Tool)

        self._backend = None

        #self.myClientNameLineEdit = clientIPLineEdit.ClientIPLineEdit(self)
        #self.clientIPBoxLayout.addWidget(self.myClientNameLineEdit)

        #connects buttons
        self.createButton.clicked.connect(self.CreateProject)
        self.cancelButton.clicked.connect(self.cancel)
        self.UpdateClientList()
        self.clientComboBox.currentIndexChanged.connect(self.UpdateIPList)

        self.UpdateIPList(self.clientComboBox.currentIndex())

        #self.open()

    def UpdateClientList(self):
        self.clientComboBox.clear()
        for client in sharedDB.myClients.values():
            self.clientComboBox.addItem(client._name,
                                        QtCore.QVariant(client._idclients))

    def UpdateIPList(self, sentclientcmboboxindex):
        self.ipComboBox.clear()
        #print sharedDB.myClients[str(self.clientComboBox.itemData(self.clientComboBox.currentIndex()).toString())]
        if str(
                self.clientComboBox.itemData(self.clientComboBox.currentIndex(
                )).toString()) in sharedDB.myClients:
            client = sharedDB.myClients[str(
                self.clientComboBox.itemData(
                    self.clientComboBox.currentIndex()).toString())]
            #if str(client._idclients) == self.clientComboBox.itemData(sentclientid).toString():
            for ip in client._ips.values():
                self.ipComboBox.addItem(ip._name, QtCore.QVariant(ip._idips))

    def cancel(self):
        self.close()

    def setDefaults(self):
        #set initial values
        self.duedateEdit.setDate(QDate.currentDate().addDays(30))
        self.xres_spinBox.setValue(1280)
        self.yres_spinBox.setValue(720)

        self.phaseListWidget.clear()

        for myPhase in sharedDB.myPhases.values():
            if myPhase._name != "DUE":
                item = QtGui.QListWidgetItem(myPhase._name)
                self.phaseListWidget.addItem(item)
                self.phaseListWidget.setItemSelected(item, True)

        self.descriptionTextEdit.setText("")
        self.durationEdit.setTime(QTime.fromString("00:01:00"))
        self.projectNameQLineEdit.setText("Project Name")
        self.fps.setValue(25)

    '''def open(self):
	self.show()
	self.activateWindow()
    '''

    def CreateProject(self):
        name = str(self.projectNameQLineEdit.text())
        folderLocation = ''
        #idstatus = 0
        idips = str(
            self.ipComboBox.itemData(
                self.ipComboBox.currentIndex()).toString())
        idclients = str(
            self.clientComboBox.itemData(
                self.clientComboBox.currentIndex()).toString())
        fps = self.fps.value()
        renderWidth = self.xres_spinBox.value()
        renderHeight = self.yres_spinBox.value()
        due_date = self.duedateEdit.date().toPyDate()
        renderPriority = 50
        description = self.descriptionTextEdit.toPlainText()
        phases = []

        #for each list item
        for item in self.phaseListWidget.selectedItems():
            #get phase of same name
            for x in range(0, len(sharedDB.myPhases))[::-1]:
                #print sharedDB.myPhases[x]._name
                #print item.text
                if sharedDB.myPhases.values()[x]._name == item.text():
                    phases.append(
                        sharedDB.phaseAssignments.PhaseAssignments(
                            _idphases=sharedDB.myPhases.values()[x].id(),
                            _startdate=due_date,
                            _enddate=due_date,
                            _updated=0))
                    continue
            #start from due date and work backwards
            #for

        phases = InitializeDates(phases, due_date, self.durationEdit.time())

        #Add due date into phases
        phases.append(
            sharedDB.phaseAssignments.PhaseAssignments(_idphases=16,
                                                       _startdate=due_date,
                                                       _enddate=due_date,
                                                       _updated=0))

        newProj = sharedDB.projects.Projects(_name=name,
                                             _folderLocation='',
                                             _idstatuses=1,
                                             _fps=fps,
                                             _renderWidth=renderWidth,
                                             _renderHeight=renderHeight,
                                             _due_date=due_date,
                                             _renderPriority=renderPriority,
                                             _description=description,
                                             _idips=idips,
                                             _idclients=idclients,
                                             _new=0)

        newProj._new = 1
        newProj.Save()
        #sharedDB.myProjects.append(newProj)

        #connect phases to projectid

        for phase in phases:
            newProj.AddPhase(phase)
            '''
	    phase._idprojects = newProj._idprojects
	    phase.project = newProj
	    phase._new = 1
	    phase.Save()
	    newProj._phases[str(phase.id())] = phase
	    '''

        if str(idips) in sharedDB.myIps:
            ip = sharedDB.myIps[str(idips)]
            ip._projects[str(newProj.id())] = newProj

        newProj.UpdateStartDate()

        self.close()
예제 #5
0
class WeekliesWidget(QWidget):
   
    def __init__( self, parent = None ):
        
        super(WeekliesWidget, self).__init__( parent )
        
        self.dockWidget = QDockWidget()
        parent.addDockWidget(QtCore.Qt.RightDockWidgetArea,self.dockWidget)
        self.dockWidget.setFloating(1)
        self.dockWidget.setWindowTitle("Weeklies")
        self.dockWidget.setWidget(self)
        
        mainlay = QtGui.QVBoxLayout()
        self.setLayout(mainlay)
        topLayout = QtGui.QHBoxLayout()
        mainlay.addLayout(topLayout)
        
        self.dateEdit = QtGui.QDateEdit(date.today())
        self.dateEdit.setCalendarPopup(True)
        self.dateEdit.dateChanged.connect(self.CalculateWeeklies)
        topLayout.addWidget(self.dateEdit)
        
        self.textBox = QtGui.QTextEdit()
        self.textDocument = QtGui.QTextDocument()
        self.textBox.setDocument(self.textDocument)
        
        mainlay.addWidget(self.textBox)       
        
                
        
        #self.setWindowFlags(QtCore.Qt.Tool)
        
    def CalculateWeeklies(self):
        
        day = self.dateEdit.date().toPyDate()

        #dt = datetime.strptime(day, '%d/%b/%Y')
        
        mon = day - timedelta(days=day.weekday())
        
        fri = mon + timedelta(days=4)
        
        departmentDict = {}
        phaseDict = {}
        projList = []
        text = "<b> **PROJECTS** </b>"
        
        #iterate through projects and add to project list
        for proj in sharedDB.myProjects.values():
            if proj._startdate <= fri:
                if int(proj._idstatuses) < 4 or int(proj._idstatuses) == 7:
                    projList.append(proj)
                    
        projList.sort(key=operator.attrgetter('_startdate'))
        
        for proj in projList:
            text += "<br><u>"+proj._name+"</u> - "
            stat = proj._statusDescription
            if stat is not None and len(stat):
                text+=stat
            else:
                text+=sharedDB.myStatuses[str(proj._idstatuses)]._name
        
        for phase in sharedDB.myPhaseAssignments.values():
        #    if start before saturday and end after sunday
            proj = sharedDB.myProjects[str(phase._idprojects)]
            if phase._startdate <= fri and int(phase._idstatuses) != 5 and int(phase._idstatuses) != 6 and not 4 <= proj._idstatuses <= 6:
                
                if ((phase._enddate >= mon) and int(phase._idstatuses) != 4) or int(phase._idstatuses) != 4:
                    departmentID = str(sharedDB.myPhases[str(phase._idphases)]._iddepartments)
                    if departmentID in departmentDict:
                        departmentDict[departmentID].append(phase)
                    else:
                       departmentDict[departmentID] = [phase]
                       
                    phaseID = str(phase._idphases)
                    if phaseID in phaseDict:
                        phaseDict[phaseID].append(phase)
                    else:
                        phaseDict[phaseID] = [phase]
                    
                
        #phaselist.sort(key=operator.attrgetter('_startdate'))
        
        text += "<br><br>"
        
        for key in phaseDict.keys():
            text += "<b>"+sharedDB.myPhases[key]._name+"</b>\n"
            phaselist = phaseDict[key]
            phaselist.sort(key=operator.attrgetter('_startdate'))
            for phase in phaselist:
                
                dateInfo = ''
                
                #starting
                if phase._startdate >= mon:
                    #starting and ending this week
                    if phase._enddate <= fri:
                        dateInfo = "<font color=\"green\">"+phase._startdate.strftime("%m/%d")+'</font> - <font color=\"red\">'+phase._enddate.strftime("%m/%d")+'</font> - '
                    else:
                        dateInfo = "<font color=\"green\">"+phase._startdate.strftime("%m/%d")+' - START</font> - '
                #due
                elif phase._enddate <= fri:
                    dateInfo = "<font color=\"red\">"+phase._enddate.strftime("%m/%d")+' - DUE</font> - '
                #continuing
                else:
                    dateInfo = 'Continuing through week... - '
                
                #users
                defaultUserInfo = '<font color=\"blue\"> - UNASSIGNED</font>' 
                userInfo =  defaultUserInfo              
                for ua in phase._userAssignments.values():
                    if ua._hours > 1:
                        if userInfo == defaultUserInfo:
                            userInfo = ' - '+sharedDB.myUsers[str(ua._idusers)]._name
                        else:
                            userInfo += ", "+sharedDB.myUsers[str(ua._idusers)]._name
                
                pname = sharedDB.myPhases[str(phase._idphases)]._name
                if  pname in ["Approval","DUE","Delivery","Internal Review","Rendering"]:
                    userInfo = ""
                
                #if phase or project on hold
                startTag = "<tab>"
                endTag = "</tab>"
                if phase._idstatuses == 3 or proj._idstatuses == 3:
                    startTag += "<s>"
                    endTag += "</s>"
                    startTag += "**ON HOLD** "
                    
                if phase._idstatuses == 4:
                    startTag += "<s>"
                    endTag += "</s>"
                
                if phase._description is not None and phase._description != "None":
                    additionalInfo = " - "+phase._description
                else:
                    additionalInfo = ""
                    
                text += "<br>"+startTag+dateInfo+"<u>"+sharedDB.myProjects[str(phase._idprojects)]._name+"</u>"+userInfo+additionalInfo+endTag+"\n"
            text += "<br><br>"
        self.textDocument.setHtml(text)
예제 #6
0
class WeekliesWidget(QWidget):
    def __init__(self, parent=None):

        super(WeekliesWidget, self).__init__(parent)

        self.dockWidget = QDockWidget()
        parent.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dockWidget)
        self.dockWidget.setFloating(1)
        self.dockWidget.setWindowTitle("Weeklies")
        self.dockWidget.setWidget(self)

        mainlay = QtGui.QVBoxLayout()
        self.setLayout(mainlay)
        topLayout = QtGui.QHBoxLayout()
        mainlay.addLayout(topLayout)

        self.dateEdit = QtGui.QDateEdit(date.today())
        self.dateEdit.setCalendarPopup(True)
        #self.dateEdit.dateChanged.connect(self.CalculateWeeklies)
        self.dateEdit.dateChanged.connect(self.PopulateTable)
        topLayout.addWidget(self.dateEdit)

        self.userDropdown = QtGui.QComboBox()
        self.userDropdown.currentIndexChanged.connect(self.PopulateTable)
        topLayout.addWidget(self.userDropdown)
        '''
        self.textBox = QtGui.QTextEdit()
        self.textDocument = QtGui.QTextDocument()
        self.textBox.setDocument(self.textDocument)        
        '''

        self.buttonPrint = QtGui.QPushButton('Print', self)
        self.buttonPrint.clicked.connect(self.handlePrint)
        self.buttonPreview = QtGui.QPushButton('Preview', self)
        self.buttonPreview.clicked.connect(self.handlePreview)
        topLayout.addWidget(self.buttonPrint)
        topLayout.addWidget(self.buttonPreview, 1)

        self.table = QtGui.QTableWidget()
        self.table.setColumnCount(6)
        self.table.verticalHeader().setVisible(False)
        mainlay.addWidget(self.table)
        '''
        for i,row in enumerate(cur):
            self.table.insertRow(self.table.rowCount())
            for j,val in enumerate(row):
                self.table.setItem(i, j, QtGui.QTableWidgetItem(str(val)))
        '''

        self.PopulateUsers()
        #self.PopulateTable()

        #mainlay.addWidget(self.textBox)

        #sharedDB.mySQLConnection.newPhaseAssignmentSignal.connect(self.CalculateWeeklies)

    def PopulateUsers(self):
        #for user in user list
        userList = []

        for user in sharedDB.myUsers.values():
            if user.isActive() == 1:
                userList.extend(user.name())
                self.userDropdown.addItem(user.name(), user.idUsers())

        self.userDropdown.model().sort(0)

    def handlePrint(self):
        dialog = QtGui.QPrintDialog()
        if dialog.exec_() == QtGui.QDialog.Accepted:
            self.handlePaintRequest(dialog.printer())

    def handlePreview(self):
        dialog = QtGui.QPrintPreviewDialog()
        dialog.paintRequested.connect(self.handlePaintRequest)
        dialog.exec_()

    def handlePaintRequest(self, printer):
        document = self.makeTableDocument()
        document.print_(printer)

    def makeTableDocument(self):
        document = QtGui.QTextDocument()
        cursor = QtGui.QTextCursor(document)
        rows = self.table.rowCount()
        columns = self.table.columnCount()
        table = cursor.insertTable(rows + 1, columns)
        format = table.format()
        format.setHeaderRowCount(1)
        table.setFormat(format)
        format = cursor.blockCharFormat()
        format.setFontWeight(QtGui.QFont.Bold)
        for column in range(columns):
            cursor.setCharFormat(format)
            cursor.insertText(self.table.horizontalHeaderItem(column).text())
            cursor.movePosition(QtGui.QTextCursor.NextCell)
        for row in range(rows):
            for column in range(columns):
                if self.table.item(row, column) is not None:
                    cursor.insertText(self.table.item(row, column).text())
                cursor.movePosition(QtGui.QTextCursor.NextCell)
        return document

    def PopulateTable(self):
        day = self.dateEdit.date().toPyDate()

        mon = day - timedelta(days=day.weekday())

        self.table.setRowCount(0)
        self.table.setWordWrap(1)

        dates = [
            mon, mon + timedelta(days=1), mon + timedelta(days=2),
            mon + timedelta(days=3), mon + timedelta(days=4)
        ]
        self.table.setHorizontalHeaderLabels(
            ("", "Mon " + mon.strftime("%m/%d/%y"),
             "Tues " + (mon + timedelta(days=1)).strftime("%m/%d/%y"),
             "Wed " + (mon + timedelta(days=2)).strftime("%m/%d/%y"),
             "Thurs " + (mon + timedelta(days=3)).strftime("%m/%d/%y"),
             "Fri " + (mon + timedelta(days=4)).strftime("%m/%d/%y")))

        self.table.insertRow(0)

        #find user then set name column
        currentUser = sharedDB.myUsers[str(
            self.userDropdown.itemData(
                self.userDropdown.currentIndex()).toString())]
        self.table.setItem(0, 0,
                           QtGui.QTableWidgetItem(str(currentUser.name())))

        #d+1 is column, b is row
        for d in range(0, len(dates)):
            #iterate through users bookings
            if str(dates[d]) in currentUser._bookingDict:
                bookings = currentUser._bookingDict[str(dates[d])]
                for b in range(0, len(bookings)):
                    if b >= self.table.rowCount():
                        self.table.insertRow(self.table.rowCount())

                    #assemble Text
                    text = sharedDB.myProjects[str(bookings[b].idprojects(
                    ))].name() + " - " + sharedDB.myPhaseAssignments[str(
                        bookings[b].idphaseassignments())].name()
                    self.table.setItem(b, d + 1, QtGui.QTableWidgetItem(text))
                    #print bookings[b].idprojects() idphaseassignments

        self.table.resizeRowsToContents()

    '''    
class MenuBuilderDialog(QDialog, FORM_CLASS):
    def __init__(self, uiparent):
        super(MenuBuilderDialog, self).__init__()

        self.setupUi(self)

        # reference to caller
        self.uiparent = uiparent

        self.combo_profile.lineEdit().setPlaceholderText(self.tr("Profile name"))

        # add icons
        self.button_add_menu.setIcon(QIcon(":/plugins/MenuBuilder/resources/plus.svg"))
        self.button_delete_profile.setIcon(QIcon(":/plugins/MenuBuilder/resources/delete.svg"))

        # custom qtreeview
        self.target = CustomQtTreeView(self)
        self.target.setGeometry(QRect(440, 150, 371, 451))
        self.target.setAcceptDrops(True)
        self.target.setDragEnabled(True)
        self.target.setDragDropMode(QAbstractItemView.DragDrop)
        self.target.setObjectName("target")
        self.target.setDropIndicatorShown(True)
        self.target.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.target.setHeaderHidden(True)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.target.sizePolicy().hasHeightForWidth())
        self.target.setSizePolicy(sizePolicy)
        self.target.setAutoFillBackground(True)
        self.verticalLayout_2.addWidget(self.target)

        self.browser = QgsBrowserModel()
        self.source.setModel(self.browser)
        self.source.setHeaderHidden(True)
        self.source.setDragEnabled(True)
        self.source.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.menumodel = MenuTreeModel(self)
        self.target.setModel(self.menumodel)
        self.target.setAnimated(True)

        # add a dock widget
        self.dock_widget = QDockWidget("Menus", self.uiparent.iface.mainWindow())
        self.dock_widget.resize(400, 300)
        self.dock_widget.setFloating(True)
        self.dock_widget.setObjectName(self.tr("Menu Tree"))
        self.dock_widget_content = QWidget()
        self.dock_widget.setWidget(self.dock_widget_content)
        dock_layout = QVBoxLayout()
        self.dock_widget_content.setLayout(dock_layout)
        self.dock_view = DockQtTreeView(self.dock_widget_content)
        self.dock_view.setDragDropMode(QAbstractItemView.DragOnly)
        self.dock_menu_filter = QLineEdit()
        self.dock_menu_filter.setPlaceholderText(self.tr("Filter by table description (postgis only)"))
        dock_layout.addWidget(self.dock_menu_filter)
        dock_layout.addWidget(self.dock_view)
        self.dock_view.setHeaderHidden(True)
        self.dock_view.setDragEnabled(True)
        self.dock_view.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.dock_view.setAnimated(True)
        self.dock_view.setObjectName("treeView")
        self.proxy_model = LeafFilterProxyModel(self)
        self.proxy_model.setFilterRole(Qt.ToolTipRole)
        self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)

        self.profile_list = []
        self.table = 'qgis_menubuilder_metadata'

        self.layer_handler = {
            'vector': self.load_vector,
            'raster': self.load_raster
        }

        # connect signals and handlers
        self.combo_database.activated.connect(partial(self.set_connection, dbname=None))
        self.combo_schema.activated.connect(self.update_profile_list)
        self.combo_profile.activated.connect(partial(self.update_model_idx, self.menumodel))
        self.button_add_menu.released.connect(self.add_menu)
        self.button_delete_profile.released.connect(self.delete_profile)
        self.dock_menu_filter.textEdited.connect(self.filter_update)
        self.dock_view.doubleClicked.connect(self.load_from_index)

    def filter_update(self):
        text = self.dock_menu_filter.displayText()
        self.proxy_model.setFilterRegExp(text)

    def show_dock(self, state, profile=None, schema=None):
        if not state:
            # just hide widget
            self.dock_widget.setVisible(state)
            return
        # dock must be read only and deepcopy of model is not supported (c++ inside!)
        self.dock_model = MenuTreeModel(self)
        if profile:
            # bypass combobox
            self.update_model(self.dock_model, schema, profile)
        else:
            self.update_model_idx(self.dock_model, self.combo_profile.currentIndex())
        self.dock_model.setHorizontalHeaderLabels(["Menus"])
        self.dock_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.proxy_model.setSourceModel(self.dock_model)
        self.dock_view.setModel(self.proxy_model)
        self.dock_widget.setVisible(state)

    def show_menus(self, state, profile=None, schema=None):
        if state:
            self.load_menus(profile=profile, schema=schema)
            return
        # remove menus
        for menu in self.uiparent.menus:
            self.uiparent.iface.mainWindow().menuBar().removeAction(menu.menuAction())

    def add_menu(self):
        """
        Add a menu inside qtreeview
        """
        item = QStandardItem('NewMenu')
        item.setIcon(QIcon(':/plugins/MenuBuilder/resources/menu.svg'))
        # select current index selected and insert as a sibling
        brother = self.target.selectedIndexes()

        if not brother or not brother[0].parent():
            # no selection, add menu at the top level
            self.menumodel.insertRow(self.menumodel.rowCount(), item)
            return

        parent = self.menumodel.itemFromIndex(brother[0].parent())
        if not parent:
            self.menumodel.insertRow(self.menumodel.rowCount(), item)
            return
        parent.appendRow(item)

    def update_database_list(self):
        """update list of defined postgres connections"""
        settings = QSettings()
        settings.beginGroup("/PostgreSQL/connections")
        keys = settings.childGroups()
        self.combo_database.clear()
        self.combo_schema.clear()
        self.menumodel.clear()
        self.combo_database.addItems(keys)
        self.combo_database.setCurrentIndex(-1)
        settings.endGroup()
        # clear profile list
        self.combo_profile.clear()
        self.combo_profile.setCurrentIndex(-1)

    def set_connection(self, databaseidx, dbname=None):
        """
        Connect to selected postgresql database
        """
        selected = self.combo_database.itemText(databaseidx) or dbname
        if not selected:
            return

        settings = QSettings()
        settings.beginGroup("/PostgreSQL/connections/{}".format(selected))

        if not settings.contains("database"):
            # no entry?
            QMessageBox.critical(self, "Error", "There is no defined database connection")
            return

        uri = QgsDataSourceURI()

        settingsList = ["service", "host", "port", "database", "username", "password"]
        service, host, port, database, username, password = map(
            lambda x: settings.value(x, "", type=str), settingsList)

        useEstimatedMetadata = settings.value("estimatedMetadata", False, type=bool)
        sslmode = settings.value("sslmode", QgsDataSourceURI.SSLprefer, type=int)

        settings.endGroup()

        if service:
            uri.setConnection(service, database, username, password, sslmode)
        else:
            uri.setConnection(host, port, database, username, password, sslmode)

        uri.setUseEstimatedMetadata(useEstimatedMetadata)

        # connect to db
        self.connect_to_uri(uri)
        # update schema list
        self.update_schema_list()

    @contextmanager
    def transaction(self):
        try:
            yield
            self.connection.commit()
        except self.pg_error_types() as e:
            self.connection.rollback()
            raise e

    def check_connected(func):
        """
        Decorator that checks if a database connection is active before executing function
        """
        @wraps(func)
        def wrapped(inst, *args, **kwargs):
            if not getattr(inst, 'connection', False):
                QMessageBox(
                    QMessageBox.Warning,
                    "Menu Builder",
                    inst.tr("Not connected to any database, please select one"),
                    QMessageBox.Ok,
                    inst
                ).exec_()
                return
            if inst.connection.closed:
                QMessageBox(
                    QMessageBox.Warning,
                    "Menu Builder",
                    inst.tr("Not connected to any database, please select one"),
                    QMessageBox.Ok,
                    inst
                ).exec_()
                return
            return func(inst, *args, **kwargs)
        return wrapped

    def connect_to_uri(self, uri):
        self.close_connection()
        self.host = uri.host() or os.environ.get('PGHOST')
        self.port = uri.port() or os.environ.get('PGPORT')

        username = uri.username() or os.environ.get('PGUSER') or os.environ.get('USER')
        password = uri.password() or os.environ.get('PGPASSWORD')

        try:
            self.connection = psycopg2.connect(uri.connectionInfo())
        except self.pg_error_types() as e:
            err = str(e)
            conninfo = uri.connectionInfo()

            ok, username, password = QgsCredentials.instance().get(
                conninfo, username, password, err)
            if not ok:
                raise Exception(e)

            if username:
                uri.setUsername(username)

            if password:
                uri.setPassword(password)

            self.connection = psycopg2.connect(uri.connectionInfo())

        self.pgencoding = self.connection.encoding

        return True

    def pg_error_types(self):
        return psycopg2.InterfaceError,\
               psycopg2.OperationalError,\
               psycopg2.ProgrammingError

    @check_connected
    def update_schema_list(self):
        self.combo_schema.clear()
        with self.transaction():
            cur = self.connection.cursor()
            cur.execute("""
                select nspname
                from pg_namespace
                where nspname not ilike 'pg_%'
                and nspname not in ('pg_catalog', 'information_schema')
                """)
            schemas = [row[0] for row in cur.fetchall()]
            self.combo_schema.addItems(schemas)

    @check_connected
    def update_profile_list(self, schemaidx):
        """
        update profile list from database
        """
        schema = self.combo_schema.itemText(schemaidx)
        with self.transaction():
            cur = self.connection.cursor()
            cur.execute("""
                select 1
                from pg_tables
                    where schemaname = '{0}'
                    and tablename = '{1}'
                union
                select 1
                from pg_matviews
                    where schemaname = '{0}'
                    and matviewname = '{1}'
                """.format(schema, self.table))
            tables = cur.fetchone()
            if not tables:
                box = QMessageBox(
                    QMessageBox.Warning,
                    "Menu Builder",
                    self.tr("Table '{}.{}' not found in this database, "
                            "would you like to create it now ?")
                    .format(schema, self.table),
                    QMessageBox.Cancel | QMessageBox.Yes,
                    self
                )
                ret = box.exec_()
                if ret == QMessageBox.Cancel:
                    return False
                elif ret == QMessageBox.Yes:
                    cur.execute("""
                        create table {}.{} (
                            id serial,
                            name varchar,
                            profile varchar,
                            model_index varchar,
                            datasource_uri text
                        )
                        """.format(schema, self.table))
                    self.connection.commit()
                    return False

            cur.execute("""
                select distinct(profile) from {}.{}
                """.format(schema, self.table))
            profiles = [row[0] for row in cur.fetchall()]
            saved_profile = self.combo_profile.currentText()
            self.combo_profile.clear()
            self.combo_profile.addItems(profiles)
            self.combo_profile.setCurrentIndex(self.combo_profile.findText(saved_profile))

    @check_connected
    def delete_profile(self):
        """
        Delete profile currently selected
        """
        idx = self.combo_profile.currentIndex()
        schema = self.combo_schema.currentText()
        profile = self.combo_profile.itemText(idx)
        box = QMessageBox(
            QMessageBox.Warning,
            "Menu Builder",
            self.tr("Delete '{}' profile ?").format(profile),
            QMessageBox.Cancel | QMessageBox.Yes,
            self
        )
        ret = box.exec_()
        if ret == QMessageBox.Cancel:
            return False
        elif ret == QMessageBox.Yes:
            self.combo_profile.removeItem(idx)
            with self.transaction():
                cur = self.connection.cursor()
                cur.execute("""
                    delete from {}.{}
                    where profile = '{}'
                    """.format(schema, self.table, profile))
        self.menumodel.clear()
        self.combo_profile.setCurrentIndex(-1)

    def update_model_idx(self, model, profile_index):
        """
        wrapper that checks combobox
        """
        profile = self.combo_profile.itemText(profile_index)
        schema = self.combo_schema.currentText()
        self.update_model(model, schema, profile)

    def sortby_modelindex(self, rows):
        return sorted(
            rows,
            key=lambda line: '/'.join(
                ['{:04}'.format(elem[0]) for elem in json.loads(line[2])]
            ))

    @check_connected
    def update_model(self, model, schema, profile):
        """
        Update the model by retrieving the profile given in database
        """
        menudict = {}

        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select name, profile, model_index, datasource_uri
                from {}.{}
                where profile = '{}'
                """.format(schema, self.table, profile)
            cur.execute(select)
            rows = cur.fetchall()
            model.clear()
            for name, profile, model_index, datasource_uri in self.sortby_modelindex(rows):
                menu = model.invisibleRootItem()
                indexes = json.loads(model_index)
                parent = ''
                for idx, subname in indexes[:-1]:
                    parent += '{}-{}/'.format(idx, subname)
                    if parent in menudict:
                        # already created entry
                        menu = menudict[parent]
                        continue
                    # create menu
                    item = QStandardItem(subname)
                    uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
                    item.setData(uri_struct)
                    item.setIcon(QIcon(':/plugins/MenuBuilder/resources/menu.svg'))
                    item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable |
                                  Qt.ItemIsEnabled | Qt.ItemIsDropEnabled |
                                  Qt.ItemIsEditable)
                    item.setWhatsThis("menu")
                    menu.appendRow(item)
                    menudict[parent] = item
                    # set current menu to the new created item
                    menu = item

                # add leaf (layer item)
                item = QStandardItem(name)
                uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
                # fix layer name instead of table name
                # usefull when the layer has been renamed in menu
                uri_struct.name = name
                if uri_struct.providerKey in ICON_MAPPER:
                    item.setIcon(QIcon(ICON_MAPPER[uri_struct.providerKey]))
                item.setData(uri_struct)
                # avoid placing dragged layers on it
                item.setDropEnabled(False)
                if uri_struct.providerKey == 'postgres':
                    # set tooltip to postgres comment
                    comment = self.get_table_comment(uri_struct.uri)
                    item.setToolTip(comment)
                menudict[parent].appendRow(item)

    @check_connected
    def save_changes(self):
        """
        Save changes in the postgres table
        """
        schema = self.combo_schema.currentText()
        profile = self.combo_profile.currentText()
        if not profile:
            QMessageBox(
                QMessageBox.Warning,
                "Menu Builder",
                self.tr("Profile cannot be empty"),
                QMessageBox.Ok,
                self
            ).exec_()
            return False

        try:
            with self.transaction():
                cur = self.connection.cursor()
                cur.execute("delete from {}.{} where profile = '{}'".format(
                    schema, self.table, profile))
                for item, data in self.target.iteritems():
                    if not data:
                        continue
                    cur.execute("""
                    insert into {}.{} (name,profile,model_index,datasource_uri)
                    values (%s, %s, %s, %s)
                    """.format(schema, self.table), (
                        item[-1][1],
                        profile,
                        json.dumps(item),
                        data.data())
                    )
        except psycopg2.ProgrammingError as exc:
            QMessageBox(
                QMessageBox.Warning,
                "Menu Builder",
                exc.message.decode(self.pgencoding),
                QMessageBox.Ok,
                self
            ).exec_()

        self.save_session(
            self.combo_database.currentText(),
            schema,
            profile,
            self.activate_dock.isChecked(),
            self.activate_menubar.isChecked()
        )
        self.update_profile_list(self.combo_schema.currentIndex())
        self.show_dock(self.activate_dock.isChecked())
        self.show_menus(self.activate_menubar.isChecked())
        return True

    @check_connected
    def load_menus(self, profile=None, schema=None):
        """
        Load menus in the main windows qgis bar
        """
        if not schema:
            schema = self.combo_schema.currentText()
        if not profile:
            profile = self.combo_profile.currentText()
        # remove previous menus
        for menu in self.uiparent.menus:
            self.uiparent.iface.mainWindow().menuBar().removeAction(menu.menuAction())

        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select name, profile, model_index, datasource_uri
                from {}.{}
                where profile = '{}'
                """.format(schema, self.table, profile)
            cur.execute(select)
            rows = cur.fetchall()
        # item accessor ex: '0-menu/0-submenu/1-item/'
        menudict = {}
        # reference to parent item
        parent = ''
        # reference to qgis main menu bar
        menubar = self.uiparent.iface.mainWindow().menuBar()

        for name, profile, model_index, datasource_uri in self.sortby_modelindex(rows):
            uri_struct = QgsMimeDataUtils.Uri(datasource_uri)
            indexes = json.loads(model_index)
            # root menu
            parent = '{}-{}/'.format(indexes[0][0], indexes[0][1])
            if parent not in menudict:
                menu = QMenu(self.uiparent.iface.mainWindow())
                self.uiparent.menus.append(menu)
                menu.setObjectName(indexes[0][1])
                menu.setTitle(indexes[0][1])
                menubar.insertMenu(
                    self.uiparent.iface.firstRightStandardMenu().menuAction(),
                    menu)
                menudict[parent] = menu
            else:
                # menu already there
                menu = menudict[parent]

            for idx, subname in indexes[1:-1]:
                # intermediate submenus
                parent += '{}-{}/'.format(idx, subname)
                if parent not in menudict:
                    submenu = menu.addMenu(subname)
                    submenu.setObjectName(subname)
                    submenu.setTitle(subname)
                    menu = submenu
                    # store it for later use
                    menudict[parent] = menu
                    continue
                # already treated
                menu = menudict[parent]

            # last item = layer
            layer = QAction(name, self.uiparent.iface.mainWindow())

            if uri_struct.providerKey in ICON_MAPPER:
                layer.setIcon(QIcon(ICON_MAPPER[uri_struct.providerKey]))

            if uri_struct.providerKey == 'postgres':
                # set tooltip to postgres comment
                comment = self.get_table_comment(uri_struct.uri)
                layer.setStatusTip(comment)
                layer.setToolTip(comment)

            layer.setData(uri_struct.uri)
            layer.setWhatsThis(uri_struct.providerKey)
            layer.triggered.connect(self.layer_handler[uri_struct.layerType])
            menu.addAction(layer)

    def get_table_comment(self, uri):
        schema, table = re.match(
            '.*table=(.*)\(.*',
            uri
        ).group(1).strip().replace('"', '').split('.')
        with self.transaction():
            cur = self.connection.cursor()
            select = """
                select description from pg_description
                join pg_class on pg_description.objoid = pg_class.oid
                join pg_namespace on pg_class.relnamespace = pg_namespace.oid
                where relname = '{}' and nspname='{}'
                """.format(table, schema)
            cur.execute(select)
            row = cur.fetchone()
            if row:
                return row[0]
        return ''

    def load_from_index(self, index):
        """Load layers from selected item index"""
        item = self.dock_model.itemFromIndex(self.proxy_model.mapToSource(index))
        if item.whatsThis() == 'menu':
            return
        if item.data().layerType == 'vector':
            layer = QgsVectorLayer(
                item.data().uri,  # uri
                item.text(),  # layer name
                item.data().providerKey  # provider name
            )
        elif item.data().layerType == 'raster':
            layer = QgsRasterLayer(
                item.data().uri,  # uri
                item.text(),  # layer name
                item.data().providerKey  # provider name
            )
        if not layer:
            return
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def load_vector(self):
        action = self.sender()
        layer = QgsVectorLayer(
            action.data(),  # uri
            action.text(),  # layer name
            action.whatsThis()  # provider name
        )
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def load_raster(self):
        action = self.sender()
        layer = QgsRasterLayer(
            action.data(),  # uri
            action.text(),  # layer name
            action.whatsThis()  # provider name
        )
        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def accept(self):
        if self.save_changes():
            QDialog.reject(self)
            self.close_connection()

    def reject(self):
        self.close_connection()
        QDialog.reject(self)

    def close_connection(self):
        """close current pg connection if exists"""
        if getattr(self, 'connection', False):
            if self.connection.closed:
                return
            self.connection.close()

    def save_session(self, database, schema, profile, dock, menubar):
        """save current profile for next session"""
        settings = QSettings()
        settings.setValue("MenuBuilder/database", database)
        settings.setValue("MenuBuilder/schema", schema)
        settings.setValue("MenuBuilder/profile", profile)
        settings.setValue("MenuBuilder/dock", dock)
        settings.setValue("MenuBuilder/menubar", menubar)

    def restore_session(self):
        settings = QSettings()
        database = settings.value("MenuBuilder/database", False)
        schema = settings.value("MenuBuilder/schema", 'public')
        profile = settings.value("MenuBuilder/profile", False)
        dock = settings.value("MenuBuilder/dock", False)
        menubar = settings.value("MenuBuilder/menubar", False)
        if not any([database, profile]):
            return

        self.set_connection(0, dbname=database)
        self.show_dock(bool(dock), profile=profile, schema=schema)
        self.show_menus(bool(menubar), profile=profile, schema=schema)
예제 #8
0
파일: mleap.py 프로젝트: ChannelFinder/hla
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 &copy; 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 CreateProjectWidget(QWidget):
   
    def __init__( self, parent = None ):
        
        super(CreateProjectWidget, self).__init__( parent )
        
        # load the user interface# load the user interface
        if getattr(sys, 'frozen', None):
	    #print (sys._MEIPASS+"/ui/createprojectwidget.ui");
	    projexui.loadUi(sys._MEIPASS, self, uifile = (sys._MEIPASS+"/ui/createprojectwidget.ui"))
	    
	else:
	    projexui.loadUi(__file__, self)
        #projexui.loadUi(__file__, self)
        
        # define custom properties
	
	self.dockWidget = QDockWidget()
        parent.addDockWidget(QtCore.Qt.LeftDockWidgetArea,self.dockWidget)
        self.dockWidget.setFloating(1)
        self.dockWidget.setWindowTitle("Create Project")
        self.dockWidget.setWidget(self)
	
	#self.setWindowFlags(QtCore.Qt.Tool)
        
        self._backend               = None
        
	#self.myClientNameLineEdit = clientIPLineEdit.ClientIPLineEdit(self)
	#self.clientIPBoxLayout.addWidget(self.myClientNameLineEdit)
	
        #connects buttons
        self.createButton.clicked.connect(self.CreateProject)
        self.cancelButton.clicked.connect(self.cancel)
        self.UpdateClientList()
	self.clientComboBox.currentIndexChanged.connect(self.UpdateIPList)
	
	self.UpdateIPList(self.clientComboBox.currentIndex())        
	
        #self.open()

    def UpdateClientList(self):
	self.clientComboBox.clear()
	for client in sharedDB.myClients.values():
	    self.clientComboBox.addItem(client._name,QtCore.QVariant(client._idclients))
    
    def UpdateIPList(self, sentclientcmboboxindex):
	self.ipComboBox.clear()
	#print sharedDB.myClients[str(self.clientComboBox.itemData(self.clientComboBox.currentIndex()).toString())]
	if str(self.clientComboBox.itemData(self.clientComboBox.currentIndex()).toString()) in sharedDB.myClients:
	    client = sharedDB.myClients[str(self.clientComboBox.itemData(self.clientComboBox.currentIndex()).toString())]
	    #if str(client._idclients) == self.clientComboBox.itemData(sentclientid).toString():
	    for ip in client._ips.values():
		self.ipComboBox.addItem(ip._name,QtCore.QVariant(ip._idips))


    def cancel(self):
        self.close()

    def setDefaults(self):
        #set initial values
        self.duedateEdit.setDate(QDate.currentDate().addDays(30))
        self.xres_spinBox.setValue(1280)
        self.yres_spinBox.setValue(720)
        
	self.phaseListWidget.clear()
	
        for myPhase in sharedDB.myPhases.values():        
            if myPhase._name != "DUE":
                item = QtGui.QListWidgetItem(myPhase._name)
                self.phaseListWidget.addItem(item)
                self.phaseListWidget.setItemSelected(item,True)
        
        
        self.descriptionTextEdit.setText("")
        self.durationEdit.setTime(QTime.fromString("00:01:00"))
        self.projectNameQLineEdit.setText("Project Name")
        self.fps.setValue(25)
        
    '''def open(self):
	self.show()
	self.activateWindow()
    '''	
    def CreateProject(self):        
        name = str(self.projectNameQLineEdit.text())
        folderLocation = ''
        #idstatus = 0
        idips = str(self.ipComboBox.itemData(self.ipComboBox.currentIndex()).toString())
	idclients = str(self.clientComboBox.itemData(self.clientComboBox.currentIndex()).toString())
	fps = self.fps.value()
        renderWidth = self.xres_spinBox.value()
        renderHeight = self.yres_spinBox.value()
        due_date = self.duedateEdit.date().toPyDate();
        renderPriority = 50
        description = self.descriptionTextEdit.toPlainText()
        phases = []
        
        #for each list item
        for item in self.phaseListWidget.selectedItems():
            #get phase of same name
            for x in range(0,len(sharedDB.myPhases))[::-1]:
                #print sharedDB.myPhases[x]._name
                #print item.text
                if sharedDB.myPhases.values()[x]._name == item.text():                    
		    phases.append(sharedDB.phaseAssignments.PhaseAssignments(_idphases = sharedDB.myPhases.values()[x].id(), _startdate = due_date,_enddate = due_date,_updated = 0))
                    continue
            #start from due date and work backwards
            #for 
        
        phases = InitializeDates(phases,due_date,self.durationEdit.time())
        
        #Add due date into phases
        phases.append(sharedDB.phaseAssignments.PhaseAssignments(_idphases = 16, _startdate = due_date,_enddate = due_date,_updated = 0))
        
        newProj = sharedDB.projects.Projects(_name = name, _folderLocation = '', _idstatuses = 1, _fps = fps, _renderWidth = renderWidth, _renderHeight = renderHeight, _due_date = due_date, _renderPriority = renderPriority, _description = description, _idips = idips, _idclients = idclients, _new = 0)
	
	newProj._new = 1
	newProj.Save()
	#sharedDB.myProjects.append(newProj)
	
	#connect phases to projectid
	
	for phase in phases:
	    newProj.AddPhase(phase)
	    '''
	    phase._idprojects = newProj._idprojects
	    phase.project = newProj
	    phase._new = 1
	    phase.Save()
	    newProj._phases[str(phase.id())] = phase
	    '''
	
	if str(idips) in sharedDB.myIps:
	    ip = sharedDB.myIps[str(idips)]
	    ip._projects[str(newProj.id())] = newProj
	
        self.close();