Esempio n. 1
0
class dbmanagerUI(QMainWindow, ui_dbmanager.Ui_dbmanagerUI):
    """Main UI for MASAR database manager."""
    def __init__(self):
        """"""
        super(dbmanagerUI, self).__init__()
        self.setupUi(self)
        self.statusbar.showMessage("Ready")

        exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit Masar Configuration Manager.')
        exitAction.triggered.connect(QtGui.qApp.quit)

        self.groupdatabasemenubar()

        #default database source, could be 0: SQLite, 1: MongoDB, and 2: MySQL
        self.dbsource = None

        self.defaultdbinfo = self._loadmasarconfig()
        self.usedefaultdb = True

        self.comboxboxSignalMapper = QtCore.QSignalMapper(self)
        self.comboxboxSignalMapper.mapped[QtGui.QWidget].connect(
            self.comboboxSignalMapperMapped)

        self.pushbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.pushbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.pushbuttonSignalMapperMapped)

        self.showpvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.showpvbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.showpvbuttonSignalMapperMapped)

        self.choosepvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.choosepvbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.choosepvbuttonSignalMapperMapped)

        self.currentselectedrow4config = -1

        self.selectedsystem = "Others"

        # self.pvgrouptreeview = QTreeView()
        self.pvGroupTreeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.pvgroupmodel = QStandardItemModel()
        # self.pvgroupmodel.setHorizontalHeaderLabels(['id', 'date', 'version', "description"])
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        self.pvGroupTreeView.setModel(self.pvgroupmodel)
        self.pvGroupTreeView.setUniformRowHeights(True)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def comboboxSignalMapperMapped(self, comboBox):
        if self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).isEnabled():
            self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).setEnabled(False)
            button = self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(
                button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            button.setPalette(palette)
        else:
            self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).setEnabled(True)
            button = self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(
                button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('red'))
            button.setPalette(palette)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def pushbuttonSignalMapperMapped(self, pushbutton):
        configstatus = str(
            self.masarConfigTableWidget.cellWidget(
                pushbutton.row, pushbutton.column - 1).currentText())
        cid = str(self.masarConfigTableWidget.item(pushbutton.row, 0).text())
        cname = str(self.masarConfigTableWidget.item(pushbutton.row, 1).text())

        if self.updatemasarconfigstatus(configstatus, cid, configname=cname):
            palette = QtGui.QPalette(
                pushbutton.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            pushbutton.setPalette(palette)
            pushbutton.setEnabled(False)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def showpvbuttonSignalMapperMapped(self, showpvbutton):
        if self.newConfigTableWidget.item(showpvbutton.row,
                                          showpvbutton.column + 2) is None:
            raise RuntimeError("Unknown pv file")
        pvfilename = self.newConfigTableWidget.item(
            showpvbutton.row, showpvbutton.column + 2).text()
        if not os.path.isfile(pvfilename):
            raise RuntimeError(
                "Invalid pv file name for (row, col): (%s, %s)" %
                (showpvbutton.row, showpvbutton.column + 2))

        text = ", ".join(np.loadtxt(str(pvfilename), dtype=str, comments="#"))
        if self.newConfigTableWidget.item(showpvbutton.row, 0) is not None:
            head = self.newConfigTableWidget.item(showpvbutton.row, 0).text()
        else:
            head = ""
        msg = QMessageBox(self,
                          windowTitle='PVs for group %s' % head,
                          text="The following PVs to be added:")
        msg.setDetailedText(text)
        msg.exec_()

    @QtCore.pyqtSlot(QtGui.QWidget)
    def choosepvbuttonSignalMapperMapped(self, chhosepvbutton):
        self.newConfigTableWidget.setItem(
            chhosepvbutton.row, chhosepvbutton.column + 1,
            QTableWidgetItem(QFileDialog.getOpenFileName(self, "Open File")))

    def _loadmasarconfig(self):
        cf = ConfigParser.SafeConfigParser()
        cf.read([
            os.path.expanduser('~/.masarservice.conf'),
            '/etc/masarservice.conf', 'masarservice.conf',
            "%s/masarservice.conf" % os.path.abspath(os.path.dirname(__file__))
        ])
        return cf

    def groupdatabasemenubar(self):
        """Group 3 Databases menu together to make selection exclusive."""
        group = QtGui.QActionGroup(self)
        self.actionSQLite.setActionGroup(group)
        self.actionMongoDB.setActionGroup(group)
        self.actionMySQL.setActionGroup(group)

    def actionsqlitemenu(self):
        """Answer action when SQLite is selected."""
        if self.actionSQLite.isChecked():
            self.dbsource = 0
            self.defaultsqlitedb()
            self.listPvGroupPushButton.setEnabled(True)

    def actionmongodbmenu(self):
        """Answer action when MongoDB is selected."""
        if self.actionMongoDB.isChecked():
            self.dbsource = 1
            self.defaultmongodb()
            self.listPvGroupPushButton.setEnabled(False)

    def actionmysqlmenu(self):
        """Answer action when MySQL is selected."""
        if self.actionMySQL.isChecked():
            QMessageBox.warning(self, 'Warning',
                                "MySQL support not implemented yet.")
            self.actionMySQL.setChecked(False)
            if self.dbsource == 0:
                self.actionSQLite.setChecked(True)
            elif self.dbsource == 1:
                self.actionMongoDB.setChecked(True)

    def showdefaultdbinfo(self):
        """"""
        if self.dbsource == 0:
            self.defaultsqlitedb()
        elif self.dbsource == 1:
            self.defaultmongodb()
        elif self.dbsource == 2:
            QMessageBox.warning(self, 'Warning',
                                "MySQL support not implemented yet.")

    def defaultsqlitedb(self):
        """"""
        if self.defaultdbinfo.has_section("sqlite"):
            self.databaseDefault.setText(
                self.defaultdbinfo.get("sqlite", "database"))
            self.hostDefault.clear()
            self.portDefault.clear()
            self.userDefault.clear()

    def defaultmongodb(self):
        """"""
        if self.defaultdbinfo.has_section("mongodb"):
            self.databaseDefault.setText(
                self.defaultdbinfo.get("mongodb", "database"))
            self.hostDefault.setText(self.defaultdbinfo.get("mongodb", "host"))
            self.portDefault.setText(self.defaultdbinfo.get("mongodb", "port"))
            self.userDefault.setText(
                self.defaultdbinfo.get("mongodb", "username"))

    def getdatabasename(self):
        """"""
        self.database = self.databaseLineEdit.text()

    def getdatabaseport(self):
        """"""
        self.databaseport = self.databaseportLineEdit.text()

    def getdatabasehost(self):
        """"""
        self.databasehost = self.databaseHostLineEdit.text()

    def getdatabasepw(self):
        """"""
        self.databasepw = self.databasePwLineEdit.text()

    def showmasarconfigs(self):
        """"""
        result = None
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite
            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigs(
                conn, servicename="masar")
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo
            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)
            resultdict = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
                mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            result = [['id', 'name', 'desc', 'date', 'version', 'status']]
            for res in resultdict:
                result.append([
                    res['configidx'], res['name'], res['desc'],
                    res['created_on'], res['version'], res['status']
                ])
                # res['system']

        if result is not None:
            self._setconfigtable(result)

    def choosedbsrc(self, bool):
        """Choose DB source"""
        if bool:
            self.usedefaultdb = True
        else:
            self.usedefaultdb = False

    def _setconfigtable(self, content):
        """"""
        # head = self.masarConfigTableWidget.horizontalHeader()
        self.masarConfigTableWidget.clearContents()
        # self.masarConfigTableWidget.setHorizontalHeaderLabels(head)

        if len(content) > 1:
            self.masarConfigTableWidget.setRowCount(len(content) - 1)

            n = 0
            data = sorted(content[1:], key=itemgetter(0), reverse=True)
            for res in data:
                m = 0
                for item in res:
                    if not isinstance(item, basestring):
                        item = str(item)
                    if item:
                        if m == 5:
                            newitem = QtGui.QComboBox()
                            newitem.addItem("active")
                            newitem.addItem("inactive")
                            if item == "active":
                                newitem.setCurrentIndex(0)
                            else:
                                newitem.setCurrentIndex(1)

                            newitem.row = n
                            newitem.column = m
                            self.masarConfigTableWidget.setCellWidget(
                                n, m, newitem)
                            self.comboxboxSignalMapper.setMapping(
                                newitem, newitem)
                            newitem.currentIndexChanged.connect(
                                self.comboxboxSignalMapper.map)

                            updatebutton = QtGui.QPushButton()
                            updatebutton.setText("Update")
                            updatebutton.setEnabled(False)
                            updatebutton.row = n
                            updatebutton.column = m + 1
                            self.pushbuttonSignalMapper.setMapping(
                                updatebutton, updatebutton)
                            self.masarConfigTableWidget.setCellWidget(
                                n, m + 1, updatebutton)
                            updatebutton.clicked.connect(
                                self.pushbuttonSignalMapper.map)
                        else:
                            newitem = QTableWidgetItem(item)
                            newitem.setFlags(Qt.ItemIsEnabled
                                             | Qt.ItemIsSelectable)
                            self.masarConfigTableWidget.setItem(n, m, newitem)
                    m += 1
                n += 1

            self.masarConfigTableWidget.resizeColumnsToContents()

    def updatemasarconfigstatus(self, configstatus, cid, configname=None):
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            pymasarsqlite.service.updateServiceConfigStatus(
                conn, cid, status=configstatus)
            pymasarsqlite.utils.save(conn)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)
            pymasarmongo.pymasarmongo.pymasar.updateconfig(mongoconn,
                                                           collection,
                                                           configname,
                                                           configidx=int(cid),
                                                           status=configstatus)
            pymasarmongo.db.utils.close(mongoconn)

        return True

    def addnewpvgrouprow(self):
        """Add a new row to add pv group to MASAR configuration"""
        print("""Add a new row to add pv group to MASAR configuration""")
        currowcount = self.newConfigTableWidget.rowCount()
        self.newConfigTableWidget.setRowCount(currowcount + 1)

        showpvbutton = QtGui.QPushButton()
        showpvbutton.setText("Show PVs")
        showpvbutton.setEnabled(True)
        showpvbutton.row = currowcount
        showpvbutton.column = 2
        self.showpvbuttonSignalMapper.setMapping(showpvbutton, showpvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount,
                                                showpvbutton.column,
                                                showpvbutton)
        showpvbutton.clicked.connect(self.showpvbuttonSignalMapper.map)

        choosepvbutton = QtGui.QPushButton()
        choosepvbutton.setText("PV File")
        choosepvbutton.setEnabled(True)
        choosepvbutton.row = currowcount
        choosepvbutton.column = 3
        self.choosepvbuttonSignalMapper.setMapping(choosepvbutton,
                                                   choosepvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount,
                                                choosepvbutton.column,
                                                choosepvbutton)
        choosepvbutton.clicked.connect(self.choosepvbuttonSignalMapper.map)

    def removepvgrouprow(self):
        """Remove selected pv group from the configuration to be added into MASAR database"""
        if self.currentselectedrow4config == -1:
            raise RuntimeError("No pv group selected.")
        rownametobedelete = self.newConfigTableWidget.item(
            self.currentselectedrow4config, 0)
        if rownametobedelete is not None:
            rownametobedelete = rownametobedelete.text()
        self.newConfigTableWidget.removeRow(self.currentselectedrow4config)
        if rownametobedelete is not None:
            print("Successfully delete pv group: ", rownametobedelete)
        else:
            print("Successfully delete row: ", self.currentselectedrow4config)
        self.currentselectedrow4config = -1

    def savemasarsqlite(self):
        """"""
        # get data from sqlite
        if self.usedefaultdb:
            masardb = str(self.databaseDefault.toPlainText())
        else:
            masardb = str(self.databaseLineEdit.text())

        if masardb != "":
            os.environ["MASAR_SQLITE_DB"] = masardb
        else:
            QMessageBox.warning(self, "Warning",
                                "Cannot find MASAR SQLite Database")
            return

        import pymasarsqlite
        conn = pymasarsqlite.utils.connect()
        existedresult = pymasarsqlite.service.retrieveServiceConfigs(
            conn, servicename="masar")

        newcfgdata = self._getnewconfigurationdata(existedresult)
        if newcfgdata is None:
            QMessageBox.warning(
                self, "Warning",
                "Not enough information for a new configuration.")
            return
        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]

        if newcfgname is None or msystem is None or newcfgdata is None:
            # Nothing to be added.
            QMessageBox.warning(
                self, "Warning",
                "No name or system is given, or empty configuration data.")
            return

        for i in range(len(cfgdata[0])):
            if cfgdata[0][i] is None:
                QMessageBox.warning(self, "Warning", "Wrong PV group name")
                return

            if cfgdata[2][i] is not None and os.path.isfile(cfgdata[2][i]):
                pvs = list(np.loadtxt(cfgdata[2][i], dtype=str, comments="#"))
                if len(pvs) > 0:
                    for j, pv in enumerate(pvs):
                        pvs[j] = pv.strip()
                    pymasarsqlite.pvgroup.savePvGroup(conn,
                                                      cfgdata[0][i],
                                                      func=cfgdata[1][i])
                    pymasarsqlite.pvgroup.saveGroupPvs(conn, cfgdata[0][i],
                                                       pvs)

        try:
            pymasarsqlite.service.saveServiceConfig(conn,
                                                    "masar",
                                                    newcfgname,
                                                    configdesc=desc,
                                                    system=msystem)
            pymasarsqlite.service.saveServicePvGroup(conn, newcfgname,
                                                     cfgdata[0])
        except Exception as e:
            QMessageBox.warning(self, "Error", e.message)
            return

        pymasarsqlite.utils.save(conn)
        QMessageBox.information(
            self, "Congratulation",
            "A new configuration has been added successfully.")
        pymasarsqlite.utils.close(conn)

    def savemasarmongodb(self):
        """"""
        # get data from mongodb
        if self.usedefaultdb:
            database = str(self.databaseDefault.toPlainText())
            host = str(self.hostDefault.toPlainText())
            port = str(self.portDefault.toPlainText())
        else:
            database = str(self.databaseLineEdit.text())
            host = str(self.databaseHostLineEdit.text())
            port = str(self.databasePortLineEdit.text())

        import pymasarmongo

        mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                           port=port,
                                                           db=database)
        existedresult = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
            mongoconn, collection)
        existedcfg = []
        for res in existedresult:
            existedcfg.append([
                res['configidx'], res['name'], res['desc'], res['created_on'],
                res['version'], res['status']
            ])
        newcfgdata = self._getnewconfigurationdata(existedcfg)
        if newcfgdata is None:
            # Nothing to be added.
            raise ValueError("Empty configuration.")

        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]
        pvs = []
        for pvf in cfgdata[2]:
            if pvf is not None and os.path.isfile(pvf):
                pvs += list(np.loadtxt(pvf, dtype=str, comments="#"))
        if pvs:
            for i, pv in enumerate(pvs):
                pvs[i] = pv.strip()
            pymasarmongo.pymasarmongo.pymasar.saveconfig(mongoconn,
                                                         collection,
                                                         newcfgname,
                                                         desc=desc,
                                                         system=msystem,
                                                         pvlist={"names": pvs})
            QMessageBox.information(
                self, "Congratulation",
                "A new configuration has been added successfully.")
        else:
            QMessageBox.warning(self, "Warning",
                                "No PVs available for the new configuration.")

        pymasarmongo.db.utils.close(mongoconn)

    def submitmasarconfig(self):
        """submit a new configuration to MASAR database"""
        if self.dbsource is None:
            QMessageBox.warning(
                self, "Warning",
                "Unknown database source.\nPlease select which database should be use."
            )
            return
        if self.dbsource == 0:
            self.savemasarsqlite()
        elif self.dbsource == 1:
            self.savemasarmongodb()

    def _getnewconfigurationdata(self, existedresult):
        """"""
        newcfgname = self.newConfigurationLineEdit.text()
        if newcfgname is None or str(newcfgname) == "":
            QMessageBox.warning(self, "Warning",
                                "Name of configuration is empty.")
            return None
        elif str(newcfgname) in np.array(existedresult)[:, 1]:
            QMessageBox.warning(
                self, "Warning",
                "Configuration (%s) exists already." % str(newcfgname))
            return None
        else:
            newcfgname = str(newcfgname)
        desc = self.newConfigurationDescription.text()
        if str(desc) == "":
            desc = None
        else:
            desc = str(desc)

        msystem = str(self.systemComboBox.currentText())
        if msystem == "Others":
            msystem = self.systemLineEdit.text()
            if msystem is None or str(msystem) == "":
                QMessageBox.warning(
                    self, "Warning",
                    "System for configuration (%s) not specified yet." %
                    str(newcfgname))
                return None
            else:
                msystem = str(msystem)

        pvgroups = self.newConfigTableWidget.rowCount()
        if pvgroups == 0:
            QMessageBox.warning(
                self, "Warning",
                "No PV founded for new configuration (%s)." % str(newcfgname))
            return None

        pvgroupnames = []
        pvgroupdescs = []
        pvgroupfiles = []
        for count in range(pvgroups):
            # Collect PV group name information
            if self.newConfigTableWidget.item(count, 0) is not None and str(
                    self.newConfigTableWidget.item(count, 0).text()) != "":
                pvgname = str(self.newConfigTableWidget.item(count, 0).text())
                if pvgname in pvgroupnames:
                    QMessageBox.warning(
                        self, "Warning",
                        "Duplicated pv group name: %s." % str(pvgname))
                    return None
                pvgroupnames.append(pvgname)
            elif self.newConfigTableWidget.item(count, 4) is None or str(
                    self.newConfigTableWidget.item(count, 4)) == "":
                continue
            elif self.dbsource == 1:
                pvgroupnames.append(None)
            else:
                QMessageBox.warning(self, "Warning", "Empty pv group name.")
                return None
                # reply = QMessageBox.question(self, "Message",
                #                              "pv group name not specified for row {}.\nContinue?".format(count),
                #                              QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                # if reply == QMessageBox.Yes:
                #     pvgroupnames.append(None)
                # else:
                #     return None

            # Collection PV group descriptions
            if self.newConfigTableWidget.item(count, 1) is not None:
                pvgroupdescs.append(
                    str(self.newConfigTableWidget.item(count, 1).text()))
            else:
                pvgroupdescs.append(None)

            # PV files for the pv group.
            if self.newConfigTableWidget.item(count, 4) is not None:
                pvgroupfiles.append(
                    str(self.newConfigTableWidget.item(count, 4).text()))
            else:
                pvgroupfiles.append(None)

        if pvgroupfiles:
            return newcfgname, desc, msystem, [
                pvgroupnames, pvgroupdescs, pvgroupfiles
            ]
        else:
            return None

    def currentselectedrow(self, row, col):
        """Cache current selected row in MASAR configuration Table Widget."""
        self.currentselectedrow4config = row

    def updateselectedsystem(self, system):
        """Update selected system if value changed"""
        self.selectedsystem = str(system)

    def updatesystemcombobox(self):
        """Update selected system if value changed"""
        self.systemComboBox.clear()
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigProps(
                conn, propname="system", servicename="masar")
            index = 0
            if len(result) > 1:
                res = sorted(set(list(np.array(result[1:])[:, 3])))
                #for res in result[1:]:
                self.systemComboBox.addItems(res)
                index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)

            result = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
                mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            results = []
            for res in result:
                if res["system"] not in results:
                    results.append(res["system"])
            res = sorted(set(results))
            self.systemComboBox.addItems(res)
            index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)

    def listpvgroups(self):
        """"""
        self.pvgroupmodel.clear()
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.pvgroup.retrievePvGroups(conn)
            if len(result) > 0:
                result = sorted(result, key=itemgetter(0), reverse=True)
            for res in result:
                parent1 = QStandardItem(res[1])
                child1 = QStandardItem('id: {}'.format(res[0]))
                child2 = QStandardItem('description: {}'.format(res[2]))
                child3 = QStandardItem('date: {}'.format(res[3]))
                child4 = QStandardItem('version: {}'.format(res[4]))
                parent1.appendColumn([child1, child2, child3, child4])
                self.pvgroupmodel.appendRow(parent1)
            selmod = self.pvGroupTreeView.selectionModel()
            index2 = self.pvgroupmodel.indexFromItem(child3)
            selmod.select(
                index2, QItemSelectionModel.Select | QItemSelectionModel.Rows)
            pymasarsqlite.utils.close(conn)

            self.pvGroupTreeView.setContextMenuPolicy(Qt.CustomContextMenu)
            self.pvGroupTreeView.clicked.connect(self.showpvsinpvgroup)
            # self.connect(self.pvGroupTreeView,
            #              QtCore.SIGNAL("clicked(QModelIndex)"),
            #              #QtCore.SIGNAL("customContextMenuRequested(const QPoint &)"),
            #              self.doMenu)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

    def showpvsinpvgroup(self, point):
        if point.model().itemFromIndex(point).child(0) is None:
            return
        reply = QMessageBox.question(
            self, 'Message', "show all pvs belong to group {} ?".format(
                point.model().itemFromIndex(point).text()),
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            pvgroupidx = int(
                str(point.model().itemFromIndex(point).child(0).text().split(
                    ":")[1]).strip())

            # msg = QMessageBox(self)
            msg = ShowPvMessageBox()
            msg.setWindowTitle('PVs for group {}'.format(
                point.model().itemFromIndex(point).text()))
            msg.setText("Click show details to see all pvs.")
            # windowTitle = 'PVs for group {}'.format(point.model().itemFromIndex(point).text())

            if self.dbsource == 0:
                # get data from sqlite
                if self.usedefaultdb:
                    # masardb = str(self.databaseDefault.text())
                    masardb = str(self.databaseDefault.toPlainText())
                else:
                    masardb = str(self.databaseLineEdit.text())

                if masardb != "":
                    os.environ["MASAR_SQLITE_DB"] = masardb
                else:
                    raise RuntimeError("Cannot find MASAR SQLite Database")

                import pymasarsqlite

                conn = pymasarsqlite.utils.connect()
                result = pymasarsqlite.pvgroup.retrieveGroupPvs(
                    conn, pvgroupidx)
                text = "\n".join(list(np.array(result)[:, 0]))
                msg.setDetailedText(text)
                msg.exec_()

            elif self.dbsource == 1:
                # get data from mongodb
                if self.usedefaultdb:
                    database = str(self.databaseDefault.toPlainText())
                    host = str(self.hostDefault.toPlainText())
                    port = str(self.portDefault.toPlainText())
                else:
                    database = str(self.databaseLineEdit.text())
                    host = str(self.databaseHostLineEdit.text())
                    port = str(self.databasePortLineEdit.text())

                import pymasarmongo
Esempio n. 2
0
class dbmanagerUI(QMainWindow, ui_dbmanager.Ui_dbmanagerUI):
    """Main UI for MASAR database manager."""
    def __init__(self):
        """"""
        super(dbmanagerUI, self).__init__()
        self.setupUi(self)
        self.statusbar.showMessage("Ready")

        exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit Masar Configuration Manager.')
        exitAction.triggered.connect(QtGui.qApp.quit)

        self.groupdatabasemenubar()

        #default database source, could be 0: SQLite, 1: MongoDB, and 2: MySQL
        self.dbsource = None

        self.defaultdbinfo = self._loadmasarconfig()
        self.usedefaultdb = True

        self.comboxboxSignalMapper = QtCore.QSignalMapper(self)
        self.comboxboxSignalMapper.mapped[QtGui.QWidget].connect(self.comboboxSignalMapperMapped)

        self.pushbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.pushbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.pushbuttonSignalMapperMapped)

        self.showpvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.showpvbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.showpvbuttonSignalMapperMapped)

        self.choosepvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.choosepvbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.choosepvbuttonSignalMapperMapped)

        self.currentselectedrow4config = -1

        self.selectedsystem = "Others"

        # self.pvgrouptreeview = QTreeView()
        self.pvGroupTreeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.pvgroupmodel = QStandardItemModel()
        # self.pvgroupmodel.setHorizontalHeaderLabels(['id', 'date', 'version', "description"])
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        self.pvGroupTreeView.setModel(self.pvgroupmodel)
        self.pvGroupTreeView.setUniformRowHeights(True)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def comboboxSignalMapperMapped(self, comboBox):
        if self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).isEnabled():
            self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).setEnabled(False)
            button = self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            button.setPalette(palette)
        else:
            self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).setEnabled(True)
            button = self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('red'))
            button.setPalette(palette)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def pushbuttonSignalMapperMapped(self, pushbutton):
        configstatus = str(self.masarConfigTableWidget.cellWidget(pushbutton.row, pushbutton.column-1).currentText())
        cid = str(self.masarConfigTableWidget.item(pushbutton.row, 0).text())
        cname = str(self.masarConfigTableWidget.item(pushbutton.row, 1).text())

        if self.updatemasarconfigstatus(configstatus, cid, configname=cname):
            palette = QtGui.QPalette(pushbutton.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            pushbutton.setPalette(palette)
            pushbutton.setEnabled(False)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def showpvbuttonSignalMapperMapped(self, showpvbutton):
        if self.newConfigTableWidget.item(showpvbutton.row, showpvbutton.column + 2) is None:
            raise RuntimeError("Unknown pv file")
        pvfilename = self.newConfigTableWidget.item(showpvbutton.row, showpvbutton.column + 2).text()
        if not os.path.isfile(pvfilename):
            raise RuntimeError("Invalid pv file name for (row, col): (%s, %s)" % (showpvbutton.row,
                                                                                  showpvbutton.column + 2))

        text = ", ".join(np.loadtxt(str(pvfilename), dtype=str, comments="#"))
        if self.newConfigTableWidget.item(showpvbutton.row, 0) is not None:
            head = self.newConfigTableWidget.item(showpvbutton.row, 0).text()
        else:
            head = ""
        msg = QMessageBox(self,
                          windowTitle='PVs for group %s' % head,
                          text="The following PVs to be added:")
        msg.setDetailedText(text)
        msg.exec_()

    @QtCore.pyqtSlot(QtGui.QWidget)
    def choosepvbuttonSignalMapperMapped(self, chhosepvbutton):
        self.newConfigTableWidget.setItem(chhosepvbutton.row,
                                          chhosepvbutton.column+1,
                                          QTableWidgetItem(QFileDialog.getOpenFileName(self, "Open File")))

    def _loadmasarconfig(self):
        cf = ConfigParser.SafeConfigParser()
        cf.read([
            os.path.expanduser('~/.masarservice.conf'),
            '/etc/masarservice.conf',
            'masarservice.conf',
            "%s/masarservice.conf" % os.path.abspath(os.path.dirname(__file__))
        ])
        return cf

    def groupdatabasemenubar(self):
        """Group 3 Databases menu together to make selection exclusive."""
        group = QtGui.QActionGroup(self)
        self.actionSQLite.setActionGroup(group)
        self.actionMongoDB.setActionGroup(group)
        self.actionMySQL.setActionGroup(group)

    def actionsqlitemenu(self):
        """Answer action when SQLite is selected."""
        if self.actionSQLite.isChecked():
            self.dbsource = 0
            self.defaultsqlitedb()
            self.listPvGroupPushButton.setEnabled(True)

    def actionmongodbmenu(self):
        """Answer action when MongoDB is selected."""
        if self.actionMongoDB.isChecked():
            self.dbsource = 1
            self.defaultmongodb()
            self.listPvGroupPushButton.setEnabled(False)

    def actionmysqlmenu(self):
        """Answer action when MySQL is selected."""
        if self.actionMySQL.isChecked():
            QMessageBox.warning(self, 'Warning', "MySQL support not implemented yet.")
            self.actionMySQL.setChecked(False)
            if self.dbsource == 0:
                self.actionSQLite.setChecked(True)
            elif self.dbsource == 1:
                self.actionMongoDB.setChecked(True)

    def showdefaultdbinfo(self):
        """"""
        if self.dbsource == 0:
            self.defaultsqlitedb()
        elif self.dbsource == 1:
            self.defaultmongodb()
        elif self.dbsource == 2:
            QMessageBox.warning(self, 'Warning', "MySQL support not implemented yet.")

    def defaultsqlitedb(self):
        """"""
        if self.defaultdbinfo.has_section("sqlite"):
            self.databaseDefault.setText(self.defaultdbinfo.get("sqlite", "database"))
            self.hostDefault.clear()
            self.portDefault.clear()
            self.userDefault.clear()

    def defaultmongodb(self):
        """"""
        if self.defaultdbinfo.has_section("mongodb"):
            self.databaseDefault.setText(self.defaultdbinfo.get("mongodb", "database"))
            self.hostDefault.setText(self.defaultdbinfo.get("mongodb", "host"))
            self.portDefault.setText(self.defaultdbinfo.get("mongodb", "port"))
            self.userDefault.setText(self.defaultdbinfo.get("mongodb", "username"))

    def getdatabasename(self):
        """"""
        self.database = self.databaseLineEdit.text()

    def getdatabaseport(self):
        """"""
        self.databaseport = self.databaseportLineEdit.text()

    def getdatabasehost(self):
        """"""
        self.databasehost = self.databaseHostLineEdit.text()

    def getdatabasepw(self):
        """"""
        self.databasepw = self.databasePwLineEdit.text()

    def showmasarconfigs(self):
        """"""
        result = None
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite
            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigs(conn, servicename="masar")
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo
            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
            resultdict = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            result = [['id', 'name', 'desc', 'date', 'version', 'status']]
            for res in resultdict:
                result.append([res['configidx'],
                               res['name'],
                               res['desc'],
                               res['created_on'],
                               res['version'],
                               res['status']])
                               # res['system']

        if result is not None:
            self._setconfigtable(result)

    def choosedbsrc(self, bool):
        """Choose DB source"""
        if bool:
            self.usedefaultdb = True
        else:
            self.usedefaultdb = False

    def _setconfigtable(self, content):
        """"""
        # head = self.masarConfigTableWidget.horizontalHeader()
        self.masarConfigTableWidget.clearContents()
        # self.masarConfigTableWidget.setHorizontalHeaderLabels(head)

        if len(content) > 1:
            self.masarConfigTableWidget.setRowCount(len(content)-1)

            n = 0
            data = sorted(content[1:], key=itemgetter(0), reverse=True)
            for res in data:
                m = 0
                for item in res:
                    if not isinstance(item, basestring):
                        item = str(item)
                    if item:
                        if m == 5:
                            newitem = QtGui.QComboBox()
                            newitem.addItem("active")
                            newitem.addItem("inactive")
                            if item == "active":
                                newitem.setCurrentIndex(0)
                            else:
                                newitem.setCurrentIndex(1)

                            newitem.row = n
                            newitem.column = m
                            self.masarConfigTableWidget.setCellWidget(n, m, newitem)
                            self.comboxboxSignalMapper.setMapping(newitem, newitem)
                            newitem.currentIndexChanged.connect(self.comboxboxSignalMapper.map)

                            updatebutton = QtGui.QPushButton()
                            updatebutton.setText("Update")
                            updatebutton.setEnabled(False)
                            updatebutton.row = n
                            updatebutton.column = m+1
                            self.pushbuttonSignalMapper.setMapping(updatebutton, updatebutton)
                            self.masarConfigTableWidget.setCellWidget(n, m+1, updatebutton)
                            updatebutton.clicked.connect(self.pushbuttonSignalMapper.map)
                        else:
                            newitem = QTableWidgetItem(item)
                            newitem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                            self.masarConfigTableWidget.setItem(n, m, newitem)
                    m += 1
                n += 1

            self.masarConfigTableWidget.resizeColumnsToContents()

    def updatemasarconfigstatus(self, configstatus, cid, configname=None):
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            pymasarsqlite.service.updateServiceConfigStatus(conn, cid, status=configstatus)
            pymasarsqlite.utils.save(conn)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
            pymasarmongo.pymasarmongo.pymasar.updateconfig(mongoconn, collection, configname,
                                                           configidx=int(cid), status=configstatus)
            pymasarmongo.db.utils.close(mongoconn)

        return True

    def addnewpvgrouprow(self):
        """Add a new row to add pv group to MASAR configuration"""
        print ("""Add a new row to add pv group to MASAR configuration""")
        currowcount = self.newConfigTableWidget.rowCount()
        self.newConfigTableWidget.setRowCount(currowcount + 1)

        showpvbutton = QtGui.QPushButton()
        showpvbutton.setText("Show PVs")
        showpvbutton.setEnabled(True)
        showpvbutton.row = currowcount
        showpvbutton.column = 2
        self.showpvbuttonSignalMapper.setMapping(showpvbutton, showpvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount, showpvbutton.column, showpvbutton)
        showpvbutton.clicked.connect(self.showpvbuttonSignalMapper.map)

        choosepvbutton = QtGui.QPushButton()
        choosepvbutton.setText("PV File")
        choosepvbutton.setEnabled(True)
        choosepvbutton.row = currowcount
        choosepvbutton.column = 3
        self.choosepvbuttonSignalMapper.setMapping(choosepvbutton, choosepvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount, choosepvbutton.column, choosepvbutton)
        choosepvbutton.clicked.connect(self.choosepvbuttonSignalMapper.map)

    def removepvgrouprow(self):
        """Remove selected pv group from the configuration to be added into MASAR database"""
        if self.currentselectedrow4config == -1:
            raise RuntimeError("No pv group selected.")
        rownametobedelete = self.newConfigTableWidget.item(self.currentselectedrow4config, 0)
        if rownametobedelete is not None:
            rownametobedelete = rownametobedelete.text()
        self.newConfigTableWidget.removeRow(self.currentselectedrow4config)
        if rownametobedelete is not None:
            print ("Successfully delete pv group: ", rownametobedelete)
        else:
            print ("Successfully delete row: ", self.currentselectedrow4config)
        self.currentselectedrow4config = -1

    def savemasarsqlite(self):
        """"""
        # get data from sqlite
        if self.usedefaultdb:
            masardb = str(self.databaseDefault.toPlainText())
        else:
            masardb = str(self.databaseLineEdit.text())

        if masardb != "":
            os.environ["MASAR_SQLITE_DB"] = masardb
        else:
            QMessageBox.warning(self, "Warning",
                                "Cannot find MASAR SQLite Database")
            return

        import pymasarsqlite
        conn = pymasarsqlite.utils.connect()
        existedresult = pymasarsqlite.service.retrieveServiceConfigs(conn, servicename="masar")

        newcfgdata = self._getnewconfigurationdata(existedresult)
        if newcfgdata is None:
            QMessageBox.warning(self, "Warning",
                                "Not enough information for a new configuration.")
            return
        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]

        if newcfgname is None or msystem is None or newcfgdata is None:
            # Nothing to be added.
            QMessageBox.warning(self, "Warning",
                                "No name or system is given, or empty configuration data.")
            return

        for i in range(len(cfgdata[0])):
            if cfgdata[0][i] is None:
                QMessageBox.warning(self, "Warning", "Wrong PV group name")
                return

            if cfgdata[2][i] is not None and os.path.isfile(cfgdata[2][i]):
                pvs = list(np.loadtxt(cfgdata[2][i], dtype=str, comments="#"))
                if len(pvs) > 0:
                    for j, pv in enumerate(pvs):
                        pvs[j] = pv.strip()
                    pymasarsqlite.pvgroup.savePvGroup(conn, cfgdata[0][i], func=cfgdata[1][i])
                    pymasarsqlite.pvgroup.saveGroupPvs(conn, cfgdata[0][i], pvs)

        try:
            pymasarsqlite.service.saveServiceConfig(conn, "masar", newcfgname, configdesc=desc, system=msystem)
            pymasarsqlite.service.saveServicePvGroup(conn, newcfgname, cfgdata[0])
        except Exception as e:
            QMessageBox.warning(self, "Error", e.message)
            return

        pymasarsqlite.utils.save(conn)
        QMessageBox.information(self, "Congratulation", "A new configuration has been added successfully.")
        pymasarsqlite.utils.close(conn)

    def savemasarmongodb(self):
        """"""
        # get data from mongodb
        if self.usedefaultdb:
            database = str(self.databaseDefault.toPlainText())
            host = str(self.hostDefault.toPlainText())
            port = str(self.portDefault.toPlainText())
        else:
            database = str(self.databaseLineEdit.text())
            host = str(self.databaseHostLineEdit.text())
            port = str(self.databasePortLineEdit.text())

        import pymasarmongo

        mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
        existedresult = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
        existedcfg = []
        for res in existedresult:
            existedcfg.append([res['configidx'],
                               res['name'],
                               res['desc'],
                               res['created_on'],
                               res['version'],
                               res['status']])
        newcfgdata = self._getnewconfigurationdata(existedcfg)
        if newcfgdata is None:
            # Nothing to be added.
            raise ValueError("Empty configuration.")

        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]
        pvs = []
        for pvf in cfgdata[2]:
            if pvf is not None and os.path.isfile(pvf):
                pvs += list(np.loadtxt(pvf, dtype=str, comments="#"))
        if pvs:
            for i, pv in enumerate(pvs):
                pvs[i] = pv.strip()
            pymasarmongo.pymasarmongo.pymasar.saveconfig(mongoconn, collection, newcfgname,
                                                         desc=desc,
                                                         system=msystem,
                                                         pvlist={"names": pvs})
            QMessageBox.information(self, "Congratulation", "A new configuration has been added successfully.")
        else:
            QMessageBox.warning(self, "Warning", "No PVs available for the new configuration.")

        pymasarmongo.db.utils.close(mongoconn)

    def submitmasarconfig(self):
        """submit a new configuration to MASAR database"""
        if self.dbsource is None:
            QMessageBox.warning(self, "Warning",
                                "Unknown database source.\nPlease select which database should be use.")
            return
        if self.dbsource == 0:
            self.savemasarsqlite()
        elif self.dbsource == 1:
            self.savemasarmongodb()

    def _getnewconfigurationdata(self, existedresult):
        """"""
        newcfgname = self.newConfigurationLineEdit.text()
        if newcfgname is None or str(newcfgname) == "":
            QMessageBox.warning(self, "Warning",
                                "Name of configuration is empty.")
            return None
        elif str(newcfgname) in np.array(existedresult)[:, 1]:
            QMessageBox.warning(self, "Warning",
                                "Configuration (%s) exists already." % str(newcfgname))
            return None
        else:
            newcfgname = str(newcfgname)
        desc = self.newConfigurationDescription.text()
        if str(desc) == "":
            desc = None
        else:
            desc = str(desc)

        msystem = str(self.systemComboBox.currentText())
        if msystem == "Others":
            msystem = self.systemLineEdit.text()
            if msystem is None or str(msystem) == "":
                QMessageBox.warning(self, "Warning",
                                    "System for configuration (%s) not specified yet." % str(newcfgname))
                return None
            else:
                msystem = str(msystem)

        pvgroups = self.newConfigTableWidget.rowCount()
        if pvgroups == 0:
            QMessageBox.warning(self, "Warning",
                                "No PV founded for new configuration (%s)." % str(newcfgname))
            return None

        pvgroupnames = []
        pvgroupdescs = []
        pvgroupfiles = []
        for count in range(pvgroups):
            # Collect PV group name information
            if self.newConfigTableWidget.item(count, 0) is not None and str(self.newConfigTableWidget.item(count, 0).text()) != "":
                pvgname = str(self.newConfigTableWidget.item(count, 0).text())
                if pvgname in pvgroupnames:
                    QMessageBox.warning(self, "Warning",
                                        "Duplicated pv group name: %s." % str(pvgname))
                    return None
                pvgroupnames.append(pvgname)
            elif self.newConfigTableWidget.item(count, 4) is None or str(self.newConfigTableWidget.item(count, 4)) == "":
                continue
            elif self.dbsource == 1:
                pvgroupnames.append(None)
            else:
                QMessageBox.warning(self, "Warning",
                                    "Empty pv group name.")
                return None
                # reply = QMessageBox.question(self, "Message",
                #                              "pv group name not specified for row {}.\nContinue?".format(count),
                #                              QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                # if reply == QMessageBox.Yes:
                #     pvgroupnames.append(None)
                # else:
                #     return None

            # Collection PV group descriptions
            if self.newConfigTableWidget.item(count, 1) is not None:
                pvgroupdescs.append(str(self.newConfigTableWidget.item(count, 1).text()))
            else:
                pvgroupdescs.append(None)

            # PV files for the pv group.
            if self.newConfigTableWidget.item(count, 4) is not None:
                pvgroupfiles.append(str(self.newConfigTableWidget.item(count, 4).text()))
            else:
                pvgroupfiles.append(None)

        if pvgroupfiles:
            return newcfgname, desc, msystem, [pvgroupnames, pvgroupdescs, pvgroupfiles]
        else:
            return None

    def currentselectedrow(self, row, col):
        """Cache current selected row in MASAR configuration Table Widget."""
        self.currentselectedrow4config = row

    def updateselectedsystem(self, system):
        """Update selected system if value changed"""
        self.selectedsystem = str(system)

    def updatesystemcombobox(self):
        """Update selected system if value changed"""
        self.systemComboBox.clear()
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigProps(conn, propname="system", servicename="masar")
            index = 0
            if len(result) > 1:
                res = sorted(set(list(np.array(result[1:])[:, 3])))
                #for res in result[1:]:
                self.systemComboBox.addItems(res)
                index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)

            result = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            results = []
            for res in result:
                if res["system"] not in results:
                    results.append(res["system"])
            res = sorted(set(results))
            self.systemComboBox.addItems(res)
            index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)

    def listpvgroups(self):
        """"""
        self.pvgroupmodel.clear()
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.pvgroup.retrievePvGroups(conn)
            if len(result) > 0:
                result = sorted(result, key=itemgetter(0), reverse=True)
            for res in result:
                parent1 = QStandardItem(res[1])
                child1 = QStandardItem('id: {}'.format(res[0]))
                child2 = QStandardItem('description: {}'.format(res[2]))
                child3 = QStandardItem('date: {}'.format(res[3]))
                child4 = QStandardItem('version: {}'.format(res[4]))
                parent1.appendColumn([child1, child2, child3, child4])
                self.pvgroupmodel.appendRow(parent1)
            selmod = self.pvGroupTreeView.selectionModel()
            index2 = self.pvgroupmodel.indexFromItem(child3)
            selmod.select(index2, QItemSelectionModel.Select | QItemSelectionModel.Rows)
            pymasarsqlite.utils.close(conn)

            self.pvGroupTreeView.setContextMenuPolicy(Qt.CustomContextMenu)
            self.pvGroupTreeView.clicked.connect(self.showpvsinpvgroup)
            # self.connect(self.pvGroupTreeView,
            #              QtCore.SIGNAL("clicked(QModelIndex)"),
            #              #QtCore.SIGNAL("customContextMenuRequested(const QPoint &)"),
            #              self.doMenu)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

    def showpvsinpvgroup(self, point):
        if point.model().itemFromIndex(point).child(0) is None:
            return
        reply = QMessageBox.question(self, 'Message',
                                     "show all pvs belong to group {} ?".format(point.model().
                                                                                itemFromIndex(point).
                                                                                text()),
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            pvgroupidx = int(str(point.model().itemFromIndex(point).child(0).text().split(":")[1]).strip())

            # msg = QMessageBox(self)
            msg = ShowPvMessageBox()
            msg.setWindowTitle('PVs for group {}'.format(point.model().itemFromIndex(point).text()))
            msg.setText("Click show details to see all pvs.")
            # windowTitle = 'PVs for group {}'.format(point.model().itemFromIndex(point).text())

            if self.dbsource == 0:
                # get data from sqlite
                if self.usedefaultdb:
                    # masardb = str(self.databaseDefault.text())
                    masardb = str(self.databaseDefault.toPlainText())
                else:
                    masardb = str(self.databaseLineEdit.text())

                if masardb != "":
                    os.environ["MASAR_SQLITE_DB"] = masardb
                else:
                    raise RuntimeError("Cannot find MASAR SQLite Database")

                import pymasarsqlite

                conn = pymasarsqlite.utils.connect()
                result = pymasarsqlite.pvgroup.retrieveGroupPvs(conn, pvgroupidx)
                text = "\n".join(list(np.array(result)[:, 0]))
                msg.setDetailedText(text)
                msg.exec_()

            elif self.dbsource == 1:
                # get data from mongodb
                if self.usedefaultdb:
                    database = str(self.databaseDefault.toPlainText())
                    host = str(self.hostDefault.toPlainText())
                    port = str(self.portDefault.toPlainText())
                else:
                    database = str(self.databaseLineEdit.text())
                    host = str(self.databaseHostLineEdit.text())
                    port = str(self.databasePortLineEdit.text())

                import pymasarmongo
Esempio n. 3
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.newProjectButton.pressed.connect(self.newproject)
        self.removeProjectButton.pressed.connect(self.deletebuttonpressed)
        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectsaved.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.updateformsnode)
        self.projectwidget.selectlayersupdated.connect(self.updateformsnode)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.newproject.connect(self.newproject)
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

    def updateformsnode(self, *args):
        haslayers = self.projectwidget.checkcapturelayers()
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.nodetype == Treenode.FormsNode:
            self.newProjectButton.setEnabled(haslayers)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        self.bar.pushError('Seems something has gone wrong. Press for more details',
                                  info)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

    def newproject(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node and node.canadd:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)

    def deletebuttonpressed(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        self.projectwidget.setpage(node.page)
        self.removeProjectButton.setEnabled(node.canremove)
        self.newProjectButton.setEnabled(node.canadd)
        #self.newProjectButton.setText(node.addtext)
        #self.removeProjectButton.setText(node.removetext)

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.FormNode:
            self.projectwidget.setform(node.form)
        elif node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")
        elif node.nodetype == Treenode.MapNode:
            self.projectwidget.loadmap()
        elif node.nodetype == Treenode.FormsNode:
            haslayers = self.projectwidget.checkcapturelayers()
            self.newProjectButton.setEnabled(haslayers)


        self.projectwidget.projectbuttonframe.setVisible(not project is None)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        self.treemodel.dataChanged.emit(index, index)
Esempio n. 4
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp
        self.reloadingProject = False
        self.loadedProject = None

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.projectLoaded)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

        # ## If all the layers are gone we need to reset back to the top state to get the widgets
        # ## back into sync.
        # QgsMapLayerRegistry.instance().removeAll.connect(self.reset_project)

    # def reset_project(self):
    #     print(QgsProject.instance().fileName())
    #     print("ALL REMOVED")
    #     node = self.projectsnode.find_by_filename(QgsProject.instance().fileName())

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def projectLoaded(self, project):
        roam.utils.info("Project loaded: {}".format(project.name))
        node = self.projectsnode.find_by_name(project.name)
        node.create_children()
        self.projectwidget.setpage(node.page, node, refreshingProject=True)
        self.reloadingProject = False
        self.loadedProject = project
        self.projectList.setExpanded(node.index(), True )

    def nodeselected(self, index, last, reloadProject=False):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project

        if project:
            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)
                return

        if node.nodetype == Treenode.ProjectNode and reloadProject:
            self.reloadingProject = True
            self.projectwidget.setproject(project)
            return

        if project and self.loadedProject != project:
            # Only load the project if it's different the current one.
            roam.utils.info("Swapping to project: {}".format(project.name))
            if self.loadedProject:
                lastnode = self.projectsnode.find_by_name(self.loadedProject.name)
                self.projectList.setExpanded(lastnode.index(), False )
            ## We are closing the open project at this point. Watch for null ref after this.
            self.projectwidget.setproject(project)
            return
        else:
            roam.utils.debug("Setting page")
            self.projectwidget.setpage(node.page, node)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)


    def projectupdated(self, project):
        # index = self.projectList.currentIndex()
        # node = find_node(index)
        # node.refresh()
        print "PROJECT UPDATED"
        node = self.projectsnode.find_by_name(project.name)
        self.projectList.selectionModel().select(node.index(), QItemSelectionModel.ClearAndSelect)
        self.nodeselected(node.index(), None, reloadProject=True)
