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
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
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)
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)
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
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)
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()
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()
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
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()
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()
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()
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)
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)
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