Esempio n. 5
0
class LandmarkToolbox(QDockWidget, Ui_DockWidget):
    landmarkMessage = pyqtSignal(unicode, int)

    def __init__(self, iface):
        QDockWidget.__init__(self)
        self.setupUi(self)

        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.geoCrs = QgsCoordinateReferenceSystem(4326)

        self.btnAddPhoto.setIcon(QIcon(':/icons/camera.svg'))

        self.txtPhotoComment.setPlaceholderText(self.tr('Comment'))
        self.cmbLayers.setFilters(QgsMapLayerProxyModel.VectorLayer)

        self.db = QSqlDatabase.addDatabase('QPSQL')
        self.landmarkId = None
        self.photoId = None
        self.highlight = None

        self.model = QStandardItemModel()
        self.lstPhotos.setModel(self.model)

        self.btnUpdateLandmark.clicked.connect(self.saveLandmark)
        self.btnDeleteLandmark.clicked.connect(self.deleteLandmark)
        self.btnAddPhoto.clicked.connect(self.addPhoto)
        self.btnUpdatePhoto.clicked.connect(self.savePhoto)
        self.btnDeletePhoto.clicked.connect(self.removePhoto)
        self.lstPhotos.selectionModel().selectionChanged.connect(
            self.photoSelected)
        self.lstPhotos.doubleClicked.connect(self.showPhoto)

        self._enableOrDisableButtons()
        self.ToggleToolbox()

    def ToggleToolbox(self):
        layer_list = self.canvas.layers()

        if not layer_list:
            self.hide()
            return
        elif len(layer_list) == 0:
            self.hide()
            return

        self.setVisible(not self.isVisible())

    def getLandmarkID(self):
        #ランドマークがなかった時の処理
        return self.landmarkId

    def openDatabase(self):
        if self.db.isValid():
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            dbHostName = settings.value('hostname')
            dbDatabaseName = settings.value('databasename')
            dbUserName = settings.value('username')
            dbPassword = settings.value('dbpassword')

            self.db.setHostName(dbHostName)
            self.db.setDatabaseName(dbDatabaseName)
            self.db.setUserName(dbUserName)
            self.db.setPassword(dbPassword)

            if not self.db.open():
                self.GKukanMusiumMessage.emit(
                    self.tr('Can not open GKukanMusium database'),
                    QgsMessageBar.WARNING)
                return False

            self.query = QSqlQuery(self.db)
            return True
        else:
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            dbHostName = settings.value('hostname')
            dbDatabaseName = settings.value('databasename')
            dbUserName = settings.value('username')
            dbPassword = settings.value('dbpassword')

            self.db.removeDatabase(dbDatabaseName)
            del self.db
            self.db = None
            self.db = QSqlDatabase.addDatabase('QPSQL')

            self.db.setHostName(dbHostName)
            self.db.setDatabaseName(dbDatabaseName)
            self.db.setUserName(dbUserName)
            self.db.setPassword(dbPassword)

            if not self.db.open():
                self.GKukanMusiumMessage.emit(
                    self.tr('Can not open GKukanMusium database'),
                    QgsMessageBar.WARNING)
                return False

            self.query = QSqlQuery(self.db)
            return True

        return False

    def GetPhotoFolderPath(self):
        if not self.openDatabase():
            return False

        if self.query.exec_(u'select * from m_folder'):
            self.query.first()
            self.folderpath = self.query.value(2)
            self.thumbpath = os.path.join(self.folderpath, 'thumb')
            ret = self.folderpath
        else:
            ret = ''

        self.db.close()

        return ret

    def landmarkSelected(self, infos):
        self.info = infos[0]
        ft = self.info[1]
        self.landmarkId = ft['id']

        self.leLandmarkTitle.setText(ft['title'] if ft['title'] else '')
        self.spnLandmarkClass.setValue(
            ft['icon_type'] if ft['icon_type'] != None else 0)

        self._highlightLandmark()
        self.populatePhotos()
        self._enableOrDisableButtons()

    def populatePhotos(self, index=0):
        self.model.clear()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        # photos is a list of tuples (id, title, imagepath)
        photos = self._photosOfLandmark()
        for i in photos:

            tp = os.path.join(self.thumbpath, str(i[0])) + '.png'

            img = self.thumbnailPhoto(i[2], tp)

            icon = QIcon(img)

            title = i[1] if i[1] else '<unnamed photo> %s' % i[0]

            item = QStandardItem(title)
            item.setIcon(icon)
            item.setData(i[0], Qt.UserRole)
            item.setToolTip(title)
            self.model.appendRow(item)
            lastIdx = self.model.indexFromItem(item)

        idx = self.model.createIndex(0, 0)
        if self.model.rowCount() > 0:
            if index == -1:
                idx = lastIdx
            elif index > 0:
                idx = self.model.createIndex(index, 0)
            self.lstPhotos.selectionModel().select(idx,
                                                   QItemSelectionModel.Select)
        else:
            self._clearForm()

        QApplication.restoreOverrideCursor()

    def thumbnailPhoto(self, imagePath, tp):

        if os.path.exists(tp):
            return QPixmap(tp)
        else:
            if os.path.exists(os.path.dirname(tp)) == False:
                os.mkdir(os.path.dirname(tp))
            pixmap = QPixmap(imagePath).scaled(800, 600).scaled(
                75, 50, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
            a = pixmap.save(tp, 'PNG')
            return pixmap

    def showPhoto(self, index):
        if not self.openDatabase():
            return

        item = self.lstPhotos.model().itemFromIndex(index)
        self.query.prepare(
            'SELECT filename ,ST_X(geom),ST_Y(geom),geom FROM t_photo WHERE p_id=?;'
        )
        self.query.addBindValue(item.data(Qt.UserRole))
        if self.query.exec_():
            self.query.first()
            path = os.path.join(self.folderpath, self.query.value(0))
            if self.query.value(
                    3) <> '010100000000000000000000000000000000000000':
                lon = self.query.value(1)
                lat = self.query.value(2)
                point = self._transformPoint(QgsPoint(lon, lat))
                self.canvas.freeze(True)
                self.canvas.setCenter(point)
                self.canvas.freeze(False)
                self.canvas.refresh()

            dlg = ViewPhotoDialog(path)
            dlg.exec_()

        else:
            a = self.query.lastError().text()

        self.db.close()

    def saveLandmark(self):
        layer = self.info[0]
        fid = self.info[1].id()

        idxTitle = layer.fieldNameIndex('title')
        idxClassification = layer.fieldNameIndex('icon_type')

        attrs = {idxTitle: self.leLandmarkTitle.text(),\
                 idxClassification: self.spnLandmarkClass.value()
                }

        layer.dataProvider().changeAttributeValues({fid: attrs})
        layer.reload()
        layer.triggerRepaint()

        self.landmarkMessage.emit(self.tr('Landmark updated.'),
                                  QgsMessageBar.INFO)

    def deleteLandmark(self):
        layer = self.info[0]
        fid = self.info[1].id()

        layer.dataProvider().deleteFeatures([fid])
        layer.reload()
        layer.triggerRepaint()

        self._clearAllFields()

        self.landmarkMessage.emit(self.tr('Landmark deleted.'),
                                  QgsMessageBar.INFO)

    def addPhoto(self):
        if self.landmarkId is not None:
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            lastDir = settings.value('lastPhotoDir', '.')

            fileName = QFileDialog.getOpenFileName(self,
                                                   self.tr('Select photo'),
                                                   lastDir,
                                                   self._createFilter())

            if fileName == '':
                return

            settings.setValue('lastPhotoDir',
                              QFileInfo(fileName).absoluteDir().absolutePath())

            projectPath = self.GetPhotoFolderPath() + os.sep
            photoPath = os.path.basename(fileName)
            photoDate = self._photoDate(fileName).toString('yyyy-MM-dd')

            if not self.openDatabase():
                return

            self.query.prepare(
                'INSERT INTO t_photo("cdate", "filename", "landmark_id",lon,lat,angle,geomtype,geom) VALUES(?, ?, ?,?,?,?,?,?);'
            )
            self.query.addBindValue(photoDate)
            self.query.addBindValue(photoPath)
            self.query.addBindValue(self.landmarkId)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(
                '010100000000000000000000000000000000000000')
            if self.query.exec_():
                self._copyPhotoToFolder(fileName, self.landmarkId)
                self.populatePhotos(-1)
            else:
                a = self.query.lastError().text()

            self.db.close()
        else:
            self.landmarkMessage.emit(
                self.tr('Select landmark before adding a photo.'),
                QgsMessageBar.WARNING)

    def savePhoto(self):
        if not self.openDatabase():
            return

        self.query.prepare(
            'UPDATE t_photo SET film_no=?, keywords=?, keyword1=?, keyword2=?, keyword3=?, notes=?, mdate=?, registrant=?, comment=?, reference=?, angle=? WHERE p_id=?;'
        )
        self.query.addBindValue(self.lePhotoTitle.text())
        self.query.addBindValue(self.leKeywords.text())
        self.query.addBindValue(self.leKeyword1.text())
        self.query.addBindValue(self.leKeyword2.text())
        self.query.addBindValue(self.leKeyword3.text())
        self.query.addBindValue(self.txtPhotoComment.toPlainText())
        self.query.addBindValue(
            self.edPhotoDate.dateTime().toString('yyyy-MM-dd'))
        self.query.addBindValue(self.leRegistrant.text())
        self.query.addBindValue(self.leComment.text())
        self.query.addBindValue(self.lerRference.text())
        self.query.addBindValue(self.spnPhotoAngle.value())
        self.query.addBindValue(self.photoId)

        if self.query.exec_():
            self.landmarkMessage.emit(self.tr('Photo updated.'),
                                      QgsMessageBar.INFO)
            self.populatePhotos(self.lstPhotos.currentIndex().row())
        else:
            a = self.query.lastError().text()

        self.db.close()

    def removePhoto(self):
        if not self.openDatabase():
            return

        self.query.prepare('DELETE FROM t_photo WHERE "p_id"=?;')
        self.query.addBindValue(self.photoId)
        if self.query.exec_():
            self.db.close()
            self._removePhotofromFolder()

            self.populatePhotos()

    def photoSelected(self, current, previous):
        if not self.openDatabase():
            return

        idx = current.indexes()[0]
        item = self.lstPhotos.model().itemFromIndex(idx)
        self.photoId = item.data(Qt.UserRole)

        self.query.prepare(
            'SELECT film_no, filename, keywords, keyword1, keyword2, keyword3, notes, mdate, registrant, comment, reference, angle FROM t_photo WHERE p_id=?;'
        )
        self.query.addBindValue(self.photoId)
        if self.query.exec_():
            self.query.first()
            self.filename = self.query.value(1)
            self.lePhotoTitle.setText(
                self.query.value(0) if self.query.value(0) else '')
            self.txtPhotoComment.setPlainText(
                self.query.value(6) if self.query.value(6) else '')
            self.leKeywords.setText(
                self.query.value(2) if self.query.value(2) else '')
            self.leKeyword1.setText(
                self.query.value(3) if self.query.value(3) else '')
            self.leKeyword2.setText(
                self.query.value(4) if self.query.value(4) else '')
            self.leKeyword3.setText(
                self.query.value(5) if self.query.value(5) else '')
            self.leRegistrant.setText(
                self.query.value(8) if self.query.value(8) else '')
            self.leComment.setText(
                self.query.value(9) if self.query.value(9) else '')
            self.lerRference.setText(
                self.query.value(10) if self.query.value(10) else '')
            self.spnPhotoAngle.setValue(
                int(self.query.value(11)) if self.query.value(11) else 0)
            self.edPhotoDate.setDateTime(
                self.query.value(7) if self.query.value(7) else QDateTime.
                currentDateTime())

        self._enableOrDisableButtons()

        self.db.close()

    def _photosOfLandmark(self):

        projectPath = self.GetPhotoFolderPath()

        if not self.openDatabase():
            return

        photos = []
        self.query.prepare(
            'SELECT "p_id", "keywords", "filename" FROM t_photo WHERE "landmark_id"=? ORDER BY "p_id";'
        )
        self.query.addBindValue(self.landmarkId)
        if self.query.exec_():
            while self.query.next():
                photos.append((self.query.value(0), self.query.value(1),
                               os.path.join(projectPath, self.query.value(2))))

        self.db.close()

        return photos

    def _createFilter(self):
        formats = ''
        for f in QImageReader.supportedImageFormats():
            f = unicode(f)
            if f == 'svg':
                continue
            formats += '*.{} *.{} '.format(f.lower(), f.upper())

        return self.tr('Image files (%s);;All files (*.*)' % formats[:-1])

    def _clearForm(self):
        self.lePhotoTitle.clear()
        self.txtPhotoComment.clear()
        self.leKeyword1.clear()
        self.leKeyword2.clear()
        self.leKeyword3.clear()
        self.leRegistrant.clear()
        self.leComment.clear()
        self.lerRference.clear()

        self.photoId = None
        self.landmarkId = None

    def _enableOrDisableButtons(self):
        if self.landmarkId is None:
            self.btnAddPhoto.setEnabled(False)
        else:
            self.btnAddPhoto.setEnabled(True)

        if self.photoId is None:
            self.btnDeletePhoto.setEnabled(False)
            self.btnUpdatePhoto.setEnabled(False)
        else:
            self.btnDeletePhoto.setEnabled(True)
            self.btnUpdatePhoto.setEnabled(True)

    def _highlightLandmark(self):
        self._clearHighlight()

        self.highlight = QgsHighlight(self.canvas, self.info[1].geometry(),
                                      self.info[0])

        settings = QSettings()
        color = QColor(
            settings.value('/Map/highlight/color',
                           QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = settings.value('/Map/highlight/colorAlpha',
                               QGis.DEFAULT_HIGHLIGHT_COLOR.alpha(),
                               type=int)
        buffer = settings.value('/Map/highlight/buffer',
                                QGis.DEFAULT_HIGHLIGHT_BUFFER_MM,
                                type=float)
        minWidth = settings.value('/Map/highlight/minWidth',
                                  QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM,
                                  type=float)

        self.highlight.setColor(color)
        color.setAlpha(alpha)
        self.highlight.setFillColor(color)
        self.highlight.setBuffer(buffer)
        self.highlight.setMinWidth(minWidth)
        self.highlight.show()

    def _photoDate(self, path):
        with open(path, 'rb') as imgFile:
            tags = exifread.process_file(imgFile, details=False)

        if 'EXIF GPS GPSDate' in tags:
            return QDateTime.fromString(tags['EXIF GPS GPSDate'].values,
                                        'yyyy:MM:dd')
        else:
            return QDateTime.currentDateTime()

    def _clearHighlight(self):
        if hasattr(self, 'highlight'):
            del self.highlight
            self.highlight = None

    def _clearAllFields(self):
        self.leLandmarkTitle.clear()
        self.spnLandmarkClass.clear()

        self._clearHighlight()
        self._clearForm()
        self.model.clear()

    def _copyPhotoToFolder(self, path, landmark_id):
        projectPath = self.GetPhotoFolderPath()
        dst = os.path.join(projectPath, os.path.basename(path))

        shutil.copy2(path, dst)

    def _removePhotofromFolder(self, path, landmark_id):
        projectPath = self.GetPhotoFolderPath()
        dst = os.path.join(projectPath, path)

        os.remove(dst)

    def _transformPoint(self, pnt):
        crsDest = self.canvas.mapSettings().destinationCrs()
        xform = QgsCoordinateTransform(self.geoCrs, crsDest)
        p2 = xform.transform(pnt)
        return p2
Esempio n. 6
0
class TreeView(QTreeView):
    def __init__(self, parent):
        QTreeView.__init__(self, parent)
        while not isinstance(parent, QDialog) and not isinstance(parent, QMainWindow):
            parent = parent.parent()
        self.setObjectName("TreeView" + str(len(parent.findChildren(TreeView))))

        # self.setObjectName("TreeViewWidget")
        # self.hLayout = QHBoxLayout(self)
        # self.hLayout.setObjectName("hLayout")
        # 
        # sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        # sizePolicy.setHorizontalStretch(0)
        # sizePolicy.setVerticalStretch(0)
        # sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        # self.setSizePolicy(sizePolicy)
        # 
        # # self.frame = Frame()
        # self = QTreeView(self)
        # self.hLayout.addWidget(self)

        self.stdModel = QStandardItemModel()
        self.setModel(self.stdModel)

        self.hasObject = False
        self.treeNodeList = []

        self.checkBoxList = []
        self.setHeaderHidden(True)
        # self.stdModel.appendRow(TreeNode("P"))
        # rootIndex = self.rootIndex()

        # rootItem = self.stdModel.itemFromIndex(rootIndex)
        # rootItem.setText("Root")

    def mouseMoveEvent(self, mouseEvent):
        pt = mouseEvent.pos()
        pass
    def Clear(self):
        self.stdModel.clear()
        self.hasObject = False
        self.treeNodeList = []
    def Add(self, caption):
        item = TreeNode(caption)
        if len(self.treeNodeList) > 0:
            item.PrevNode = self.treeNodeList[len(self.treeNodeList) - 1]
            item.PrevNode.NextNode = item
            item.Index = len(self.treeNodeList)
        # item.nodeIndex = len(self.treeNodeList)
        self.treeNodeList.append(item)
        self.stdModel.appendRow(item)
        return item
    def RemoveNode(self, i):
        self.stdModel.removeRow(i)
        self.treeNodeList.pop(i)
        for j in range(i, len(self.treeNodeList)):
            self.treeNodeList[j].nodeIndex -= 1
    def Remove(self, item):
        removedIndex = self.treeNodeList.index(item)
        if removedIndex == 0:
            self.treeNodeList[1].PrevNode = None
        elif removedIndex == len(self.treeNodeList) - 1:
            self.treeNodeList[len(self.treeNodeList) - 2].NextNode = None
        else:
            self.treeNodeList[removedIndex + 1].PrevNode = self.treeNodeList[removedIndex - 1]
            self.treeNodeList[removedIndex - 1].NextNode = self.treeNodeList[removedIndex + 1]
        self.treeNodeList.pop(removedIndex)
        self.stdModel.removeRow(removedIndex)
        i = 0
        for node in self.treeNodeList:
            node.Index = i
            node.LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            i += 1
    def Insert(self, index, text):
        if index == 0 and len(self.treeNodeList) == 0:
            self.Add(text)
            return
        node = TreeNode(text)
        node.Parent = self
        self.treeNodeList.insert(index, node)
        i = 0
        for node0 in self.treeNodeList:
            node0.Index = i
            i += 1
        if index == 0:
            self.treeNodeList[index].PrevNode = None
            if len(self.treeNodeList) == 1:
                self.treeNodeList[index].NextNode = None
                self.treeNodeList[index].LastNode = self.treeNodeList[index]
            else:
                self.treeNodeList[index].NextNode = self.treeNodeList[index + 1]
                self.treeNodeList[index].LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            
            self.treeNodeList[index + 1].PrevNode = self.treeNodeList[index]
            
        else:
            self.treeNodeList[index].PrevNode = self.treeNodeList[index - 1]
            self.treeNodeList[index].NextNode = self.treeNodeList[index + 1]
            self.treeNodeList[index].LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            
            self.treeNodeList[index + 1].PrevNode = self.treeNodeList[index]
            self.treeNodeList[index - 1].NextNode = self.treeNodeList[index]
            

        self.stdModel.insertRow(index, node)
        return node

    def get_Items(self):
        return self.treeNodeList
    Nodes = property(get_Items, None, None, None)

    def Node(self, index):
        if not self.stdModel.rowCount() > 0:
            return None
        return self.treeNodeList[index]

    def getSelectedNode(self):
        if not self.stdModel.rowCount() > 0:
            return None
        index = self.currentIndex()
        return self.stdModel.itemFromIndex(index)
    def setSelectedNode(self, node):
        if not self.stdModel.rowCount() > 0:
            return
        # self.s
        index = self.stdModel.indexFromItem(node)
        self.setCurrentIndex(index)
        # self.treeNodeList.pop(index)
        # self.treeNodeList.insert(index, node)
        # self.stdModel.setItem(index, node)

    SelectedNode = property(getSelectedNode, setSelectedNode, None, None)




    def get_Enabled(self):
        return self.isEnabled()
    def set_Enabled(self, bool):
        self.setEnabled(bool)
    Enabled = property(get_Enabled, set_Enabled, None, None)

    def get_Visible(self):
        return self.isVisible()
    def set_Visible(self, bool):
        self.setVisible(bool)
    Visible = property(get_Visible, set_Visible, None, None)
Esempio n. 7
0
class NeoNavigationDock(QDockWidget, Ui_neoNavigationDock):
    """ Implements a navigation dock for Neo hierarchies. Tightly coupled
    with :class:`main_window_neo.MainWindowNeo`, the main reason for this
    class to exist is to keep the dock out of the general ui file.
    """

    object_removed = pyqtSignal()  # Signal to remove an object

    def __init__(self, parent):
        QDockWidget.__init__(self, parent)
        self.parent = parent

        self.setupUi(self)

        self.block_model = QStandardItemModel()
        self.segment_model = QStandardItemModel()
        self.channelgroup_model = QStandardItemModel()
        self.channel_model = QStandardItemModel()
        self.unit_model = QStandardItemModel()

        self.neoBlockList.setModel(self.block_model)
        self.neoSegmentList.setModel(self.segment_model)
        self.neoChannelGroupList.setModel(self.channelgroup_model)
        self.neoChannelList.setModel(self.channel_model)
        self.neoUnitList.setModel(self.unit_model)

        self.neoBlockList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.block_model))
        self.neoSegmentList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.segment_model))
        self.neoChannelGroupList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channelgroup_model))
        self.neoChannelList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channel_model))
        self.neoUnitList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.unit_model))

        self.neoBlockList.selectionModel().selectionChanged.connect(
            self.selected_blocks_changed)
        self.neoChannelGroupList.selectionModel().selectionChanged.connect(
            self.selected_channel_groups_changed)
        self.neoChannelList.selectionModel().selectionChanged.connect(
            self.selected_channels_changed)
        self.neoUnitList.selectionModel().selectionChanged.connect(
            self.selected_units_changed)
        self.neoSegmentList.selectionModel().selectionChanged.connect(
            self.selected_segments_changed)

    def clear(self):
        """ Clear all lists
        """
        self.neoBlockList.clearSelection()
        self.block_model.clear()

    def get_letter_id(self, id_, small=False):
        """ Return a name consisting of letters given an integer
        """
        if id_ < 0:
            return ''

        name = ''
        id_ += 1
        if small:
            start = ord('a')
        else:
            start = ord('A')

        while id_ > 26:
            id_ -= 1
            name += chr(start + (id_ % 26))
            id_ /= 26

        name += chr(start + id_ - 1)

        return name[::-1]

    def ensure_not_filtered(self, objects, all_objects, filters):
        """ Deactivates all filters that prevent the the given sequence
        of objects to be displayed. The passed filter tuple list is
        modified to only include valid filters.

        :param sequence objects: The objects that need to be visible.
        :param sequence all_objects: The whole object list to be filtered,
            including objects that are allowed to be hidden.
        :param sequence filters: A sequence of (Filter, name) tuples.
        """
        objset = set(objects)
        if not objset.issubset(
                set(self.parent.filter_list(all_objects, filters))):
            i = 1
            while i <= len(filters):
                test_filters = filters[:i]
                if objset.issubset(set(self.parent.filter_list(
                        all_objects, test_filters))):
                    i += 1
                else:
                    test_filters[-1][0].active = False
                    filters.pop(i - 1)

        for o in objects:
            i = 0
            while i < len(filters):
                if self.parent.is_filtered(o, [filters[i]]):
                    filters[i][0].active = False
                    filters.pop(i)
                else:
                    i += 1

    def filter_ordered(self, objects, filters):
        """ Filter a sequence of objects with a sequence of filters. Apply
        the filters in the order given by the sequence. Return the filtered
        list.
        """
        for f in filters:
            if f[0].combined:
                objects = self.parent.filter_list(objects, [f])
            else:
                objects = [o for o in objects
                           if not self.parent.is_filtered(o, [f])]
        return objects

    def populate_neo_block_list(self):
        """ Fill the block list with appropriate entries.
        Qt.UserRole: The :class:`neo.Block` object
        """
        self.block_model.clear()

        filters = self.parent.get_active_filters('Block')

        blocks = self.filter_ordered(
            self.parent.block_names.keys(), filters)
        for b in blocks:
            item = QStandardItem(self.parent.block_names[b])
            item.setData(b, Qt.UserRole)
            self.block_model.appendRow(item)

        self.neoBlockList.setCurrentIndex(self.block_model.index(0, 0))
        self.set_blocks_label()
        if not blocks:
            self.selected_blocks_changed()

    def populate_neo_segment_list(self):
        """ Fill the segment list with appropriate entries.
        Qt.UserRole: The :class:`neo.Segment` object
        """
        self.segment_model.clear()

        segments = []
        for b in self.blocks():
            segments.extend(b.segments)

        filters = self.parent.get_active_filters('Segment')
        segments = self.filter_ordered(segments, filters)
        for i, s in enumerate(segments):
            if s.name:
                name = s.name + ' (%s-%i)' % \
                    (self.parent.block_ids[s.block], i)
            else:
                name = '%s-%i' % (self.parent.block_ids[s.block], i)

            new_item = QStandardItem(name)
            new_item.setData(s, Qt.UserRole)
            self.segment_model.appendRow(new_item)

        self.neoSegmentList.setCurrentIndex(self.segment_model.index(0, 0))
        if api.config.autoselect_segments:
            self.neoSegmentList.selectAll()
        self.selected_segments_changed()

    def populate_neo_channel_group_list(self):
        """ Fill the channel group list with appropriate entries.
        Qt.UserRole: The :class:`neo.RecordingChannelGroup` object
        """
        self.neoChannelGroupList.clearSelection()
        self.channelgroup_model.clear()
        self.parent.channel_group_names.clear()

        rcgs = []
        for b in self.blocks():
            rcgs.extend(b.recordingchannelgroups)

        filters = self.parent.get_active_filters(
            'Recording Channel Group')
        rcgs = self.filter_ordered(rcgs, filters)

        for i, rcg in enumerate(rcgs):
            self.parent.channel_group_names[rcg] = '%s-%s' % (
                self.parent.block_ids[rcg.block],
                self.get_letter_id(i, True))
            if rcg.name:
                name = rcg.name + ' (%s)' % \
                                  self.parent.channel_group_names[rcg]
            else:
                name = self.parent.channel_group_names[rcg]
            new_item = QStandardItem(name)
            new_item.setData(rcg, Qt.UserRole)
            self.channelgroup_model.appendRow(new_item)

        self.neoChannelGroupList.setCurrentIndex(
            self.channelgroup_model.index(0, 0))
        if api.config.autoselect_channel_groups:
            self.neoChannelGroupList.selectAll()
        elif not rcgs:
            self.selected_channel_groups_changed()

    def populate_neo_channel_list(self):
        """ Fill the channel list with appropriate entries. Data slots:
        Qt.UserRole: The :class:`neo.RecordingChannel`
        Qt.UserRole+1: The channel index
        """
        self.channel_model.clear()
        channels = set()

        rcs = []
        rc_group_name = {}
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in channels:
                    continue
                channels.add(rc)
                rcs.append(rc)
                rc_group_name[rc] = self.parent.channel_group_names[rcg]

        filters = self.parent.get_active_filters(
            'Recording Channel')
        rcs = self.filter_ordered(rcs, filters)

        for rc in rcs:
            identifier = '%s.%d' % \
                         (rc_group_name[rc],
                          rc.index)
            if rc.name:
                name = rc.name + ' (%s)' % identifier
            else:
                name = identifier

            new_item = QStandardItem(name)
            new_item.setData(rc, Qt.UserRole)
            new_item.setData(rc.index, Qt.UserRole + 1)
            self.channel_model.appendRow(new_item)

        if api.config.autoselect_channels:
            self.neoChannelList.selectAll()
        self.selected_channels_changed()

    def populate_neo_unit_list(self):
        """ Fill the unit list with appropriate entries.
        Qt.UserRole: The :class:`neo.Unit` object
        """
        self.unit_model.clear()

        units = []
        for rcg in self.recording_channel_groups():
            units.extend(rcg.units)

        filters = self.parent.get_active_filters('Unit')
        units = self.filter_ordered(units, filters)

        for i, u in enumerate(units):
            if self.parent.is_filtered(u, filters):
                continue
            if u.name:
                name = u.name + ' (%s-%d)' % \
                       (self.parent.channel_group_names[rcg], i)
            else:
                name = '%s-%d' % (self.parent.channel_group_names[rcg], i)
            new_item = QStandardItem(name)
            new_item.setData(u, Qt.UserRole)
            self.unit_model.appendRow(new_item)

        if api.config.autoselect_units:
            self.neoUnitList.selectAll()
        self.selected_units_changed()

    def set_blocks_label(self):
        self.blocksLabel.setText(
            'Blocks (%d/%d):' % (len(self.neoBlockList.selectedIndexes()),
                                 self.block_model.rowCount()))

    def set_channel_groups_label(self):
        self.channelGroupsLabel.setText(
            'Channel Groups (%d/%d):' % (
                len(self.neoChannelGroupList.selectedIndexes()),
                self.channelgroup_model.rowCount()))

    def selected_blocks_changed(self):
        self.set_blocks_label()
        self.populate_neo_channel_group_list()
        self.populate_neo_segment_list()

    def selected_channel_groups_changed(self):
        self.set_channel_groups_label()
        self.populate_neo_channel_list()
        self.populate_neo_unit_list()

    def selected_channels_changed(self):
        self.channelsLabel.setText(
            'Channels (%d/%d):' % (
                len(self.neoChannelList.selectedIndexes()),
                self.channel_model.rowCount()))

    def selected_units_changed(self):
        self.unitsLabel.setText(
            'Units (%d/%d):' % (
                len(self.neoUnitList.selectedIndexes()),
                self.unit_model.rowCount()))

    def selected_segments_changed(self):
        self.segmentsLabel.setText(
            'Segments (%d/%d):' % (
                len(self.neoSegmentList.selectedIndexes()),
                self.segment_model.rowCount()))

    def _edit_item_annotations(self, index, model):
        api.annotation_editor(model.data(index, Qt.UserRole))

    def remove_selected(self, list_widget):
        """ Remove all selected objects from the given list widget.
        """
        items = list_widget.selectedIndexes()
        if len(items) < 1:
            return
        model = list_widget.model()

        question = ('Do you really want to remove %d %s' %
                    (len(items),
                    type(model.data(items[0], Qt.UserRole)).__name__))
        if len(items) > 1:
            question += 's'
        question += '?'

        if QMessageBox.question(
                self, 'Please confirm', question,
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        for i in list_widget.selectedIndexes():
            data = model.data(i, Qt.UserRole)
            if isinstance(data, neo.Block):
                self.parent.block_names.pop(data)
            else:
                spykeutils.tools.remove_from_hierarchy(data)
            list_widget.selectionModel().select(i, QItemSelectionModel.Deselect)

        self.object_removed.emit()

    def _context_actions(self, list_widget):
        idx = list_widget.currentIndex()
        if not idx:
            return []

        data = list_widget.model().data(idx, Qt.UserRole)

        edit_action = QAction(get_icon('edit.png'),
                              'Edit annotations...', self)
        edit_action.triggered.connect(
            lambda x: self._edit_item_annotations(idx, list_widget.model()))

        delete_name = 'Delete %s' % type(data).__name__
        if len(list_widget.selectedIndexes()) > 1:
            delete_name += 's'
        delete_action = QAction(get_icon('editdelete.png'),
                                delete_name, self)
        delete_action.triggered.connect(
            lambda x:
            self.remove_selected(list_widget))

        return [edit_action, delete_action]

    def on_neoBlockList_customContextMenuRequested(self, pos):
        if not self.neoBlockList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoBlockList))
        context_menu.popup(self.neoBlockList.mapToGlobal(pos))

    def on_neoSegmentList_customContextMenuRequested(self, pos):
        if not self.neoSegmentList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoSegmentList))
        context_menu.popup(self.neoSegmentList.mapToGlobal(pos))

    def on_neoChannelGroupList_customContextMenuRequested(self, pos):
        if not self.neoChannelGroupList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelGroupList))
        context_menu.popup(self.neoChannelGroupList.mapToGlobal(pos))

    def on_neoChannelList_customContextMenuRequested(self, pos):
        if not self.neoChannelList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelList))
        context_menu.popup(self.neoChannelList.mapToGlobal(pos))

    def on_neoUnitList_customContextMenuRequested(self, pos):
        if not self.neoUnitList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoUnitList))
        context_menu.popup(self.neoUnitList.mapToGlobal(pos))

    def blocks(self):
        """ Return selected :class:`neo.Block` objects.
        """
        return [self.block_model.data(i, Qt.UserRole) for i in
                self.neoBlockList.selectedIndexes()]

    def segments(self):
        """ Return selected :class:`neo.Segment` objects.
        """
        return [self.segment_model.data(i, Qt.UserRole) for i in
                self.neoSegmentList.selectedIndexes()]

    def recording_channel_groups(self):
        """ Return selected :class:`neo.RecordingChannelGroup` objects.
        """
        return [self.channelgroup_model.data(i, Qt.UserRole) for i in
                self.neoChannelGroupList.selectedIndexes()]

    def recording_channels(self):
        """ Return selected :class:`neo.RecordingChannel` objects.
        """
        return [self.channel_model.data(i, Qt.UserRole) for i in
                self.neoChannelList.selectedIndexes()]

    def units(self):
        """ Return selected :class:`neo.Unit` objects.
        """
        return [self.unit_model.data(i, Qt.UserRole) for i in
                self.neoUnitList.selectedIndexes()]

    def set_selection(self, data):
        """ Set the selected data.
        """
        block_list = []
        for b in data['blocks']:
            cl = None
            rp = None
            if len(b) > 2:
                cl = NeoDataProvider.find_io_class(b[2])
            if len(b) > 3:
                rp = b[3]
            loaded = NeoDataProvider.get_block(
                b[1], b[0], force_io=cl, read_params=rp)
            if loaded is None:
                raise IOError('One of the files contained in the '
                              'selection could not be loaded!')
            block_list.append(loaded)

        block_set = set([(b[0], b[1]) for b in data['blocks']])
        # Select blocks
        self.ensure_not_filtered(block_list, self.parent.block_names.keys(),
                                 self.parent.get_active_filters('Block'))
        self.populate_neo_block_list()
        selection = QItemSelection()
        for i in self.block_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            block = i.data(Qt.UserRole)
            t = (NeoDataProvider.block_indices[block],
                 self.parent.block_files[block])

            if t in block_set:
                selection.append(QItemSelectionRange(
                    self.block_model.indexFromItem(i)))
        self.neoBlockList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select segments
        seg_list = [block_list[idx[1]].segments[idx[0]]
                    for idx in data['segments']]
        all_segs = []
        for b in self.blocks():
            all_segs.extend(b.segments)
        self.ensure_not_filtered(seg_list, all_segs,
                                 self.parent.get_active_filters('Segment'))
        self.populate_neo_segment_list()

        selection = QItemSelection()
        for i in self.segment_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            segment = i.data(Qt.UserRole)
            if not segment.block in block_list:
                continue

            seg_idx = segment.block.segments.index(segment)
            block_idx = block_list.index(segment.block)
            if [seg_idx, block_idx] in data['segments']:
                selection.append(QItemSelectionRange(
                    self.segment_model.indexFromItem(i)))
        self.neoSegmentList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select recording channel groups
        rcg_list = [block_list[rcg[1]].recordingchannelgroups[rcg[0]]
                    for rcg in data['channel_groups']]
        all_rcgs = []
        for b in self.blocks():
            all_rcgs.extend(b.recordingchannelgroups)
        self.ensure_not_filtered(
            rcg_list, all_rcgs,
            self.parent.get_active_filters('Recording Channel Group'))
        self.populate_neo_channel_group_list()

        selection = QItemSelection()
        for i in self.channelgroup_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            rcg = i.data(Qt.UserRole)
            if not rcg.block in block_list:
                continue

            rcg_idx = rcg.block.recordingchannelgroups.index(rcg)
            block_idx = block_list.index(rcg.block)
            if [rcg_idx, block_idx] in data['channel_groups']:
                selection.append(QItemSelectionRange(
                    self.channelgroup_model.indexFromItem(i)))
        self.neoChannelGroupList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select channels
        rc_list = [rcg_list[rc[1]].recordingchannels[rc[0]]
                   for rc in data['channels']]
        all_rcs = []
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in all_rcs:
                    continue
                all_rcs.append(rc)
        self.ensure_not_filtered(
            rc_list, all_rcs,
            self.parent.get_active_filters('Recording Channel'))
        self.populate_neo_channel_list()

        selection = QItemSelection()
        rcg_set = set(rcg_list)
        for i in self.channel_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            channel = i.data(Qt.UserRole)
            if not set(channel.recordingchannelgroups).intersection(rcg_set):
                continue

            for rcg in channel.recordingchannelgroups:
                if [rcg.recordingchannels.index(channel),
                        rcg_list.index(rcg)] in data['channels']:
                    selection.append(QItemSelectionRange(
                        self.channel_model.indexFromItem(i)))
                    break
        self.neoChannelList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select units
        unit_list = [rcg_list[u[1]].units[u[0]]
                     for u in data['units']]
        all_units = []
        for rcg in self.recording_channel_groups():
            all_units.extend(rcg.units)
        self.ensure_not_filtered(
            unit_list, all_units,
            self.parent.get_active_filters('Unit'))
        self.populate_neo_unit_list()

        selection = QItemSelection()
        for i in self.unit_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            unit = i.data(Qt.UserRole)
            if unit.recordingchannelgroup not in rcg_list:
                continue

            rcg_idx = rcg_list.index(unit.recordingchannelgroup)
            unit_idx = unit.recordingchannelgroup.units.index(unit)
            if [unit_idx, rcg_idx] in data['units']:
                selection.append(QItemSelectionRange(
                    self.unit_model.indexFromItem(i)))
        self.neoUnitList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        self.parent.refresh_filters()
Esempio n. 8
0
class DlgSqlLayerWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, layer, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.layer = layer

        uri = QgsDataSourceUri(layer.source())
        dbplugin = None
        db = None
        if layer.dataProvider().name() == 'postgres':
            dbplugin = createDbPlugin('postgis', 'postgres')
        elif layer.dataProvider().name() == 'spatialite':
            dbplugin = createDbPlugin('spatialite', 'spatialite')
        elif layer.dataProvider().name() == 'oracle':
            dbplugin = createDbPlugin('oracle', 'oracle')
        elif layer.dataProvider().name() == 'virtual':
            dbplugin = createDbPlugin('vlayers', 'virtual')
        if dbplugin:
            dbplugin.connectToUri(uri)
            db = dbplugin.db

        self.dbplugin = dbplugin
        self.db = db
        self.filter = ""
        self.allowMultiColumnPk = isinstance(
            db, PGDatabase
        )  # at the moment only PostgreSQL allows a primary key to span multiple columns, spatialite doesn't
        self.aliasSubQuery = isinstance(
            db,
            PGDatabase)  # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" %
            (self.windowTitle(), db.connection().connectionName(),
             db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(
                self.tr("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(
                self.tr("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.editSql.setMarginVisible(True)
        self.initCompleter()

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(
                self.uniqueChanged)  # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(
                self.uniqueTextChanged
            )  # there are other events that change the displayed text and some of them can not be caught directly

        self.layerTypeWidget.hide()  # show if load as raster is supported
        #self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
        self.updateLayerBtn.clicked.connect(self.updateSqlLayer)
        self.getColumnsBtn.clicked.connect(self.fillColumnCombos)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

        # Update from layer
        # Fisrtly the SQL from QgsDataSourceUri table
        sql = uri.table()
        if uri.keyColumn() == '_uid_':
            match = re.search(
                '^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$',
                sql, re.S)
            if match:
                sql = match.group(1)
        else:
            match = re.search('^\((SELECT .+ FROM .+)\)$', sql, re.S)
            if match:
                sql = match.group(1)
        self.editSql.setText(sql)
        self.executeSql()

        # Then the columns
        self.geomCombo.setCurrentIndex(
            self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly))
        if uri.keyColumn() != '_uid_':
            self.uniqueColumnCheck.setCheckState(Qt.Checked)
            if self.allowMultiColumnPk:
                itemsData = uri.keyColumn().split(',')
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() in itemsData:
                        item.setCheckState(Qt.Checked)
            else:
                keyColumn = uri.keyColumn()
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() == keyColumn:
                        self.uniqueCombo.setCurrentIndex(
                            self.uniqueModel.indexFromItem(item).row())

        # Finally layer name, filter and selectAtId
        self.layerNameEdit.setText(layer.name())
        self.filter = uri.sql()
        if uri.selectAtIdDisabled():
            self.avoidSelectById.setCheckState(Qt.Checked)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry(
                'DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name',
            name)
        QgsProject.instance().writeEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(
                self.tr("%d rows, %.1f seconds") %
                (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(
                    self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked(
        ) else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName,
                                   newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            return None

    def loadSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            from qgis.core import QgsMapLayerRegistry
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)
        finally:
            QApplication.restoreOverrideCursor()

    def updateSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            #self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri())
            #self.layer.dataProvider().reloadData()
            XMLDocument = QDomDocument("style")
            XMLMapLayers = XMLDocument.createElement("maplayers")
            XMLMapLayer = XMLDocument.createElement("maplayer")
            self.layer.writeLayerXML(XMLMapLayer, XMLDocument)
            XMLMapLayer.firstChildElement(
                "datasource").firstChild().setNodeValue(layer.source())
            XMLMapLayers.appendChild(XMLMapLayer)
            XMLDocument.appendChild(XMLMapLayers)
            self.layer.readLayerXML(XMLMapLayer)
            self.layer.reload()
            self.iface.actionDraw().trigger()
            self.iface.mapCanvas().refresh()
            self.iface.legendInterface().refreshLayerLegend(layer)
        finally:
            QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        if self.aliasSubQuery:
            # get a new alias
            aliasIndex = 0
            while True:
                alias = "_subQuery__%d" % aliasIndex
                escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
                if not escaped.search(query):
                    break
                aliasIndex += 1

            sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (
                unicode(query), connector.quoteId(alias))
        else:
            sql = u"SELECT * FROM (%s\n) WHERE 1=0" % unicode(query)

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(
                col for col in cols
                if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[
                        0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(
            self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface,
                              self.db,
                              self,
                              reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
Esempio n. 9
0
    def randomize_groups(self):
        #participant_list_form = Participant_list()
        #participant_list_form.show()
        #participant_list_form.ui3.label.setText("sdsdfdsf")
        #ex2.ui.label_6.setText("test")
        participant_list_form = ex3
        # participant_list_form = Participant_list()
        # participant_list_form.show()
        tv = participant_list_form.ui3.tableView

        # set the table model
        tablemdata = 0
        #tabledata.append([2, 2, 2, 2, 2])
        # tabledata.remove([0])
        # tabledata.append([1, 1, 1, 1, 1])
        # table.model().layoutChanged.emit()
        tablemodel = MyTableModel(tabledata, header, self)
        rowcount = int(tablemodel.rowCount(None))
        tvmodel = tv.model()
        nums = [x+1 for x in range(rowcount)]
        random.shuffle(nums)

        print nums

        #iterating list





        #print i+1 % groups_to_create

        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # init widgets
        view = ex8.ui8.treeView
        view.setSelectionBehavior(QAbstractItemView.SelectRows)
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels(header)
        view.setModel(model)
        view.setUniformRowHeights(True)
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # populate data

        groups_to_create = int(self.ui.lineEdit.text())
        participants_in_group = int(self.ui.lineEdit_2.text())
        divider = 0
        group = 1
        parent1 = QStandardItem('Grupa {}'.format(group))
        for i, val in enumerate(nums):

            if divider == participants_in_group:
                divider = 0
                group = group + 1
                parent1 = QStandardItem('Grupa {}'.format(group))
            divider = divider + 1
            if val != 0:
                print "Grupa " + str(group) + " Osoby: " + str(val)
                self.tableindex_id = tablemodel.index (val-1,1)
                self.tableindex_surname = tablemodel.index (val-1,2)
                child1 = QStandardItem(str(val)+' '+" ")
                child2 = QStandardItem(str(tablemodel.data(self.tableindex_id,  Qt.DisplayRole)))
                child3 = QStandardItem(str(tablemodel.data(self.tableindex_surname,  Qt.DisplayRole)))
                parent1.appendRow([child1, child2, child3])


                model.appendRow(parent1)
                # span container columns
                view.setFirstColumnSpanned(i, view.rootIndex(), True)


        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # expand third container
        index = model.indexFromItem(parent1)
        view.expand(index)
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # select last row
        selmod = view.selectionModel()
        index2 = model.indexFromItem(child3)
        selmod.select(index2, QItemSelectionModel.Select | QItemSelectionModel.Rows)
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print i+1
        if i+1 > 3:
            print i / groups_to_create
        else:
            print i / groups_to_create

        #index = tablemodel.index(row, 1)

        print nums
Esempio n. 10
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])



    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)
            node.create_children()

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)
        self.projectwidget.setpage(node.page, node)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        node = find_node(index)
        node.refresh()
Esempio n. 11
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(
            self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(
            self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)
            node.create_children()

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText(
                "IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)
        self.projectwidget.setpage(node.page, node)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        node = find_node(index)
        node.refresh()
Esempio n. 12
0
class DlgSqlLayerWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, layer, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.layer = layer

        uri = QgsDataSourceUri(layer.source())
        dbplugin = None
        db = None
        if layer.dataProvider().name() == 'postgres':
            dbplugin = createDbPlugin('postgis', 'postgres')
        elif layer.dataProvider().name() == 'spatialite':
            dbplugin = createDbPlugin('spatialite', 'spatialite')
        elif layer.dataProvider().name() == 'oracle':
            dbplugin = createDbPlugin('oracle', 'oracle')
        elif layer.dataProvider().name() == 'virtual':
            dbplugin = createDbPlugin('vlayers', 'virtual')
        if dbplugin:
            dbplugin.connectToUri(uri)
            db = dbplugin.db

        self.dbplugin = dbplugin
        self.db = db
        self.filter = ""
        self.allowMultiColumnPk = isinstance(db, PGDatabase) # at the moment only PostgreSQL allows a primary key to span multiple columns, spatialite doesn't
        self.aliasSubQuery = isinstance(db, PGDatabase) # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" % (self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.editSql.setMarginVisible(True)
        self.initCompleter()

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(self.uniqueChanged)                # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly

        self.layerTypeWidget.hide()  # show if load as raster is supported
        #self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
        self.updateLayerBtn.clicked.connect(self.updateSqlLayer)
        self.getColumnsBtn.clicked.connect(self.fillColumnCombos)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

        # Update from layer
        # Fisrtly the SQL from QgsDataSourceUri table
        sql = uri.table()
        if uri.keyColumn() == '_uid_':
            match = re.search('^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S)
            if match:
                sql = match.group(1)
        else:
            match = re.search('^\((SELECT .+ FROM .+)\)$', sql, re.S)
            if match:
                sql = match.group(1)
        self.editSql.setText(sql)
        self.executeSql()

        # Then the columns
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly))
        if uri.keyColumn() != '_uid_':
            self.uniqueColumnCheck.setCheckState(Qt.Checked)
            if self.allowMultiColumnPk:
                itemsData = uri.keyColumn().split(',')
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() in itemsData:
                        item.setCheckState(Qt.Checked)
            else:
                keyColumn = uri.keyColumn()
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() == keyColumn:
                        self.uniqueCombo.setCurrentIndex(self.uniqueModel.indexFromItem(item).row())

        # Finally layer name, filter and selectAtId
        self.layerNameEdit.setText(layer.name())
        self.filter = uri.sql()
        if uri.selectAtIdDisabled():
            self.avoidSelectById.setCheckState(Qt.Checked)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name', name)
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(self.tr("%d rows, %.1f seconds") % (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            return None

    def loadSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            from qgis.core import QgsMapLayerRegistry
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)
        finally:
            QApplication.restoreOverrideCursor()

    def updateSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            #self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri())
            #self.layer.dataProvider().reloadData()
            XMLDocument = QDomDocument("style")
            XMLMapLayers = XMLDocument.createElement("maplayers")
            XMLMapLayer = XMLDocument.createElement("maplayer")
            self.layer.writeLayerXML(XMLMapLayer, XMLDocument)
            XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(layer.source())
            XMLMapLayers.appendChild(XMLMapLayer)
            XMLDocument.appendChild(XMLMapLayers)
            self.layer.readLayerXML(XMLMapLayer)
            self.layer.reload()
            self.iface.actionDraw().trigger()
            self.iface.mapCanvas().refresh()
            self.iface.legendInterface().refreshLayerSymbology(layer)
        finally:
            QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        if self.aliasSubQuery:
            # get a new alias
            aliasIndex = 0
            while True:
                alias = "_subQuery__%d" % aliasIndex
                escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
                if not escaped.search(query):
                    break
                aliasIndex += 1

            sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (unicode(query), connector.quoteId(alias))
        else:
            sql = u"SELECT * FROM (%s\n) WHERE 1=0" % unicode(query)

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface, self.db, self, reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
Esempio n. 13
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp
        self.reloadingProject = False
        self.loadedProject = None
        self.projectpath = None

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(
            self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.projectLoaded)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(
            self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

    def closeEvent(self, closeevent):
        self.save_page_config()
        closeevent.accept()

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.datanode = DataNode(folder=None)
        rootitem.appendRow(self.datanode)
        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)
        self.publishnode = PublishNode(folder=None)
        rootitem.appendRow(self.publishnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    @property
    def active_project_folder(self):
        return self.projectpath

    @active_project_folder.setter
    def active_project_folder(self, value):
        self.projectpath = value
        pass

    def loadprojects(self, projectpath):
        self.active_project_folder = projectpath

        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def projectLoaded(self, project):
        roam.utils.info("Project loaded: {}".format(project.name))
        node = self.projectsnode.find_by_name(project.name)
        node.create_children()
        self.projectwidget.setpage(node.page, node, refreshingProject=True)
        self.reloadingProject = False
        self.loadedProject = project
        self.projectList.setExpanded(node.index(), True)

    def nodeselected(self, index, last, reloadProject=False):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project

        if project:
            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)
                return

        if node.nodetype == Treenode.ProjectNode and reloadProject:
            self.reloadingProject = True
            self.projectwidget.setproject(project)
            return

        if project and self.loadedProject != project:
            # Only load the project if it's different the current one.
            if self.loadedProject:
                lastnode = self.projectsnode.find_by_name(
                    self.loadedProject.name)
                self.projectList.setExpanded(lastnode.index(), False)
            ## We are closing the open project at this point. Watch for null ref after this.
            self.projectwidget.setproject(project)
            self.projectwidget.setpage(node.page, node)
            return
        else:
            self.projectwidget.setpage(node.page, node)

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

    def save_page_config(self):
        """
        Save the current page config
        """
        self.projectwidget.savePage(closing=True)

    def projectupdated(self, project):
        print "PROJECT UPDATED"
        node = self.projectsnode.find_by_name(project.name)
        self.projectList.selectionModel().select(
            node.index(), QItemSelectionModel.ClearAndSelect)
        self.nodeselected(node.index(), None, reloadProject=True)
Esempio n. 14
0
class DetailsTreeView(DetailsDBHandler, DetailsDockWidget):
    def __init__(self, iface, spatial_unit_dock):

        """
        The method initializes the dockwidget.
        :param iface: QGIS user interface class
        :type class qgis.utils.iface
        :param plugin: The STDM plugin
        :type class
        :return: None
        """
        from stdm.ui.entity_browser import _EntityDocumentViewerHandler
        DetailsDockWidget.__init__(self, iface, spatial_unit_dock)

        DetailsDBHandler.__init__(self)

        self.spatial_unit_dock = spatial_unit_dock

        self.view = QTreeView()
        self.view.setSelectionBehavior(
            QAbstractItemView.SelectRows
        )
        #self.feature_ids = []
        self.layer_table = None
        self.entity = None
        self.feature_models = {}
        self.party_models = {}
        self.STR_models = {}
        self.feature_STR_model = {}
        self.removed_feature = None
        self.selected_root = None
        self.model = QStandardItemModel()
        self.view.setModel(self.model)
        self.view.setUniformRowHeights(True)
        self.view.setRootIsDecorated(True)
        self.view.setAlternatingRowColors(True)
        self.view.setWordWrap(True)
        self.view.setHeaderHidden(True)
        self.view.setEditTriggers(
            QAbstractItemView.NoEditTriggers
        )
        self.current_profile = current_profile()
        self.social_tenure = self.current_profile.social_tenure
        self.spatial_unit = self.social_tenure.spatial_unit
        self.party = self.social_tenure.party
        self.view.setMinimumWidth(250)
        self.doc_viewer_title = QApplication.translate(
            'EntityBrowser',
            'Document Viewer'
        )
        self.doc_viewer = _EntityDocumentViewerHandler(
            self.doc_viewer_title, self.iface.mainWindow()
        )

    def set_layer_entity(self):
        self.layer_table = self.get_layer_source(
            self.iface.activeLayer()
        )
        if self.layer_table in spatial_tables():
            self.entity = self.current_profile.entity_by_name(
                self.layer_table
            )

        else:
            self.treeview_error('The layer is not a spatial entity layer. ')

    def activate_feature_details(self, button_clicked=True):
        """
        Action for showing feature details.
        :return:
        """
        # Get the active layer.
        active_layer = self.iface.activeLayer()
        # TODO fix feature_details_btn is deleted error.
        if active_layer is not None and \
                self.spatial_unit_dock.feature_details_btn.isChecked():
            # if feature detail dock is not defined or hidden, create empty dock.
            if self is None or self.isHidden():
                # if the selected layer is not a feature layer, show not
                # feature layer. (implicitly included in the if statement).
                if not self.feature_layer(active_layer):
                    self.spatial_unit_dock.feature_details_btn.setChecked(False)
                    return
                # If the selected layer is feature layer, get data and
                # display treeview in a dock widget
                else:
                    select_feature = QApplication.translate(
                        "STDMQGISLoader",
                        "Please select a feature to view their details."
                    )
                    self.init_dock()
                    self.add_tree_view()
                    self.model.clear()
                    self.treeview_error(select_feature)

                    # enable the select tool
                    self.activate_select_tool()
                    # set entity from active layer in the child class
                    self.set_layer_entity()
                    # set entity for the super class DetailModel
                    self.set_entity(self.entity)
                    # Registery column widget
                    self.set_formatter()
                    #set formatter for social tenure relationship.
                    self.set_formatter(self.social_tenure)
                    self.set_formatter(self.party)
                    # pull data, show treeview
                    active_layer.selectionChanged.connect(
                        self.show_tree
                    )
                    self.steam_signals(self.entity)

            # if feature_detail dock is open, toggle close
            else:
                self.close_dock(
                    self.spatial_unit_dock.feature_details_btn
                )
                self.feature_details = None
        # if no active layer, show error message and uncheck the feature tool
        else:
            if button_clicked:
                self.active_layer_check()
            self.spatial_unit_dock.feature_details_btn.setChecked(False)

    def add_tree_view(self):
        """
        Adds tree view to the dock widget and sets style.
        :return: None
        """
        self.tree_scrollArea.setWidget(self.view)

    def clear_feature_models(self):
        self.feature_models.clear()

    def reset_tree_view(self, no_feature=False):
        #clear feature_ids list, model and highlight
        self.model.clear()

        self.clear_sel_highlight() # remove sel_highlight
        self.disable_buttons(no_feature)
        if self.removed_feature is None:
            self.STR_models.clear()
            self.feature_models.clear()
        else:
            self.removed_feature = None
        features = self.selected_features()
        # if the selected feature is over 1,
        # activate multi_select_highlight
        if not features is None:
            self.view.clicked.connect(
                self.multi_select_highlight
            )
        # if there is at least one selected feature
        if len(features) > 0:
            self.add_tree_view()
            #self.feature_ids = features

    def disable_buttons(self, bool):
        self.edit_btn.setDisabled(bool)
        self.delete_btn.setDisabled(bool)

    def show_tree(self):
        selected_features = self.selected_features()
        if len(selected_features) < 1:
            self.reset_tree_view(True)
            return
        if not self.entity is None:
            self.reset_tree_view()
            if len(selected_features) < 1:
                self.disable_buttons(True)
                return

            layer_icon = QIcon(':/plugins/stdm/images/icons/layer.gif')
            roots = self.add_parent_tree(
                layer_icon, format_name(self.entity.short_name)
            )
            if roots is None:
                return

            for id, root in roots.iteritems():
                db_model = entity_id_to_model(self.entity, id)

                self.add_roots(db_model, root, id)

    def add_parent_tree(self, icon, title):
        roots = OrderedDict()
        for feature_id in self.selected_features():
            root = QStandardItem(icon, title)
            root.setData(feature_id)
            self.set_bold(root)
            self.model.appendRow(root)
            roots[feature_id] = root
        return roots

    def add_roots(self, model, parent, feature_id):
        self.feature_models[feature_id] = model
        if model is None:
            return
        self.column_widget_registry(model, self.entity)

        for i, (col, row) in enumerate(self.formatted_record.iteritems()):
            child = QStandardItem('{}: {}'.format(col, row))
            child.setSelectable(False)
            parent.appendRow([child])

            # Add Social Tenure Relationship steam as a last child
            if i == len(self.formatted_record)-1:
                self.add_STR_child(parent, feature_id)
        self.expand_node(parent)

    def add_STR_steam(self, parent, STR_id):
        str_icon = QIcon(
            ':/plugins/stdm/images/icons/social_tenure.png'
        )
        title = 'Social Tenure Relationship'
        str_root = QStandardItem(str_icon, title)
        str_root.setData(STR_id)
        self.set_bold(str_root)
        parent.appendRow([str_root])
        return str_root
    def add_no_STR_steam(self, parent):
        if self.entity.name == self.spatial_unit.name:
            no_str_icon = QIcon(
                ':/plugins/stdm/images/icons/remove.png'
            )
            title = 'No STR Defined'
            no_str_root = QStandardItem(no_str_icon, title)
            self.set_bold(no_str_root)
            parent.appendRow([no_str_root])

    def add_STR_child(self, parent, feature_id):
        if len(self.feature_STR_link(feature_id)) < 1:
            self.add_no_STR_steam(parent)
            return
        for record in self.feature_STR_link(feature_id):
            self.STR_models[record.id] = record
            str_root = self.add_STR_steam(parent, record.id)
            # add STR children
            self.column_widget_registry(record, self.social_tenure)
            for i, (col, row) in enumerate(
                    self.formatted_record.iteritems()
            ):
                STR_child = QStandardItem(
                    '{}: {}'.format(col, row)
                )
                STR_child.setSelectable(False)
                str_root.appendRow([STR_child])
                if i == len(self.formatted_record)-1:
                    self.add_party_child(
                        str_root, record.party_id
                    )
        self.feature_STR_model[feature_id] = self.STR_models.keys()

    def add_party_steam(self, parent, party_id):
        party_icon = QIcon(
            ':/plugins/stdm/images/icons/table.png'
        )
        title = format_name(self.party.short_name)
        party_root = QStandardItem(party_icon, title)
        party_root.setData(party_id)
        self.set_bold(party_root)

        parent.appendRow([party_root])
        party_root.setEditable(False)
        return party_root

    def add_party_child(self, parent, party_id):

        db_model = entity_id_to_model(self.party, party_id)
        self.party_models[party_id] = db_model
        party_root = self.add_party_steam(parent, party_id)
        # add STR children
        self.column_widget_registry(db_model, self.party)
        for col, row in self.formatted_record.iteritems():
            party_child = QStandardItem('{}: {}'.format(col, row))
            party_child.setSelectable(False)
            party_root.appendRow([party_child])

    def set_bold(self, standard_item):
        """
        Make a text of Qstandaritem to bold.
        :param standard_item: Qstandaritem
        :type: Qstandaritem
        :return: None
        """
        font = standard_item.font()
        font.setBold(True)
        standard_item.setFont(font)

    def treeview_error(self, message, icon=None):
        """
        Displays error message in feature details treeview.
        :param title: the title of the treeview.
        :type: String
        :param message: The message to be displayed.
        :type: String
        :param icon: The icon of the item.
        :type: Resource string
        :return: None
        """
        not_feature_ft_msg = QApplication.translate(
            'FeatureDetails', message
        )
        if icon== None:
            root = QStandardItem(not_feature_ft_msg)
        else:
            root = QStandardItem(icon, not_feature_ft_msg)

        self.view.setRootIsDecorated(False)
        self.model.appendRow(root)
        self.view.setRootIsDecorated(True)

    def expand_node(self, parent):
        """
        Make the last tree node expand.
        :param parent: The parent to expand
        :type QStandardItem
        :return:None
        """
        index = self.model.indexFromItem(parent)
        self.view.expand(index)


    def multi_select_highlight(self, index):
        """
        Highlights a feature with rubberBald class when selecting
        features are more than one.
        :param index: Selected QTreeView item index
        :type Integer
        :return: None
        """
        map = self.iface.mapCanvas()
        try:

            # Get the selected item text using the index
            selected_item = self.model.itemFromIndex(index)
            # Use mutli-select only when more than 1 items are selected.
            if self.layer.selectedFeatures() < 2:
                return
            self.selected_root = selected_item
            # Split the text to get the key and value.
            selected_item_text = selected_item.text()

            selected_value = selected_item.data()
            # If the first word is feature, expand & highlight.
            if selected_item_text == format_name(self.spatial_unit.short_name):
                self.view.expand(index)  # expand the item
                # Clear any existing highlight
                self.clear_sel_highlight()
                # Insert highlight
                # Create expression to target the selected feature
                expression = QgsExpression(
                    "\"id\"='" + str(selected_value) + "'"
                )
                # Get feature iteration based on the expression
                ft_iteration = self.layer.getFeatures(
                    QgsFeatureRequest(expression)
                )

                # Retrieve geometry and attributes
                for feature in ft_iteration:
                    # Fetch geometry
                    geom = feature.geometry()
                    self.sel_highlight = QgsHighlight(map, geom, self.layer)

                    self.sel_highlight.setFillColor(selection_color())
                    self.sel_highlight.setWidth(4)
                    self.sel_highlight.setColor(QColor(212,95,0, 255))
                    self.sel_highlight.show()
                    break
        except AttributeError:
            # pass attribute error on child items such as party
            pass
        except IndexError:
            pass

    def steam_signals(self, entity):
        self.edit_btn.clicked.connect(
            lambda : self.edit_selected_steam(
                entity
            )
        )
        self.delete_btn.clicked.connect(
            self.delete_selected_item
        )
        self.view_document_btn.clicked.connect(
            lambda : self.view_steam_document(
                entity
            )
        )

    def steam_data(self, mode):
        item = None
        # if self.view.currentIndex().text() == format_name(self.party):
        #     return None, None
        # One item is selected and number of feature is also 1
        if len(self.layer.selectedFeatures()) == 1 and \
                        len(self.view.selectedIndexes()) == 1:
            index = self.view.selectedIndexes()[0]
            item = self.model.itemFromIndex(index)
            result = item.data()

        # One item is selected on the map but not on the treeview
        elif len(self.layer.selectedFeatures()) == 1 and \
                        len(self.view.selectedIndexes()) == 0:
            item = self.model.item(0, 0)
            result = item.data()

        # multiple features are selected but one treeview item is selected
        elif len(self.layer.selectedFeatures()) > 1 and \
                        len(self.view.selectedIndexes()) == 1:
            item = self.selected_root
            result = self.selected_root.data()
        # multiple features are selected but no treeview item is selected
        elif len(self.layer.selectedFeatures()) > 1 and \
             len(self.view.selectedIndexes()) == 0:
            result = 'Please, select an item to {}.'.format(mode)
        else:
            result = 'Please, select at least one feature to {}.'.format(mode)
        if result is None:

            if item is None:
                item = self.model.item(0, 0)
                result = item.data()
            else:
                result = item.parent().data()
        return result, item

    def edit_selected_steam(self, entity):
        id, item = self.steam_data('edit')

        feature_edit = True
        if id is None:
            return
        if isinstance(id, str):
            data_error = QApplication.translate('DetailsTreeView', id)
            QMessageBox.warning(
                self.iface.mainWindow(), "Edit Error", data_error
            )
            return

        if item.text() == 'Social Tenure Relationship':
            model = self.STR_models[id]

            feature_edit = False
            ##TODO add STR wizard edit mode here.
        elif item.text() == format_name(self.party.short_name):
            feature_edit = False

            model = self.party_models[id]
            editor = EntityEditorDialog(
                self.party, model, self.iface.mainWindow()
            )
            editor.exec_()
        else:
            model = self.feature_models[id]

            editor = EntityEditorDialog(
                entity, model, self.iface.mainWindow()
            )
            editor.exec_()
        #root = self.find_root(entity, id)
        self.view.expand(item.index())
        if feature_edit:
            self.update_edited_steam(entity, id)
        else:
            self.update_edited_steam(self.social_tenure, id)

    def delete_selected_item(self):
        str_edit = False
        id, item = self.steam_data('delete')

        if isinstance(id, str):
            data_error = QApplication.translate(
                'DetailsTreeView', id
            )
            QMessageBox.warning(
                self.iface.mainWindow(),
                'Delete Error',
                data_error
            )
            return
        if item.text() == 'Social Tenure Relationship':
            str_edit = True
            db_model = self.STR_models[id]

        elif item.text() == format_name(self.spatial_unit.short_name) and \
            id not in self.feature_STR_model.keys():
            db_model = self.feature_models[id]

        # if spatial unit is linked to STR, don't allow delete
        elif item.text() == format_name(self.spatial_unit.short_name) and \
                        id in self.feature_STR_model.keys():


            delete_warning = QApplication.translate(
                'DetailsTreeView',
                'You have to first delete the social tenure \n'
                'relationship to delete the {} record.'.format(
                    item.text()
                )

            )
            QMessageBox.warning(
                self.iface.mainWindow(),
                'Delete Error',
                delete_warning
            )
            return
        # If it is party node, STR exists and don't allow delete.
        elif item.text() == format_name(self.party.short_name):
            delete_warning = QApplication.translate(
                'DetailsTreeView',
                'You have to first delete the social tenure \n'
                'relationship to delete the {} record.'.format(
                    item.text()
                )
            )
            QMessageBox.warning(
                self.iface.mainWindow(),
                'Delete Error',
                delete_warning
            )
            return
        else:
            return
        delete_warning = QApplication.translate(
            'DetailsTreeView',
            'Are you sure you want to delete '
            'the selected record(s)?\n'
            'This action cannot be undone.'
        )

        delete_question = QMessageBox.warning(
            self.iface.mainWindow(),
            "Delete Warning",
            delete_warning,
            QMessageBox.Yes | QMessageBox.No
        )
        if delete_question == QMessageBox.Yes:
            db_model.delete()

            if str_edit:
                del self.STR_models[id]
            else:
                self.removed_feature = id
                del self.feature_models[id]

            self.updated_removed_steam(str_edit, item)
        else:
            return

    def update_edited_steam(self, entity, feature_id):

        # remove rows before adding the updated ones.
        self.layer.setSelectedFeatures(
            self.feature_models.keys()
        )
        root = self.find_root(entity, feature_id)
        if root is None:
            return
        self.view.selectionModel().select(
            root.index(), self.view.selectionModel().Select
        )
        self.expand_node(root)
        self.multi_select_highlight(root.index())

    def find_root(self, entity, feature_id):
        all_roots = self.model.findItems(
            format_name(entity.short_name)
        )
        root = None
        for item in all_roots:
            if item.data() == feature_id:
                root = item
                break
        return root

    def updated_removed_steam(self, STR_edit, item):
        if not STR_edit:
            if len(self.feature_models) > 1:
                self.refresh_layers()
            feature_ids = self.feature_models.keys()
            self.layer.setSelectedFeatures(
                feature_ids
            )
        else:
            item.removeRows(0, 2)
            item.setText('No STR Definded')
            no_str_icon = QIcon(
                ':/plugins/stdm/images/icons/remove.png'
            )
            item.setIcon(no_str_icon)

    def view_steam_document(self, entity):
        # Slot raised to show the document viewer for the selected entity

        id, item = self.steam_data('edit')

        if id is None:
            return
        if isinstance(id, str):
            data_error = QApplication.translate('DetailsTreeView', id)
            QMessageBox.warning(
                self.iface.mainWindow(), "Edit Error", data_error
            )
            return
        if item.text() == 'Social Tenure Relationship':
            db_model = self.STR_models[id]
        else:
            db_model = self.feature_models[id]

        if not db_model is None:
            docs = db_model.documents
            # Notify there are no documents for the selected doc
            if len(docs) == 0:
                msg = QApplication.translate(
                    'EntityBrowser',
                    'There are no supporting documents '
                    'for the selected record.'
                )

                QMessageBox.warning(
                    self,
                    self.doc_viewer_title,
                    msg
                )
            else:
                self.doc_viewer.load(docs)
Esempio n. 15
0
class DlgTextItemEditor(QDialog, Ui_TextItemEditor):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        Ui_TextItemEditor.__init__(self)
        self.setupUi(self)
        self.itemName = "Value"
        # Create an empty model for the list's data
        self._model = QStandardItemModel(self.list_view)

    def setupUi(self, TextItemEditor):
        super().setupUi(TextItemEditor)
        TextItemEditor.btn_add.clicked.connect(self.on_add_click)
        TextItemEditor.btn_edit.clicked.connect(self.on_edit_click)
        TextItemEditor.btn_delete.clicked.connect(self.on_delete_click)

    def get_items(self):
        for i in range(self._model.rowCount()):
            yield self._model.item(i).text()

    def set_items(self, items):
        self._model.clear()
        for item in items:
            list_item = QStandardItem(item)
            self._model.appendRow(list_item)
        self.list_view.setModel(self._model)

    def get_selected(self):
        indexes = self.list_view.selectedIndexes()
        index = indexes[0] if indexes else None
        if not index:
            return None, None

        return self._model.itemFromIndex(index).text(), index

    def on_add_click(self):
        item, ok = utils.input_query(None, "Adding %s" % self.itemName.lower(),
                                     self.itemName + ":")
        if ok:
            list_item = QStandardItem(item)
            self._model.appendRow(list_item)
            self.list_view.setCurrentIndex(
                self._model.indexFromItem(list_item))
            self.event_add(item)

    def on_edit_click(self):
        text, index = self.get_selected()
        if not index:
            QMessageBox.question(None, 'Information',
                                 "Select %s from list" % self.itemName.lower(),
                                 QMessageBox.Ok)
            return

        item, ok = utils.input_query(None,
                                     "Editing %s" % self.itemName.lower(),
                                     self.itemName + ":", text)
        if ok:
            self._model.itemFromIndex(index).setText(item)
            self.event_edit(item)

    def on_delete_click(self):
        text, index = self.get_selected()
        if not index:
            QMessageBox.question(None, 'Information',
                                 "Select %s from list" % self.itemName.lower(),
                                 QMessageBox.Ok)
            return

        should_delete = QMessageBox.question(
            None, "Deleting %s" % self.itemName.lower(), "Delete '%s'?" % text,
            QMessageBox.Yes, QMessageBox.No)
        if should_delete == QMessageBox.Yes:
            self.event_delete(text)
            self._model.removeRow(index.row())

    @Event
    def event_add(self, item):
        """Signals the parent that an item has been added to the list
        :param item: str
        """

    @Event
    def event_edit(self, item):
        """Signals the parent that an item has been added to the list
        :param item: str
        """

    @Event
    def event_delete(self, item):
        """Signals the parent that an item has been removed from the list