class ProposalEditor(QWidget): """Editor for proposals.""" def __init__(self, main_widget, parent=None): super(ProposalEditor, self).__init__(parent) self.main_widget = main_widget self.name_edit = QLineEdit() self.url_edit = QLineEdit() self.start_block_edit = QLineEdit() self.end_block_edit = QLineEdit() self.amount_edit = QLineEdit() self.address_edit = QLineEdit() self.txid_edit = QLineEdit() for i in [self.name_edit, self.url_edit, self.start_block_edit, self.end_block_edit, self.amount_edit, self.address_edit, self.txid_edit]: i.setReadOnly(True) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.main_widget.proxy_model) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.addMapping(self.name_edit, ProposalsModel.NAME) self.mapper.addMapping(self.url_edit, ProposalsModel.URL) self.mapper.addMapping(self.start_block_edit, ProposalsModel.START_BLOCK) self.mapper.addMapping(self.end_block_edit, ProposalsModel.END_BLOCK) self.mapper.addMapping(self.amount_edit, ProposalsModel.AMOUNT) self.mapper.addMapping(self.address_edit, ProposalsModel.ADDRESS) self.mapper.addMapping(self.txid_edit, ProposalsModel.TXID) block_hbox = QHBoxLayout() block_hbox.addWidget(self.start_block_edit) block_hbox.addWidget(QLabel(' - ')) block_hbox.addWidget(self.end_block_edit) self.vote_combo = QComboBox() self.vote_combo.addItem(_('Yes')) self.vote_combo.addItem(_('No')) self.vote_button = QPushButton(_('Vote')) self.vote_button.clicked.connect(self.cast_vote) vote_hbox = util.Buttons(self.vote_combo, self.vote_button) form = QFormLayout() form.addRow(_('Name:'), self.name_edit) form.addRow(_('URL:'), self.url_edit) form.addRow(_('Blocks:'), block_hbox) form.addRow(_('Monthly Payment:'), self.amount_edit) form.addRow(_('Payment Address:'), self.address_edit) form.addRow(_('Fee TxID:'), self.txid_edit) form.addRow(_('Vote:'), vote_hbox) self.setLayout(form) def cast_vote(self): name = str(self.name_edit.text()) vote_yes = True if self.vote_combo.currentIndex() == 0 else False self.main_widget.dialog.cast_vote(name, vote_yes)
class JPMainTableModel(QtSql.QSqlRelationalTableModel): # _tp中存放需要添加到数据映射器中的控件类型 # 要添加的控件,必须用字段名命名(大小写敏感) _tp = (QtWidgets.QLineEdit, QtWidgets.QDateEdit, QtWidgets.QComboBox, QtWidgets.QTextEdit, QtWidgets.QCheckBox, QtWidgets.QSpinBox) def __init__(self, parent: QWidget, tableName: str, filter: str = None, db=QtSql.QSqlDatabase()): '''用于窗体模式进行数据编辑时的主窗体数据模型。\n 会自动增加数据映射器,但是外键字段要使用addComboBoxData方法增加列表文字。 最后要调用tofirst()方法定位编辑的记录。\n 注:数据映射器不能增删记录,只能编辑 ''' super().__init__(parent=parent, db=db) self.parent = parent self.setTable(tableName) if filter: self.setFilter(filter) self.select() rec = self.record() self.mapper = QDataWidgetMapper(parent) self.mapper.setModel(self) self.mapper.setItemDelegate(_JPRelationalDelegate(parent, self.mapper)) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) for i in range(rec.count()): widget = parent.findChild(self._tp, rec.fieldName(i)) if widget: if not isinstance(widget, QComboBox): self.mapper.addMapping(widget, i) def toFirst(self): # 定位到模型中第一条记录 self.mapper.toFirst() def addComboBoxData(self, fieldName: str, dataList: list, displayColumn: int = 0, modelColumn: int = 1): '''给模型中添加一个QComboBox控件的数据源。\n dataList为行来源数据源,列表对象.\n displayColumn为Combobox中要显示文字位于列表中的列号(首列为0)\n modelColumn为要保存到模型中数据在列表中的列号(首列为0)\n ''' widget = self.parent.findChild(QComboBox, fieldName) if widget is not None: widget.setModel( _JPComboBoxModel(widget, dataList, displayColumn, modelColumn)) self.mapper.addMapping(widget, self.fieldIndex(fieldName)) def saveData(self): pass
class NoteEditDialog(QDialog, Ui_Dialog): ready = pyqtSignal(bool, int, name='ready') def __init__(self, model, row=None, *args, **kwargs): super().__init__(*args, **kwargs) self.init_ui() self.init_model(model) if row is None: # работаем в режиме добавления self.__model.insertRow(self.__model.rowCount()) self.__mapper.toLast() else: # работаем в режиме редактирования self.__mapper.setCurrentIndex(row) def init_ui(self): # loadUi('ui/notes_edit_dialog.ui', self) self.setupUi(self) self.plannedDateTimeEdit.setMinimumDate( QDateTime.currentDateTime().date() ) self.plannedDateTimeEdit.setDateTime( QDateTime.currentDateTime() ) def init_model(self, model): self.__model = model self.__mapper = QDataWidgetMapper(self) self.__mapper.setModel(self.__model) self.__mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.__mapper.addMapping(self.titleEdit, 1) self.__mapper.addMapping(self.plannedDateTimeEdit, 2) self.__mapper.addMapping(self.contentEdit, 3) def accept(self): # это стандартный метод super().accept() self.__mapper.submit() # отправляем данные в модель state = self.__model.submitAll() # отправить изменения в БД self.ready.emit(state, self.__mapper.currentIndex()) def reject(self): # это стандартный метод super().reject() self.__mapper.revert() self.__model.revertAll()
class NoteEditDialog(QDialog, Ui_Dialog): ready = pyqtSignal(bool, int, name='ready') def __init__(self, model, row=None, *args, **kwargs): super().__init__(*args, **kwargs) self.init_ui() self.init_model(model) if row is None: # в режиме добавления self.__model.insertRow(self.__model.rowCount()) self.__mapper.toLast() else: # работаем в режиме редактирования self.__mapper.setCurrentIndex(row) def init_ui(self): self.setupUi(self) self.plannedDateTimeEdit.setMinimumDate( QDateTime.currentDateTime().date()) self.plannedDateTimeEdit.setDateTime(QDateTime.currentDateTime()) def init_model(self, model): self.__model = model self.__mapper = QDataWidgetMapper(self) self.__mapper.setModel(self.__model) self.__mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.__mapper.addMapping(self.titleEdit, 1) self.__mapper.addMapping(self.plannedDateTimeEdit, 2) self.__mapper.addMapping(self.contentEdit, 3) def accept(self): # это стандартный метод super().accept() self.__mapper.submit() # отправили данные в модель state = self.__model.submitAll() # отправить изменения в БД self.ready.emit(state, self.__mapper.currentIndex()) def reject(self): super().reject() self.__mapper.revert() # отмена изменений self.__model.revertAll()
class PhoneLogDlg(QDialog): FIRST, PREV, NEXT, LAST = range(4) def __init__(self, parent=None): super(PhoneLogDlg, self).__init__(parent) callerLabel = QLabel("&Caller:") self.callerEdit = QLineEdit() callerLabel.setBuddy(self.callerEdit) today = QDate.currentDate() startLabel = QLabel("&Start:") self.startDateTime = QDateTimeEdit() startLabel.setBuddy(self.startDateTime) self.startDateTime.setDateRange(today, today) self.startDateTime.setDisplayFormat(DATETIME_FORMAT) endLabel = QLabel("&End:") self.endDateTime = QDateTimeEdit() endLabel.setBuddy(self.endDateTime) self.endDateTime.setDateRange(today, today) self.endDateTime.setDisplayFormat(DATETIME_FORMAT) topicLabel = QLabel("&Topic:") topicEdit = QLineEdit() topicLabel.setBuddy(topicEdit) outcomeLabel = QLabel("&Outcome:") self.outcomeComboBox = QComboBox() outcomeLabel.setBuddy(self.outcomeComboBox) firstButton = QPushButton() firstButton.setIcon(QIcon(":/first.png")) prevButton = QPushButton() prevButton.setIcon(QIcon(":/prev.png")) nextButton = QPushButton() nextButton.setIcon(QIcon(":/next.png")) lastButton = QPushButton() lastButton.setIcon(QIcon(":/last.png")) addButton = QPushButton("&Add") addButton.setIcon(QIcon(":/add.png")) deleteButton = QPushButton("&Delete") deleteButton.setIcon(QIcon(":/delete.png")) quitButton = QPushButton("&Quit") quitButton.setIcon(QIcon(":/quit.png")) if not MAC: addButton.setFocusPolicy(Qt.NoFocus) deleteButton.setFocusPolicy(Qt.NoFocus) fieldLayout = QGridLayout() fieldLayout.addWidget(callerLabel, 0, 0) fieldLayout.addWidget(self.callerEdit, 0, 1, 1, 3) fieldLayout.addWidget(startLabel, 1, 0) fieldLayout.addWidget(self.startDateTime, 1, 1) fieldLayout.addWidget(endLabel, 1, 2) fieldLayout.addWidget(self.endDateTime, 1, 3) fieldLayout.addWidget(topicLabel, 2, 0) fieldLayout.addWidget(topicEdit, 2, 1, 1, 3) fieldLayout.addWidget(outcomeLabel, 3, 0) fieldLayout.addWidget(self.outcomeComboBox, 3, 1, 1, 3) navigationLayout = QHBoxLayout() navigationLayout.addWidget(firstButton) navigationLayout.addWidget(prevButton) navigationLayout.addWidget(nextButton) navigationLayout.addWidget(lastButton) fieldLayout.addLayout(navigationLayout, 4, 0, 1, 2) buttonLayout = QVBoxLayout() buttonLayout.addWidget(addButton) buttonLayout.addWidget(deleteButton) buttonLayout.addStretch() buttonLayout.addWidget(quitButton) layout = QHBoxLayout() layout.addLayout(fieldLayout) layout.addLayout(buttonLayout) self.setLayout(layout) self.model = QSqlRelationalTableModel(self) self.model.setTable("calls") self.model.setRelation(OUTCOMEID, QSqlRelation("outcomes", "id", "name")) self.model.setSort(STARTTIME, Qt.AscendingOrder) self.model.select() self.mapper = QDataWidgetMapper(self) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setModel(self.model) self.mapper.setItemDelegate(QSqlRelationalDelegate(self)) self.mapper.addMapping(self.callerEdit, CALLER) self.mapper.addMapping(self.startDateTime, STARTTIME) self.mapper.addMapping(self.endDateTime, ENDTIME) self.mapper.addMapping(topicEdit, TOPIC) relationModel = self.model.relationModel(OUTCOMEID) self.outcomeComboBox.setModel(relationModel) self.outcomeComboBox.setModelColumn(relationModel.fieldIndex("name")) self.mapper.addMapping(self.outcomeComboBox, OUTCOMEID) self.mapper.toFirst() firstButton.clicked.connect(lambda: self.saveRecord(PhoneLogDlg.FIRST)) prevButton.clicked.connect(lambda: self.saveRecord(PhoneLogDlg.PREV)) nextButton.clicked.connect(lambda: self.saveRecord(PhoneLogDlg.NEXT)) lastButton.clicked.connect(lambda: self.saveRecord(PhoneLogDlg.LAST)) addButton.clicked.connect(self.addRecord) deleteButton.clicked.connect(self.deleteRecord) quitButton.clicked.connect(self.done) self.setWindowTitle("Phone Log") def done(self, result=None): self.mapper.submit() QDialog.done(self, True) def addRecord(self): row = self.model.rowCount() self.mapper.submit() self.model.insertRow(row) self.mapper.setCurrentIndex(row) now = QDateTime.currentDateTime() self.startDateTime.setDateTime(now) self.endDateTime.setDateTime(now) self.outcomeComboBox.setCurrentIndex( self.outcomeComboBox.findText("Unresolved")) self.callerEdit.setFocus() def deleteRecord(self): caller = self.callerEdit.text() starttime = self.startDateTime.dateTime().toString(DATETIME_FORMAT) if (QMessageBox.question( self, "Delete", "Delete call made by<br>{0} on {1}?".format(caller, starttime), QMessageBox.Yes | QMessageBox.No) == QMessageBox.No): return row = self.mapper.currentIndex() self.model.removeRow(row) self.model.submitAll() self.model.select() if row + 1 >= self.model.rowCount(): row = self.model.rowCount() - 1 self.mapper.setCurrentIndex(row) def saveRecord(self, where): row = self.mapper.currentIndex() self.mapper.submit() if where == PhoneLogDlg.FIRST: row = 0 elif where == PhoneLogDlg.PREV: row = 0 if row <= 1 else row - 1 elif where == PhoneLogDlg.NEXT: row += 1 if row >= self.model.rowCount(): row = self.model.rowCount() - 1 elif where == PhoneLogDlg.LAST: row = self.model.rowCount() - 1 self.mapper.setCurrentIndex(row)
class APISFilm(QDialog, FORM_CLASS): FIRST, PREV, NEXT, LAST = range(4) OBLIQUE, VERTICAL = range(2) def __init__(self, iface, dbm, imageRegistry, apisLayer, parent=None): """Constructor.""" super(APISFilm, self).__init__(parent) self.iface = iface self.dbm = dbm self.imageRegistry = imageRegistry self.apisLayer = apisLayer self.parent = parent self.setupUi(self) # Initial window size/pos last saved. Use default values for first time if GetWindowSize("film"): self.resize(GetWindowSize("film")) if GetWindowPos("film"): self.move(GetWindowPos("film")) self.printingOptionsDlg = None self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat) self.editMode = False self.addMode = False self.initalLoad = True #self.uiInitalEntryQgsDate.setCalendarPopup(False) #self.uiLastChangesQgsDate.setCalendarPopup(False) # Signals/Slot Connections self.rejected.connect(self.onReject) self.uiOkBtn.clicked.connect(self.onAccept) self.uiCancelBtn.clicked.connect(self.cancelEdit) self.uiSaveBtn.clicked.connect(self.saveEdits) self.uiFilmSelectionBtn.clicked.connect(self.openFilmSelectionDialog) self.uiNewFilmBtn.clicked.connect(self.openNewFilmDialog) self.uiSearchFilmBtn.clicked.connect(self.openSearchFilmDialog) self.uiEditWeatherBtn.clicked.connect(self.openEditWeatherDialog) self.uiExportPdfBtn.clicked.connect(self.exportDetailsPdf) self.uiShowFlightPathBtn.clicked.connect(lambda: self.openFlightPathDialog([self.uiCurrentFilmNumberEdit.text()])) # For LaLe Mode if self.settings.value("APIS/disable_site_and_findspot", "0") != "1": self.uiListSitesOfFilmBtn.setEnabled(True) self.uiListSitesOfFilmBtn.clicked.connect(self.openSiteSelectionListDialog) else: self.uiListSitesOfFilmBtn.setEnabled(False) self.uiListImagesOfFilmBtn.clicked.connect(self.openImageSelectionListDialog) self.uiExtractGpsFromImagesBtn.clicked.connect(self.extractGpsFromImages) self.uiWeatherCodeEdit.textChanged.connect(self.generateWeatherCode) self.uiFilmModeCombo.currentIndexChanged.connect(self.onFilmModeChanged) self.uiEditProjectTableBtn.clicked.connect(lambda: self.openSystemTableEditorDialog("projekt", self.uiProjectSelectionCombo)) self.uiEditCopyrightTableBtn.clicked.connect(lambda: self.openSystemTableEditorDialog("copyright", self.uiCopyrightCombo)) # self.uiEditProjectTableBtn.clicked.connect(lambda: VersionToCome()) # self.uiEditCopyrightTableBtn.clicked.connect(lambda: VersionToCome()) # init Project Btn self.uiAddProjectBtn.clicked.connect(self.addProject) self.uiRemoveProjectBtn.clicked.connect(self.removeProject) # Setup Sub-Dialogs self.filmSelectionDlg = APISFilmNumberSelection(self) self.newFilmDlg = APISFilmNew(parent=self) self.searchFilmDlg = APISFilmSearch(self.dbm, self) # (self.iface, self.dbm) self.editWeatherDlg = APISWeather(self.iface, self.dbm, self) self.flightPathDlg = APISFlightPath(self.iface, self.dbm, self.apisLayer, self) self.siteSelectionListDlg = APISSiteSelectionList(self.iface, self.dbm, self.imageRegistry, self.apisLayer, self) self.imageSelectionListDlg = APISImageSelectionList(self.iface, self.dbm, self.imageRegistry, self.apisLayer, parent=self) self.systemTableEditorDlg = None # Setup film model self.model = QSqlRelationalTableModel(self, self.dbm.db) self.model.setTable("film") self.model.select() while (self.model.canFetchMore()): self.model.fetchMore() self.setupMapper() self.setupComboBox(self.uiProjectSelectionCombo, "projekt", 0, None) self.setupComboBox(self.newFilmDlg.uiProducerCombo, "hersteller", 2, None) self.setupNavigation() self.mapper.toFirst() self.initalLoad = False def setupMapper(self): self.mapper = QDataWidgetMapper(self) self.mapper.currentIndexChanged.connect(self.onCurrentIndexChanged) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setItemDelegate(FilmDelegate()) self.mapper.setModel(self.model) self.mandatoryEditors = [self.uiImageCountEdit, self.uiCameraCombo, self.uiFilmMakeCombo, self.uiFilmModeCombo] self.disableEditorsIfOblique = [self.uiCameraNumberEdit, self.uiCalibratedFocalLengthEdit] # LineEdits & PlainTextEdits self.intValidator = QIntValidator() self.doubleValidator = QDoubleValidator() self.lineEditMaps = { "filmnummer": { "editor": self.uiCurrentFilmNumberEdit }, "hersteller": { "editor": self.uiProducerEdit }, "anzahl_bilder": { "editor": self.uiImageCountEdit, "validator": self.intValidator }, "militaernummer": { "editor": self.uiMilitaryNumberEdit }, "militaernummer_alt": { "editor": self.uiOldMilitaryNumberEdit }, "form1": { "editor": self.uiFormat1Edit }, "form2": { "editor": self.uiFormat2Edit }, "kalibrierungsnummer": { "editor": self.uiCameraNumberEdit }, "kammerkonstante": { "editor": self.uiCalibratedFocalLengthEdit, "validator": self.doubleValidator }, "kassettennummer": { "editor": self.uiCassetteEdit }, "art_ausarbeitung": { "editor": self.uiFilmMakeEdit }, "fotograf": { "editor": self.uiPhotographerEdit }, "pilot": { "editor": self.uiPilotEdit }, "flugzeug": { "editor": self.uiAirplaneEdit }, "abflug_flughafen": { "editor": self.uiDepartureAirportEdit }, "ankunft_flughafen": { "editor": self.uiArrivalAirportEdit }, "flugzeit": { "editor": self.uiFlightDurationEdit }, "wetter": { "editor": self.uiWeatherCodeEdit }, "kommentar": { "editor": self.uiCommentsPTxt } } for key, item in self.lineEditMaps.items(): self.mapper.addMapping(item["editor"], self.model.fieldIndex(key)) if "validator" in item: item["editor"].setValidator(item["validator"]) #item["editor"].textChanged.connect(partial(self.onLineEditChanged, item["editor"])) item["editor"].textChanged.connect(self.onLineEditChanged) #Text #self.mapper.addMapping(self.uiCommentsPTxt, self.model.fieldIndex("kommentar")) # Date and Times self.mapper.addMapping(self.uiFlightDate, self.model.fieldIndex("flugdatum")) #self.mapper.addMapping(self.uiFlightQgsDate, self.model.fieldIndex("flugdatum")) self.mapper.addMapping(self.uiInitalEntryDate, self.model.fieldIndex("datum_ersteintrag")) #self.mapper.addMapping(self.uiInitalEntryQgsDate, self.model.fieldIndex("datum_ersteintrag")) self.mapper.addMapping(self.uiLastChangesDate, self.model.fieldIndex("datum_aenderung")) #self.mapper.addMapping(self.uiLastChangesQgsDate, self.model.fieldIndex("datum_aenderung")) self.mapper.addMapping(self.uiDepartureTime, self.model.fieldIndex("abflug_zeit")) self.mapper.addMapping(self.uiArrivalTime, self.model.fieldIndex("ankunft_zeit")) self.uiDepartureTime.timeChanged.connect(self.onFlightTimeChanged) self.uiArrivalTime.timeChanged.connect(self.onFlightTimeChanged) # ComboBox without Model self.mapper.addMapping(self.uiFilmModeCombo, self.model.fieldIndex("weise")) self.uiFilmModeCombo.editTextChanged.connect(self.onLineEditChanged) completer = QCompleter(self.uiFilmModeCombo.model()) self.uiFilmModeCombo.setCompleter(completer) self.uiFilmModeCombo.lineEdit().setValidator(InListValidator([self.uiFilmModeCombo.itemText(i) for i in range(self.uiFilmModeCombo.count())], self.uiFilmModeCombo.lineEdit(), None, self)) # ComboBox with Model self.comboBoxMaps = { "archiv": { "editor": self.uiArchiveCombo, "table": "hersteller", "modelcolumn": 2, "depend": None }, "kamera": { "editor": self.uiCameraCombo, "table": "kamera", "modelcolumn": 0, "depend": [{"form1": self.uiFormat1Edit}, {"form2": self.uiFormat2Edit}] }, "filmfabrikat": { "editor": self.uiFilmMakeCombo, "table": "film_fabrikat", "modelcolumn": 0, "depend": [{"art": self.uiFilmMakeEdit}] }, "target": { "editor": self.uiTargetCombo, "table": "target", "modelcolumn": 0, "depend": None }, "copyright": { "editor": self.uiCopyrightCombo, "table": "copyright", "modelcolumn": 0, "depend": None } } for key, item in self.comboBoxMaps.items(): self.mapper.addMapping(item["editor"], self.model.fieldIndex(key)) self.setupComboBox(item["editor"], item["table"], item["modelcolumn"], item["depend"]) item["editor"].editTextChanged.connect(self.onLineEditChanged) self.mapper.addMapping(self.uiProjectList, self.model.fieldIndex("projekt")) def fixComboBoxDropDownListSizeAdjustemnt(self, cb): scroll = 0 if cb.count() <= cb.maxVisibleItems() else QApplication.style().pixelMetric(QStyle.PM_ScrollBarExtent) iconWidth = cb.iconSize().width() max = 0 for i in range(cb.count()): width = cb.view().fontMetrics().width(cb.itemText(i)) if max < width: max = width QMessageBox.information(self, "info", "scroll: {0}, max: {1}, icon: {2}".format(scroll, max, iconWidth)) #cb.view().setMinimumWidth(scroll + max) def setupComboBox(self, editor, table, modelColumn, depend): model = QSqlRelationalTableModel(self, self.dbm.db) model.setTable(table) model.removeColumn(0) model.select() tv = QTableView() editor.setView(tv) tv.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) tv.setSelectionMode(QAbstractItemView.SingleSelection) tv.setSelectionBehavior(QAbstractItemView.SelectRows) tv.setAutoScroll(False) editor.setModel(model) editor.setModelColumn(modelColumn) editor.setInsertPolicy(QComboBox.NoInsert) tv.resizeColumnsToContents() tv.resizeRowsToContents() tv.verticalHeader().setVisible(False) tv.horizontalHeader().setVisible(True) #tv.setMinimumWidth(tv.horizontalHeader().length()) tv.horizontalHeader().setStretchLastSection(True) #tv.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) tv.resizeColumnsToContents() scroll = 0 if editor.count() <= editor.maxVisibleItems() else QApplication.style().pixelMetric(QStyle.PM_ScrollBarExtent) tv.setMinimumWidth(tv.horizontalHeader().length() + scroll) #self.fixComboBoxDropDownListSizeAdjustemnt(editor) #editor.resize(tv.horizontalHeader().sizeHint()) completer = QCompleter(editor.model()) editor.setCompleter(completer) #editor.setAutoCompletion(True) editor.lineEdit().setValidator(InListValidator([editor.itemText(i) for i in range(editor.count())], editor.lineEdit(), depend, self)) if depend: editor.currentIndexChanged.connect(partial(self.updateDepends, editor, depend)) def updateDepends(self, editor, depend): for dep in depend: for key, value in dep.items(): idx = editor.model().createIndex(editor.currentIndex(), editor.model().fieldIndex(key)) value.setText(str(editor.model().data(idx))) #QMessageBox.warning(None, "Test", str(idx)) def setupNavigation(self): self.uiFirstFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.FIRST)) self.uiPreviousFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.PREV)) self.uiNextFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.NEXT)) self.uiLastFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.LAST)) self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.intRecordValidator = QIntValidator(1, self.model.rowCount()) self.uiCurrentFilmCountEdit.setValidator(self.intRecordValidator) self.uiCurrentFilmCountEdit.setText(str(self.mapper.currentIndex() + 1)) self.uiCurrentFilmCountEdit.editingFinished.connect(lambda: self.loadRecordById(int(self.uiCurrentFilmCountEdit.text()) - 1)) # QMessageBox.warning(None, "Test", str(self.mapper.itemDelegate())) def enableItemsInLayout(self, layout, enable): for i in range(layout.count()): if layout.itemAt(i).widget(): layout.itemAt(i).widget().setEnabled(enable) def loadRecordByNavigation(self, mode): #self.mapper.submit() #self.submitChanges() self.initalLoad = True if mode == APISFilm.FIRST: self.mapper.toFirst() elif mode == APISFilm.PREV: self.mapper.toPrevious() elif mode == APISFilm.NEXT: self.mapper.toNext() elif mode == APISFilm.LAST: self.mapper.toLast() self.initalLoad = False def loadRecordById(self, id): #self.submitChanges self.initalLoad = True self.mapper.setCurrentIndex(id) self.initalLoad = False def loadRecordByKeyAttribute(self, attribute, value): #self.model.setFilter(attribute + " = '" + value + "'") #self.model.select() # self.mapper.toFirst() query = QSqlQuery(self.dbm.db) #qryStr = "select {0} from film where {0} = '{1}' limit 1".format(attribute, value) #qryStr = "SELECT rowid FROM film WHERE {0} = '{1}' limit 1".format(attribute, value) qryStr = "SELECT" \ " (SELECT COUNT(*)" \ " FROM film AS t2" \ " WHERE t2.rowid < t1.rowid" \ " ) + (" \ " SELECT COUNT(*)" \ " FROM film AS t3" \ " WHERE t3.rowid = t1.rowid AND t3.rowid < t1.rowid" \ " ) AS rowNum" \ " FROM film AS t1" \ " WHERE {0} = '{1}'" \ " ORDER BY t1.rowid ASC".format(attribute, value) query.exec_(qryStr) #QMessageBox.warning(None, "Test", str(query.size()) + ',' + str(query.numRowsAffected())) query.first() fn = query.value(0) if fn is not None: self.loadRecordById(fn) return True else: # Film does not exist QMessageBox.warning(None, "Film Nummer", str("Der Film mit der Nummer {0} existiert nicht!".format(value))) return False #self.model.setFilter('') #self.model.select() #while (self.model.canFetchMore()): #self.model.fetchMore() def submitChanges(self): self.mapper.submit() def onCurrentIndexChanged(self): self.uiCurrentFilmCountEdit.setText(str(self.mapper.currentIndex() + 1)) self.onFilmModeChanged() def onFlightTimeChanged(self): dTime = self.uiDepartureTime.time() aTime = self.uiArrivalTime.time() flightDuration = dTime.secsTo(aTime) self.uiFlightDurationEdit.setText(str(flightDuration / 60)) def disableIfOblique(self, isOblique): for editor in self.disableEditorsIfOblique: editor.setDisabled(isOblique) def onFilmModeChanged(self): if self.uiFilmModeCombo.currentText() == u'schräg': self.disableIfOblique(True) else: self.disableIfOblique(False) def onLineEditChanged(self): sender = self.sender() if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: sender.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(sender.metaObject().className())) self.editorsEdited.append(sender) def onComboBoxChanged(self, editor): pass def addProject(self): editor = self.uiProjectList value = self.uiProjectSelectionCombo.currentText() notInList = True for row in range(editor.count()): if value == editor.item(row).data(0): notInList = False break if notInList: editor.addItem(value) editor.sortItems() if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) def removeProject(self): editor = self.uiProjectList editor.takeItem(self.uiProjectList.currentRow()) if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) def onAccept(self): ''' Check DB Save options when pressing OK button Update Plugin Status ''' self.accept() def onReject(self): ''' Run some actions when the user closes the dialog ''' if self.editMode: res = self.cancelEdit() if res: self.close() else: self.show() else: self.close() def closeEvent(self, e): # Write window size and position to QSettings if self.editMode: self.onReject() else: SetWindowSizeAndPos("film", self.size(), self.pos()) e.accept() def extractGpsFromImages(self): key = self.uiCurrentFilmNumberEdit.text() e2p = Exif2Points(self.iface, key) layer = e2p.run() if layer: self.apisLayer.requestShapeFile(layer, groupName="Flugwege", addToCanvas=True) def exportDetailsPdf(self): if self.printingOptionsDlg is None: self.printingOptionsDlg = APISPrintingOptions(self) self.printingOptionsDlg.setWindowTitle("Druck Optionen: Film") self.printingOptionsDlg.configure(False, False, visPersonalDataChk=True) self.printingOptionsDlg.show() if self.printingOptionsDlg.exec_(): printPersonalData = self.printingOptionsDlg.printPersonalData() APISPrinterQueue([{'type': APISTemplatePrinter.FILM, 'idList': [self.uiCurrentFilmNumberEdit.text()], 'options': {'personalData': printPersonalData}}], OutputMode.MergeNone, openFile=self.printingOptionsDlg.uiOpenFilesChk.isChecked(), openFolder=self.printingOptionsDlg.uiOpenFolderChk.isChecked(), dbm=self.dbm, parent=self) def openSearchFilmDialog(self): """Run method that performs all the real work""" # show the dialog self.searchFilmDlg.show() #self.filmSelectionDlg.uiFilmNumberEdit.setFocus() # Run the dialog event loop and See if OK was pressed if self.searchFilmDlg.exec_(): # QMessageBox.warning(None, "FilmNumber", self.searchFilmDlg.generateSearchQuery()) model = QSqlRelationalTableModel(self, self.dbm.db) model.setTable("film") searchMode, searchFilter = self.searchFilmDlg.generateSearchFilter() # QMessageBox.information(self, "info", searchFilter) model.setFilter(searchFilter) model.select() rc = model.rowCount() while (model.canFetchMore()): model.fetchMore() rc = model.rowCount() query = QSqlQuery(self.dbm.db) searchQuery = "select filmnummer, substr(filmnummer, 3, 8) as 'ohne_hersteller', flugdatum, anzahl_bilder, weise, art_ausarbeitung, militaernummer, militaernummer_alt from film where {0}".format(searchFilter) query.exec_(searchQuery) querySize = 0 while(query.next()): querySize += 1 query.seek(-1) # if model.rowCount(): if querySize > 0: # open film selection list dialog searchListDlg = APISFilmSelectionList(self.iface, model, self.dbm, self.imageRegistry, parent=self) searchListDlg.uiFilmCountLbl.setText(str(rc)) searchListDlg.uiFilmCountDescriptionLbl.setText(u"Film gefunden" if model.rowCount() == 1 else u"Filme gefunden") searchListDlg.uiFilmSearchModeLbl.setText(searchMode) res = searchListDlg.loadFilmListBySqlQuery(query) if res and searchListDlg.exec_(): #QMessageBox.warning(None, "FilmNumber", unicode(searchListDlg.filmNumberToLoad)) self.loadRecordByKeyAttribute("filmnummer", searchListDlg.filmNumberToLoad) else: QMessageBox.warning(self, u"Film Suche", u"Keine Ergebnisse mit den angegebenen Suchkriterien.") self.openSearchFilmDialog() #QMessageBox.warning(None, "FilmNumber", u"{0}, rows: {1}".format(self.searchFilmDlg.generateSearchQuery(), model.rowCount())) # Get Search String/Query #if not self.loadRecordByKeyAttribute("filmnummer", self.filmSelectionDlg.filmNumber()): #self.openFilmSelectionDialog() def openFilmSelectionDialog(self): """Run method that performs all the real work""" self.filmSelectionDlg.show() self.filmSelectionDlg.uiFilmNumberEdit.setFocus() if self.filmSelectionDlg.exec_(): if not self.loadRecordByKeyAttribute("filmnummer", self.filmSelectionDlg.filmNumber()): self.openFilmSelectionDialog() def openNewFilmDialog(self): """Run method that performs all the real work""" self.newFilmDlg.show() if self.newFilmDlg.exec_(): self.addNewFilm(self.newFilmDlg.flightDate(), self.newFilmDlg.useLastEntry(), self.newFilmDlg.producer(), self.newFilmDlg.producerCode()) def openEditWeatherDialog(self): self.editWeatherDlg.setWeatherCode(self.uiWeatherCodeEdit.text()) self.editWeatherDlg.show() if self.editWeatherDlg.exec_(): self.uiWeatherCodeEdit.setText(self.editWeatherDlg.weatherCode()) #self.uiWeatherPTxt.setPlainText(self.editWeatherDlg.weatherDescription()) def generateWeatherCode(self): weatherDescription = self._generateWeatherCode(self.uiWeatherCodeEdit.text()) self.uiWeatherPTxt.setPlainText(weatherDescription) def _generateWeatherCode(self, weatherCode): categories = ["Low Cloud Amount", "Visibility Kilometres", "Low Cloud Height", "Weather", "Remarks Mission", "Remarks Weather"] query = QSqlQuery(self.dbm.db) pos = 0 help = 0 weatherDescription = "" for c in weatherCode: qryStr = "select description from wetter where category = '{0}' and code = '{1}' limit 1".format(categories[pos - help], c) query.exec_(qryStr) query.first() fn = query.value(0) if pos <= 5: weatherDescription += categories[pos] + ': ' + fn if pos < 5: weatherDescription += '\n' else: weatherDescription += '; ' + fn if pos >= 5: help += 1 pos += 1 return weatherDescription def openSystemTableEditorDialog(self, table, updateEditor): if self.dbm: self.systemTableEditorDlg = APISAdvancedInputDialog(self.dbm, table, False, parent=self) if self.systemTableEditorDlg.tableExists: if self.systemTableEditorDlg.exec_(): # See if OK was pressed # rec = self.systemTableEditorDlg.getRecord() # Update updateEditor # self.setupComboBox(self.uiProjectSelectionCombo, "projekt", 0, None) self.updateComboBox(updateEditor) else: QMessageBox.warning(self, "Tabelle nicht vorhanden", "Die Tabelle {0} ist in der APIS Datenbank nicht vorhanden".format(table)) else: QMessageBox.warning(self, "Warning Database", "Die APIS Datenbank konnte nicht gefunden werden.") def updateComboBox(self, updateEditor): updateEditor.model().select() tv = updateEditor.view() tv.resizeRowsToContents() tv.resizeColumnsToContents() scroll = 0 if updateEditor.count() <= updateEditor.maxVisibleItems() else QApplication.style().pixelMetric( QStyle.PM_ScrollBarExtent) tv.setMinimumWidth(tv.horizontalHeader().length() + scroll) updateEditor.setCurrentIndex(updateEditor.count() - 1) if updateEditor.validator(): updateEditor.lineEdit().setValidator( InListValidator([updateEditor.itemText(i) for i in range(updateEditor.count())], updateEditor.lineEdit(), None, self)) def openFlightPathDialog(self, filmList, toClose=None): self.flightPathDlg.viewFilms(filmList) # DEBUG self.flightPathDlg.show() if self.flightPathDlg.exec_(): #TODO load Data in TOC, Close Windows if toClose: toClose.close() self.close() def openSiteSelectionListDialog(self): if self.uiFilmModeCombo.currentIndex() == APISFilm.VERTICAL: fromTable = "luftbild_senk_fp" elif self.uiFilmModeCombo.currentIndex() == APISFilm.OBLIQUE: fromTable = "luftbild_schraeg_fp" query = QSqlQuery(self.dbm.db) query.prepare("SELECT fundortnummer, flurname, katastralgemeinde, fundgewinnung, sicherheit FROM fundort WHERE fundortnummer IN (SELECT DISTINCT fo.fundortnummer FROM fundort fo, {0} WHERE fo.geometry IS NOT NULL AND {0}.geometry IS NOT NULL AND {0}.filmnummer = '{1}' AND Intersects({0}.geometry, fo.geometry) AND fo.ROWID IN (SELECT ROWID FROM SpatialIndex WHERE f_table_name = 'fundort' AND search_frame = {0}.geometry)) ORDER BY katastralgemeindenummer, land, fundortnummer_nn".format(fromTable, self.uiCurrentFilmNumberEdit.text())) query.exec_() info = u"gefunden, die vom Film {0} abgedeckt/geschnitten werden.".format(self.uiCurrentFilmNumberEdit.text()) res = self.siteSelectionListDlg.loadSiteListBySpatialQuery(query, info) if res: self.siteSelectionListDlg.show() if self.siteSelectionListDlg.exec_(): pass def openImageSelectionListDialog(self): if self.uiFilmModeCombo.currentIndex() == APISFilm.VERTICAL: fromTable = 'luftbild_senk_cp' spatialIndicator = 'massstab' elif self.uiFilmModeCombo.currentIndex() == APISFilm.OBLIQUE: fromTable = 'luftbild_schraeg_cp' spatialIndicator = 'radius' query = QSqlQuery(self.dbm.db) query.prepare("SELECT cp.bildnummer AS bildnummer, cp.filmnummer AS filmnummer, cp.{2} AS mst_radius, f.weise AS weise, f.art_ausarbeitung AS art FROM {0} AS cp, film AS f WHERE cp.filmnummer = '{1}' AND f.filmnummer = '{1}'".format(fromTable, self.uiCurrentFilmNumberEdit.text(), spatialIndicator)) query.exec_() res = self.imageSelectionListDlg.loadImageListBySqlQuery(query) if res: self.imageSelectionListDlg.show() if self.imageSelectionListDlg.exec_(): pass def addNewFilm(self, flightDate, useLastEntry, producer, producerCode): self.initalLoad = True self.addMode = True self.startEditMode() row = self.model.rowCount() self.mapper.submit() while (self.model.canFetchMore()): self.model.fetchMore() self.model.insertRow(row) if useLastEntry: #copy last row for c in range(1, self.model.columnCount()): value = self.model.data(self.model.createIndex(row - 1, c)) self.model.setData(self.model.createIndex(row, c), value) editor = self.mapper.mappedWidgetAt(c) if editor and not (value == 'NULL' or value == ''): cName = editor.metaObject().className() if (cName == "QLineEdit" or cName == "QDateEdit") and editor.isReadOnly(): pass else: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) self.mapper.setCurrentIndex(row) self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.uiFlightDate.setDate(flightDate) #self.uiFlightQgsDate.setDate(flightDate) self.uiProducerEdit.setText(producer) self.uiArchiveCombo.lineEdit().setText(producer) if not useLastEntry: self.uiWeatherCodeEdit.setText("9990X") #now = QDateTime.currentDateTime() now = QDate.currentDate() self.uiInitalEntryDate.setDate(now) #self.uiInitalEntryQgsDate.setDateTime(now) self.uiLastChangesDate.setDate(now) #self.uiLastChangesQgsDate.setDateTime(now) self.uiFilmModeCombo.setEnabled(True) #Filmnummer hh = producerCode yyyy = flightDate.toString("yyyy") mm = flightDate.toString("MM") query = QSqlQuery(self.dbm.db) qryStr = "select max(filmnummer_nn) from film where filmnummer_hh_jjjj_mm = '{0}{1}{2}' limit 1".format(hh, yyyy, mm) query.exec_(qryStr) query.first() fn = query.value(0) if isinstance(fn, int): nn = str(fn + 1).zfill(2) else: nn = "01" self.uiCurrentFilmNumberEdit.setText("{0}{1}{2}{3}".format(hh, yyyy, mm, nn)) self.initalLoad = False def removeNewFilm(self): self.initalLoad = True row = self.mapper.currentIndex() self.model.removeRow(row) self.model.submitAll() while (self.model.canFetchMore()): self.model.fetchMore() self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.mapper.toLast() self.initalLoad = False def saveEdits(self): # Check Mandatory fields flag = False for mEditor in self.mandatoryEditors: cName = mEditor.metaObject().className() if cName == 'QLineEdit': value = mEditor.text() elif cName == 'QComboBox': value = mEditor.lineEdit().text() if value.strip() == "": flag = True mEditor.setStyleSheet("{0} {{background-color: rgb(240, 160, 160);}}".format(cName)) if mEditor not in self.editorsEdited: self.editorsEdited.append(mEditor) else: if mEditor in self.editorsEdited: mEditor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(cName)) #else: #mEditor.setStyleSheet("") if flag: QMessageBox.warning(self, self.tr(u"Benötigte Felder Ausfüllen"), self.tr(u"Füllen Sie bitte alle Felder aus, die mit * gekennzeichnet sind.")) return False #saveToModel currIdx = self.mapper.currentIndex() #now = QDateTime.currentDateTime() now = QDate.currentDate() self.uiLastChangesDate.setDate(now) #self.uiLastChangesQgsDate.setDateTime(now) res = self.mapper.submit() if not res: sqlError = self.mapper.model().lastError() QMessageBox.information(self, "Submit", u"Error: {0}, {1}".format(res, sqlError.text())) while (self.model.canFetchMore()): self.model.fetchMore() self.mapper.setCurrentIndex(currIdx) self.endEditMode() return True def cancelEdit(self): if self.editMode: result = QMessageBox.question(self, self.tr(u"Änderungen wurden vorgenommen!"), self.tr(u"Möchten Sie die Änerungen speichern?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) #save or not save if result == QMessageBox.Yes: res = self.saveEdits() if res: return True else: return False elif result == QMessageBox.No: if self.addMode: self.removeNewFilm() self.mapper.setCurrentIndex(self.mapper.currentIndex()) self.endEditMode() return True def startEditMode(self): self.editMode = True self.enableItemsInLayout(self.uiTopHorizontalLayout, False) self.enableItemsInLayout(self.uiBottomHorizontalLayout, False) self.uiOkBtn.setEnabled(False) self.uiSaveBtn.setEnabled(True) self.uiCancelBtn.setEnabled(True) self.editorsEdited = [] def endEditMode(self): self.editMode = False self.addMode = False self.enableItemsInLayout(self.uiTopHorizontalLayout, True) self.enableItemsInLayout(self.uiBottomHorizontalLayout, True) self.uiOkBtn.setEnabled(True) self.uiSaveBtn.setEnabled(False) self.uiCancelBtn.setEnabled(False) for editor in self.editorsEdited: cName = editor.metaObject().className() if (cName == "QLineEdit" or cName == "QDateEdit") and editor.isReadOnly(): editor.setStyleSheet("{0} {{background-color: rgb(218, 218, 218);}}".format(cName)) else: editor.setStyleSheet("") self.editorsEdited = [] self.uiFilmModeCombo.setEnabled(False) def showEvent(self, evnt): # QMessageBox.information(self, "info", "db requires update: {0}".format(self.dbm.dbRequiresUpdate)) if self.dbm.dbRequiresUpdate: self.initalLoad = True for editor in [self.uiArchiveCombo, self.uiCameraCombo, self.uiFilmMakeCombo, self.uiCopyrightCombo, self.uiProjectSelectionCombo, self.newFilmDlg.uiProducerCombo]: self.updateComboBox(editor) currIdx = self.mapper.currentIndex() self.model.select() while (self.model.canFetchMore()): self.model.fetchMore() self.mapper.setCurrentIndex(currIdx) self.dbm.dbRequiresUpdate = False self.initalLoad = False
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) ## tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.tabModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##打开数据表 self.tabModel = QSqlTableModel(self, self.DB) #数据模型 self.tabModel.setTable("employee") #设置数据表 self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit ) #数据保存方式,OnManualSubmit , OnRowChange self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) #排序 if (self.tabModel.select() == False): #查询数据失败 QMessageBox.critical( self, "错误信息", "打开数据表错误,错误信息\n" + self.tabModel.lastError().text()) return self.__getFieldNames() #获取字段名和序号 ##字段显示名 self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") #这两个字段不在tableView中显示 self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("empNo"), Qt.Horizontal, "工号") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Name"), Qt.Horizontal, "姓名") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Gender"), Qt.Horizontal, "性别") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Birthday"), Qt.Horizontal, "出生日期") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Province"), Qt.Horizontal, "省份") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Department"),Qt.Horizontal, "部门") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Salary"), Qt.Horizontal, "工资") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Memo"), Qt.Horizontal, "备注") #这两个字段不在tableView中显示 ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Photo"), Qt.Horizontal, "照片") ##创建界面组件与数据模型的字段之间的数据映射 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) #设置数据模型 self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与tabModel的具体字段之间的联系 self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() #移动到首记录 self.selModel = QItemSelectionModel(self.tabModel) #选择模型 self.selModel.currentChanged.connect(self.do_currentChanged) #当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) #选择行变化时 self.ui.tableView.setModel(self.tabModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) #隐藏列 self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) #隐藏列 ##tableView上为“性别”和“部门”两个字段设置自定义代理组件 strList = ("男", "女") self.__delegateSex = QmyComboBoxDelegate() self.__delegateSex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn( self.fldNum["Gender"], self.__delegateSex) #Combbox选择型 strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) ##更新actions和界面组件的使能状态 self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##选择数据库,打开数据表 def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQLITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##保存修改 def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##取消修改 def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##添加记录 def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) #在末尾添加一个记录 curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) #创建最后一行的ModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置刚插入的行为当前选择行 currow = curIndex.row() #获得当前行 self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000 + self.tabModel.rowCount()) #自动生成编号 self.tabModel.setData( self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() ##插入记录 def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() #QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() #清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除记录 def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() #获取当前选择单元格的模型索引 self.tabModel.removeRow(curIndex.row()) #删除当前行 @pyqtSlot() ##清除照片 def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录,QSqlRecord curRec.setNull("Photo") #设置为空值 self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() #清除界面上的图片显示 @pyqtSlot() ##设置照片 def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) #fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() #QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录QSqlRecord curRec.setValue("Photo", data) #设置字段数据 self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) #在界面上显示 @pyqtSlot() ##涨工资,遍历数据表所有记录 def on_actScan_triggered(self): if (self.tabModel.rowCount() == 0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) #获取当前记录 ## salary=aRec.value("Salary").toFloat() #错误,无需再使用toFloat()函数 salary = aRec.value("Salary") salary = salary * 1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if (self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕") @pyqtSlot(int) ##排序字段变化 def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##升序 def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() ##降序 def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##过滤,男 def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##数据过滤,女 def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女' ") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##取消数据过滤 def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("") ## print(self.tabModel.filter()) ## self.tabModel.select() ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): ##更新actPost和actCancel 的状态 self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) #有未保存修改时可用 self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): #行切换时的状态控制 self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if (current.isValid() == False): self.ui.dbLabPhoto.clear() #清除图片显示 return self.mapper.setCurrentIndex(current.row()) #更新数据映射的行号 curRec = self.tabModel.record(current.row()) #获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): #图片字段内容为空 self.ui.dbLabPhoto.clear() else: ## data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W))
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.splitter) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) def __openTable(self): self.tabModel = QSqlTableModel(self, self.DB) self.tabModel.setTable("employee") self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) if(self.tabModel.select()==False): QMessageBox.critical(self, "错误信息", "打开数据表错误,错误信息\n"+self.tabModel.lastError().text()) return self.__getFieldNames() self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.tabModel) self.selModel.currentChanged.connect(self.do_currentChanged) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.tableView.setModel(self.tabModel) self.ui.tableView.setSelectionModel(self.selModel) self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) strList = ("男", "女") self.__delegatesex = QmyComboBoxDelegate() self.__delegatesex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegatesex) strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) self.ui.actOpenDB.setEnabled(False) self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) def __getFieldNames(self): emptyRec = self.tabModel.record() self.fldNum = {} for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName]=i print(self.fldNum) def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if(current.isValid() == False): self.ui.dbLabPhoto.clear() return self.mapper.setCurrentIndex(current.row()) curRec = self.tabModel.record(current.row()) if(curRec.isNull("Photo")): self.ui.dbLabPhoto.clear() else: data = curRec.value("Photo") pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename ,flt = QFileDialog.getOpenFileName(self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if(res == False): QMessageBox.information(self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) curIndex = self.tabModel.index(self.tabModel.rowCount()-1, 1) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000+self.tabModel.rowCount()) self.tabModel.setData(self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row()) @pyqtSlot() def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setNull("Photo") self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() @pyqtSlot() def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg") if(fileName==''): return file=QFile(fileName) file.open(QIODevice.ReadOnly) try: data = file.readAll() finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setValue("Photo", data) self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actScan_triggered(self): if(self.tabModel.rowCount()==0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) salary = aRec.value("Salary") salary = salary*1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if(self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕了") @pyqtSlot() def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") @pyqtSlot() def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女'") @pyqtSlot() def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("")
class TaskView(QWidget): close = pyqtSignal() def __init__(self, model): super().__init__() self.header = QLabel('') self.desc = QLineEdit() self.date = QDateEdit() self.time = QTimeEdit() self.init_ui() self.mapper = QDataWidgetMapper() self.mapper.setModel(model) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.addMapping(self.desc, TaskModel.col_desc) self.mapper.addMapping(self.date, TaskModel.col_date) self.mapper.addMapping(self.time, TaskModel.col_time) def set_task(self, index): self.mapper.setCurrentIndex(index) self.header.setText('РЕДАКТИРОВАНИЕ ЗАДАЧИ') # text = 'НОВАЯ ЗАДАЧА' # self.date.setDate(QDate().currentDate()) def create_date(self): self.date.setDisplayFormat('dd.MM.yyyy') self.date.setCalendarPopup(True) self.date.setFixedWidth(120) return self.date def create_time(self): self.time.setDisplayFormat('hh.mm') self.time.setFixedWidth(120) return self.time def create_date_buttons(self): date_lt = QHBoxLayout() btn_now = QPushButton('сегодня') btn_now.clicked.connect(lambda: self.date.setDate(QDate().currentDate())) date_lt.addWidget(btn_now, 0, Qt.AlignCenter) btn_tomorrow = QPushButton('завтра') btn_tomorrow.clicked.connect(lambda: self.date.setDate(QDate().currentDate().addDays(1))) date_lt.addWidget(btn_tomorrow, 0, Qt.AlignCenter) btn_week_later = QPushButton('через неделю') btn_week_later.clicked.connect(lambda: self.date.setDate(QDate().currentDate().addDays(7))) date_lt.addWidget(btn_week_later, 0, Qt.AlignCenter) return date_lt # def create_time_choice(self): # self.time.setMaxVisibleItems(15) # self.time.setStyleSheet('QComboBox { combobox-popup: 0; }') # for it in range(24): # self.time.insertItem(it * 2 + 0, '%.2d:00' % it) # self.time.insertItem(it * 2 + 1, '%.2d:30' % it) # # return self.time def save(self): print('save', self.mapper.submit()) self.close.emit() def cancel(self): self.close.emit() def remove(self): self.mapper.model().removeRow(self.mapper.currentIndex()) self.close.emit() def create_control_buttons(self): control_lt = QHBoxLayout() btn_save = QPushButton('Сохранить') btn_save.clicked.connect(self.save) control_lt.addWidget(btn_save, 0, Qt.AlignCenter) btn_cancel = QPushButton('Отменить') btn_cancel.clicked.connect(self.cancel) control_lt.addWidget(btn_cancel, 0, Qt.AlignCenter) btn_remove = QPushButton('Удалить') btn_remove.clicked.connect(self.remove) control_lt.addWidget(btn_remove, 1, Qt.AlignRight) return control_lt def create_main_form(self): fm = QFormLayout() fm.addRow(self.header) fm.addRow(QLabel('')) fm.addRow(self.desc) fm.addRow(QLabel('')) fm.addRow(QLabel('Когда это нужно сделать?')) fm.addRow(self.create_date()) fm.addRow(self.create_date_buttons()) fm.addRow(QLabel('')) fm.addRow(QLabel('Во сколько?')) fm.addRow(self.create_time()) return fm def init_ui(self): layout = QVBoxLayout() layout.addLayout(self.create_main_form()) layout.addStretch() layout.addLayout(self.create_control_buttons()) self.setLayout(layout)
class MasternodeOutputsTab(QWidget): """Widget that is used to select a masternode output.""" def __init__(self, parent): super(MasternodeOutputsTab, self).__init__(parent) self.dialog = parent self.manager = parent.manager include_frozen_checkbox = QCheckBox(_('Include frozen addresses')) include_frozen_checkbox.setChecked(False) self.scan_outputs_button = QPushButton( _('Scan For Masternode Outputs')) def on_scan_outputs(): """Call scan_for_outputs() with whether to include frozen addresses.""" self.scan_for_outputs(include_frozen_checkbox.isChecked()) self.scan_outputs_button.clicked.connect(on_scan_outputs) self.status_edit = QLineEdit() self.status_edit.setReadOnly(True) self.valid_outputs_list = MasternodeOutputsWidget() self.valid_outputs_list.outputSelected.connect(self.set_output) self.collateral_edit = PrevOutWidget() self.collateral_edit.setReadOnly(True) self.mapper = QDataWidgetMapper() self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setModel(self.dialog.masternodes_widget.proxy_model) model = self.dialog.masternodes_widget.model self.mapper.addMapping(self.collateral_edit, model.VIN, b'string') self.save_output_button = QPushButton(_('Save')) self.save_output_button.setEnabled(False) self.save_output_button.clicked.connect(self.save_output) vbox = QVBoxLayout() desc = ' '.join([ 'Use this tab to scan for and choose a collateral payment for your masternode.', 'A valid collateral payment is exactly 15000000 VESTX.' ]) desc = QLabel(_(desc)) desc.setWordWrap(True) vbox.addWidget(desc) status_box = QHBoxLayout() status_box.setContentsMargins(0, 0, 0, 0) status_box.addWidget(QLabel(_('Status:'))) status_box.addWidget(self.status_edit, stretch=1) vbox.addLayout(status_box) valid_outputs_box = QVBoxLayout() valid_outputs_box.setContentsMargins(0, 0, 0, 0) valid_outputs_box.addWidget(QLabel(_('Masternode Outputs:'))) valid_outputs_box.addWidget(self.valid_outputs_list) vbox.addLayout( util.Buttons(include_frozen_checkbox, self.scan_outputs_button)) vbox.addLayout(valid_outputs_box) vbox.addWidget(self.collateral_edit) vbox.addLayout(util.Buttons(self.save_output_button)) self.setLayout(vbox) def scan_for_outputs(self, include_frozen): """Scan for 15000000 VESTX outputs. If one or more is found, populate the list and enable the sign button. """ self.valid_outputs_list.clear() exclude_frozen = not include_frozen coins = list( self.manager.get_masternode_outputs(exclude_frozen=exclude_frozen)) if len(coins) > 0: self.valid_outputs_list.add_outputs(coins) else: self.status_edit.setText( _('No 15000000 VESTX outputs were found.')) self.status_edit.setStyleSheet( util.ColorScheme.RED.as_stylesheet()) def set_output(self, vin): """Set the selected output.""" self.collateral_edit.set_dict(vin) self.save_output_button.setEnabled(True) def save_output(self): """Save the selected output as the current masternode's collateral.""" self.mapper.submit() # Determine the masternode's collateral key using this output. self.dialog.populate_collateral_key() def set_mapper_index(self, row): """Set the row that the data widget mapper should use.""" self.valid_outputs_list.clear() self.status_edit.clear() self.status_edit.setStyleSheet( util.ColorScheme.DEFAULT.as_stylesheet()) self.mapper.setCurrentIndex(row) mn = self.dialog.masternodes_widget.masternode_for_row(row) status_text = _('Masternode has no collateral payment assigned.') can_scan = not mn.announced # Disable the scan_outputs button if the masternode already has an assigned output. if mn.vin.get('value', 0) == COIN * 15000000: can_scan = False self.valid_outputs_list.add_output(mn.vin) status_text = _('Masternode already has a collateral payment.') self.status_edit.setText(_(status_text)) self.scan_outputs_button.setEnabled(can_scan)
class MainWindow(QMainWindow): """ The main window class """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.ui = uic.loadUi("gui/main_window.ui") self.timestamp_filename = None self.video_filename = None self.media_start_time = None self.media_end_time = None self.restart_needed = False self.timer_period = 100 self.is_full_screen = False self.media_started_playing = False self.media_is_playing = False self.original_geometry = None self.mute = False self.timestamp_model = TimestampModel(None, self) self.proxy_model = QSortFilterProxyModel(self) self.ui.list_timestamp.setModel(self.timestamp_model) self.ui.list_timestamp.doubleClicked.connect( lambda event: self.ui.list_timestamp.indexAt(event.pos()).isValid() and self.run() ) self.timer = QTimer() self.timer.timeout.connect(self.update_ui) self.timer.timeout.connect(self.timer_handler) self.timer.start(self.timer_period) self.vlc_instance = vlc.Instance() self.media_player = self.vlc_instance.media_player_new() # if sys.platform == "darwin": # for MacOS # self.ui.frame_video = QMacCocoaViewContainer(0) self.ui.frame_video.doubleClicked.connect(self.toggle_full_screen) self.ui.frame_video.wheel.connect(self.wheel_handler) self.ui.frame_video.keyPressed.connect(self.key_handler) # Set up buttons self.ui.button_run.clicked.connect(self.run) self.ui.button_timestamp_browse.clicked.connect( self.browse_timestamp_handler ) self.ui.button_video_browse.clicked.connect( self.browse_video_handler ) self.play_pause_model = ToggleButtonModel(None, self) self.play_pause_model.setStateMap( { True: { "text": "", "icon": qta.icon("fa.play", scale_factor=0.7) }, False: { "text": "", "icon": qta.icon("fa.pause", scale_factor=0.7) } } ) self.ui.button_play_pause.setModel(self.play_pause_model) self.ui.button_play_pause.clicked.connect(self.play_pause) self.mute_model = ToggleButtonModel(None, self) self.mute_model.setStateMap( { True: { "text": "", "icon": qta.icon("fa.volume-up", scale_factor=0.8) }, False: { "text": "", "icon": qta.icon("fa.volume-off", scale_factor=0.8) } } ) self.ui.button_mute_toggle.setModel(self.mute_model) self.ui.button_mute_toggle.clicked.connect(self.toggle_mute) self.ui.button_full_screen.setIcon( qta.icon("ei.fullscreen", scale_factor=0.6) ) self.ui.button_full_screen.setText("") self.ui.button_full_screen.clicked.connect(self.toggle_full_screen) self.ui.button_speed_up.clicked.connect(self.speed_up_handler) self.ui.button_speed_up.setIcon( qta.icon("fa.arrow-circle-o-up", scale_factor=0.8) ) self.ui.button_speed_up.setText("") self.ui.button_slow_down.clicked.connect(self.slow_down_handler) self.ui.button_slow_down.setIcon( qta.icon("fa.arrow-circle-o-down", scale_factor=0.8) ) self.ui.button_slow_down.setText("") self.ui.button_mark_start.setIcon( qta.icon("fa.quote-left", scale_factor=0.7) ) self.ui.button_mark_start.setText("") self.ui.button_mark_end.setIcon( qta.icon("fa.quote-right", scale_factor=0.7) ) self.ui.button_mark_end.setText("") self.ui.button_add_entry.clicked.connect(self.add_entry) self.ui.button_remove_entry.clicked.connect(self.remove_entry) self.ui.button_mark_start.clicked.connect( lambda: self.set_mark(start_time=int( self.media_player.get_position() * self.media_player.get_media().get_duration())) ) self.ui.button_mark_end.clicked.connect( lambda: self.set_mark(end_time=int( self.media_player.get_position() * self.media_player.get_media().get_duration())) ) self.ui.slider_progress.setTracking(False) self.ui.slider_progress.valueChanged.connect(self.set_media_position) self.ui.slider_volume.valueChanged.connect(self.set_volume) self.ui.entry_description.setReadOnly(True) # Mapper between the table and the entry detail self.mapper = QDataWidgetMapper() self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.ui.button_save.clicked.connect(self.mapper.submit) # Set up default volume self.set_volume(self.ui.slider_volume.value()) self.vlc_events = self.media_player.event_manager() self.vlc_events.event_attach( vlc.EventType.MediaPlayerTimeChanged, self.media_time_change_handler ) # Let our application handle mouse and key input instead of VLC self.media_player.video_set_mouse_input(False) self.media_player.video_set_key_input(False) self.ui.show() def add_entry(self): if not self.timestamp_filename: self._show_error("You haven't chosen a timestamp file yet") row_num = self.timestamp_model.rowCount() self.timestamp_model.insertRow(row_num) start_cell = self.timestamp_model.index(row_num, 0) end_cell = self.timestamp_model.index(row_num, 1) self.timestamp_model.setData(start_cell, TimestampDelta.from_string("")) self.timestamp_model.setData(end_cell, TimestampDelta.from_string("")) def remove_entry(self): if not self.timestamp_filename: self._show_error("You haven't chosen a timestamp file yet") selected = self.ui.list_timestamp.selectionModel().selectedIndexes() if len(selected) == 0: return self.proxy_model.removeRow(selected[0].row()) and self.mapper.submit() def set_media_position(self, position): percentage = position / 10000.0 self.media_player.set_position(percentage) absolute_position = percentage * \ self.media_player.get_media().get_duration() if absolute_position > self.media_end_time: self.media_end_time = -1 def set_mark(self, start_time=None, end_time=None): if len(self.ui.list_timestamp.selectedIndexes()) == 0: blankRowIndex = self.timestamp_model.blankRowIndex() if not blankRowIndex.isValid(): self.add_entry() else: index = self.proxy_model.mapFromSource(blankRowIndex) self.ui.list_timestamp.selectRow(index.row()) selectedIndexes = self.ui.list_timestamp.selectedIndexes() if start_time: self.proxy_model.setData(selectedIndexes[0], TimestampDelta.string_from_int( start_time)) if end_time: self.proxy_model.setData(selectedIndexes[1], TimestampDelta.string_from_int( end_time)) def update_ui(self): self.ui.slider_progress.blockSignals(True) self.ui.slider_progress.setValue( self.media_player.get_position() * 10000 ) # When the video finishes self.ui.slider_progress.blockSignals(False) if self.media_started_playing and \ self.media_player.get_media().get_state() == vlc.State.Ended: self.play_pause_model.setState(True) # Apparently we need to reset the media, otherwise the player # won't play at all self.media_player.set_media(self.media_player.get_media()) self.set_volume(self.ui.slider_volume.value()) self.media_is_playing = False self.media_started_playing = False self.run() def timer_handler(self): """ This is a workaround, because for some reason we can't call set_time() inside the MediaPlayerTimeChanged handler (as the video just stops playing) """ if self.restart_needed: self.media_player.set_time(self.media_start_time) self.restart_needed = False def key_handler(self, event): if event.key() == Qt.Key_Escape and self.is_full_screen: self.toggle_full_screen() if event.key() == Qt.Key_F: self.toggle_full_screen() if event.key() == Qt.Key_Space: self.play_pause() def wheel_handler(self, event): self.modify_volume(1 if event.angleDelta().y() > 0 else -1) def toggle_mute(self): self.media_player.audio_set_mute(not self.media_player.audio_get_mute()) self.mute = not self.mute self.mute_model.setState(not self.mute) def modify_volume(self, delta_percent): new_volume = self.media_player.audio_get_volume() + delta_percent if new_volume < 0: new_volume = 0 elif new_volume > 40: new_volume = 40 self.media_player.audio_set_volume(new_volume) self.ui.slider_volume.setValue(self.media_player.audio_get_volume()) def set_volume(self, new_volume): self.media_player.audio_set_volume(new_volume) def speed_up_handler(self): self.modify_rate(0.1) def slow_down_handler(self): self.modify_rate(-0.1) def modify_rate(self, delta_percent): new_rate = self.media_player.get_rate() + delta_percent if new_rate < 0.2 or new_rate > 2.0: return self.media_player.set_rate(new_rate) def media_time_change_handler(self, _): if self.media_end_time == -1: return if self.media_player.get_time() > self.media_end_time: self.restart_needed = True def update_slider_highlight(self): if self.ui.list_timestamp.selectionModel().hasSelection(): selected_row = self.ui.list_timestamp.selectionModel(). \ selectedRows()[0] self.media_start_time = self.ui.list_timestamp.model().data( selected_row.model().index(selected_row.row(), 0), Qt.UserRole ) self.media_end_time = self.ui.list_timestamp.model().data( selected_row.model().index(selected_row.row(), 1), Qt.UserRole ) duration = self.media_player.get_media().get_duration() self.media_end_time = self.media_end_time \ if self.media_end_time != 0 else duration if self.media_start_time > self.media_end_time: raise ValueError("Start time cannot be later than end time") if self.media_start_time > duration: raise ValueError("Start time not within video duration") if self.media_end_time > duration: raise ValueError("End time not within video duration") slider_start_pos = (self.media_start_time / duration) * \ (self.ui.slider_progress.maximum() - self.ui.slider_progress.minimum()) slider_end_pos = (self.media_end_time / duration) * \ (self.ui.slider_progress.maximum() - self.ui.slider_progress.minimum()) self.ui.slider_progress.setHighlight( int(slider_start_pos), int(slider_end_pos) ) else: self.media_start_time = 0 self.media_end_time = -1 def run(self): """ Execute the loop """ if self.timestamp_filename is None: self._show_error("No timestamp file chosen") return if self.video_filename is None: self._show_error("No video file chosen") return try: self.update_slider_highlight() self.media_player.play() self.media_player.set_time(self.media_start_time) self.media_started_playing = True self.media_is_playing = True self.play_pause_model.setState(False) except Exception as ex: self._show_error(str(ex)) print(traceback.format_exc()) def play_pause(self): """Toggle play/pause status """ if not self.media_started_playing: self.run() return if self.media_is_playing: self.media_player.pause() else: self.media_player.play() self.media_is_playing = not self.media_is_playing self.play_pause_model.setState(not self.media_is_playing) def toggle_full_screen(self): if self.is_full_screen: # TODO Artifacts still happen some time when exiting full screen # in X11 self.ui.frame_media.showNormal() self.ui.frame_media.restoreGeometry(self.original_geometry) self.ui.frame_media.setParent(self.ui.widget_central) self.ui.layout_main.addWidget(self.ui.frame_media, 2, 3, 3, 1) # self.ui.frame_media.ensurePolished() else: self.ui.frame_media.setParent(None) self.ui.frame_media.setWindowFlags(Qt.FramelessWindowHint | Qt.CustomizeWindowHint) self.original_geometry = self.ui.frame_media.saveGeometry() desktop = QApplication.desktop() rect = desktop.screenGeometry(desktop.screenNumber(QCursor.pos())) self.ui.frame_media.setGeometry(rect) self.ui.frame_media.showFullScreen() self.ui.frame_media.show() self.ui.frame_video.setFocus() self.is_full_screen = not self.is_full_screen def browse_timestamp_handler(self): """ Handler when the timestamp browser button is clicked """ tmp_name, _ = QFileDialog.getOpenFileName( self, "Choose Timestamp file", None, "Timestamp File (*.tmsp);;All Files (*)" ) if not tmp_name: return self.set_timestamp_filename(QDir.toNativeSeparators(tmp_name)) def _sort_model(self): self.ui.list_timestamp.sortByColumn(0, Qt.AscendingOrder) def _select_blank_row(self, parent, start, end): self.ui.list_timestamp.selectRow(start) def set_timestamp_filename(self, filename): """ Set the timestamp file name """ if not os.path.isfile(filename): self._show_error("Cannot access timestamp file " + filename) return try: self.timestamp_model = TimestampModel(filename, self) self.timestamp_model.timeParseError.connect( lambda err: self._show_error(err) ) self.proxy_model.setSortRole(Qt.UserRole) self.proxy_model.dataChanged.connect(self._sort_model) self.proxy_model.dataChanged.connect(self.update_slider_highlight) self.proxy_model.setSourceModel(self.timestamp_model) self.proxy_model.rowsInserted.connect(self._sort_model) self.proxy_model.rowsInserted.connect(self._select_blank_row) self.ui.list_timestamp.setModel(self.proxy_model) self.timestamp_filename = filename self.ui.entry_timestamp.setText(self.timestamp_filename) self.mapper.setModel(self.proxy_model) self.mapper.addMapping(self.ui.entry_start_time, 0) self.mapper.addMapping(self.ui.entry_end_time, 1) self.mapper.addMapping(self.ui.entry_description, 2) self.ui.list_timestamp.selectionModel().selectionChanged.connect( self.timestamp_selection_changed) self._sort_model() directory = os.path.dirname(self.timestamp_filename) basename = os.path.basename(self.timestamp_filename) timestamp_name_without_ext = os.path.splitext(basename)[0] for file_in_dir in os.listdir(directory): current_filename = os.path.splitext(file_in_dir)[0] found_video = (current_filename == timestamp_name_without_ext and file_in_dir != basename) if found_video: found_video_file = os.path.join(directory, file_in_dir) self.set_video_filename(found_video_file) break except ValueError as err: self._show_error("Timestamp file is invalid") def timestamp_selection_changed(self, selected, deselected): if len(selected) > 0: self.mapper.setCurrentModelIndex(selected.indexes()[0]) self.ui.button_save.setEnabled(True) self.ui.button_remove_entry.setEnabled(True) self.ui.entry_start_time.setReadOnly(False) self.ui.entry_end_time.setReadOnly(False) self.ui.entry_description.setReadOnly(False) else: self.mapper.setCurrentModelIndex(QModelIndex()) self.ui.button_save.setEnabled(False) self.ui.button_remove_entry.setEnabled(False) self.ui.entry_start_time.clear() self.ui.entry_end_time.clear() self.ui.entry_description.clear() self.ui.entry_start_time.setReadOnly(True) self.ui.entry_end_time.setReadOnly(True) self.ui.entry_description.setReadOnly(True) def set_video_filename(self, filename): """ Set the video filename """ if not os.path.isfile(filename): self._show_error("Cannot access video file " + filename) return self.video_filename = filename media = self.vlc_instance.media_new(self.video_filename) media.parse() if not media.get_duration(): self._show_error("Cannot play this media file") self.media_player.set_media(None) self.video_filename = None else: self.media_player.set_media(media) if sys.platform.startswith('linux'): # for Linux using the X Server self.media_player.set_xwindow(self.ui.frame_video.winId()) elif sys.platform == "win32": # for Windows self.media_player.set_hwnd(self.ui.frame_video.winId()) elif sys.platform == "darwin": # for MacOS self.media_player.set_nsobject(self.ui.frame_video.winId()) self.ui.entry_video.setText(self.video_filename) self.media_started_playing = False self.media_is_playing = False self.set_volume(self.ui.slider_volume.value()) self.play_pause_model.setState(True) def browse_video_handler(self): """ Handler when the video browse button is clicked """ tmp_name, _ = QFileDialog.getOpenFileName( self, "Choose Video file", None, "All Files (*)" ) if not tmp_name: return self.set_video_filename(QDir.toNativeSeparators(tmp_name)) def _show_error(self, message, title="Error"): QMessageBox.warning(self, title, message)
class APISSharding(QDialog, FORM_CLASS): shardingEditsSaved = pyqtSignal(bool) def __init__(self, iface, dbm, parent=None): """Constructor.""" super(APISSharding, self).__init__(parent) self.iface = iface self.dbm = dbm self.setupUi(self) # Initial window size/pos last saved. Use default values for first time if GetWindowSize("sharding"): self.resize(GetWindowSize("sharding")) self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat) self.editMode = False self.addMode = False self.initalLoad = True # Signals/Slot Connections self.rejected.connect(self.onReject) #self.uiButtonBox.rejected.connect(self.onReject) self.uiOkBtn.clicked.connect(self.onAccept) self.uiCancelBtn.clicked.connect(self.cancelEdit) self.uiSaveBtn.clicked.connect(self.saveEdits) mViewPictures = QMenu() aViewPicturesPreview = mViewPictures.addAction( QIcon( os.path.join(QSettings().value("APIS/plugin_dir"), 'ui', 'icons', 'image.png')), "in Vorschau") aViewPicturesPreview.triggered.connect(self.viewPictures) aViewPicturesFolder = mViewPictures.addAction( QIcon( os.path.join(QSettings().value("APIS/plugin_dir"), 'ui', 'icons', 'image.png')), "in Ordner") aViewPicturesFolder.triggered.connect(self.openPictures) self.uiViewPicturesTBtn.setMenu(mViewPictures) self.uiViewPicturesTBtn.clicked.connect( self.uiViewPicturesTBtn.showMenu) mViewSketches = QMenu() aViewSketchesPreview = mViewSketches.addAction( QIcon( os.path.join(QSettings().value("APIS/plugin_dir"), 'ui', 'icons', 'sketch.png')), "in Vorschau") aViewSketchesPreview.triggered.connect(self.viewSketches) aViewSketchesFolder = mViewSketches.addAction( QIcon( os.path.join(QSettings().value("APIS/plugin_dir"), 'ui', 'icons', 'sketch.png')), "in Ordner") aViewSketchesFolder.triggered.connect(self.openSketches) self.uiViewSketchesTBtn.setMenu(mViewSketches) self.uiViewSketchesTBtn.clicked.connect( self.uiViewSketchesTBtn.showMenu) self.initalLoad = False def openSharding(self, siteNumber, shardingNumber): self.initalLoad = True self.siteNumber = siteNumber self.shardingNumber = shardingNumber #QMessageBox.warning(None, self.tr(u"Neu"), self.tr(u"{0}, {1}".format(siteNumber, shardingNumber))) # Setup sharding model self.model = QSqlRelationalTableModel(self, self.dbm.db) self.model.setTable("begehung") self.model.setFilter("fundortnummer='{0}' AND begehung='{1}'".format( self.siteNumber, self.shardingNumber)) res = self.model.select() self.setupMapper() self.mapper.toFirst() self.setKgNameAndCode() self.initalLoad = False def setKgNameAndCode(self): query = QSqlQuery(self.dbm.db) qryStr = u"select CASE WHEN katastralgemeinde IS NULL AND katastralgemeindenummer IS NULL THEN '--' ELSE katastralgemeindenummer || ' - ' || katastralgemeinde END AS kg FROM fundort WHERE fundortnummer = '{0}'".format( self.siteNumber) query.exec_(qryStr) query.first() self.uiCadastralCommunityEdit.setText(query.value(0)) def setupMapper(self): self.mapper = QDataWidgetMapper(self) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setItemDelegate(ShardingDelegate()) self.mapper.setModel(self.model) self.mandatoryEditors = [self.uiShardingDate] # LineEdits & PlainTextEdits self.intValidator = QIntValidator() self.doubleValidator = QDoubleValidator() self.lineEditMaps = { "fundortnummer": { "editor": self.uiSiteNumberEdit }, "begehung": { "editor": self.uiShardingNumberEdit }, "name": { "editor": self.uiNameEdit }, "parzelle": { "editor": self.uiPlotPTxt }, "sichtbarkeit": { "editor": self.uiVisibilityEdit }, "verbleib": { "editor": self.uiWhereaboutsEdit }, "funde": { "editor": self.uiFindsPTxt }, "morphologie": { "editor": self.uiMorphologyPTxt }, "sonstiges": { "editor": self.uiMiscellaneousPTxt } } for key, item in self.lineEditMaps.items(): self.mapper.addMapping(item["editor"], self.model.fieldIndex(key)) if "validator" in item: item["editor"].setValidator(item["validator"]) #item["editor"].textChanged.connect(partial(self.onLineEditChanged, item["editor"])) item["editor"].textChanged.connect(self.onLineEditChanged) # Date and Times self.mapper.addMapping(self.uiShardingDate, self.model.fieldIndex("datum")) # ComboBox without Model self.mapper.addMapping(self.uiShardingTypeCombo, self.model.fieldIndex("begehtyp")) self.uiShardingTypeCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiShardingTypeCombo.setAutoCompletion(True) self.uiShardingTypeCombo.lineEdit().setValidator( InListValidator([ self.uiShardingTypeCombo.itemText(i) for i in range(self.uiShardingTypeCombo.count()) ], self.uiShardingTypeCombo.lineEdit(), None, self)) # ComboBox without Model self.mapper.addMapping(self.uiConditionPlantCoverCombo, self.model.fieldIndex("zustand_bewuchs")) self.uiConditionPlantCoverCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiConditionPlantCoverCombo.setAutoCompletion(True) self.uiConditionPlantCoverCombo.lineEdit().setValidator( InListValidator([ self.uiConditionPlantCoverCombo.itemText(i) for i in range(self.uiConditionPlantCoverCombo.count()) ], self.uiConditionPlantCoverCombo.lineEdit(), None, self)) # ComboBox without Model self.mapper.addMapping(self.uiConditionLightCombo, self.model.fieldIndex("zustand_licht")) self.uiConditionLightCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiConditionLightCombo.setAutoCompletion(True) self.uiConditionLightCombo.lineEdit().setValidator( InListValidator([ self.uiConditionLightCombo.itemText(i) for i in range(self.uiConditionLightCombo.count()) ], self.uiConditionLightCombo.lineEdit(), None, self)) # ComboBox without Model self.mapper.addMapping(self.uiConditionSoilCombo, self.model.fieldIndex("zustand_boden")) self.uiConditionSoilCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiConditionSoilCombo.setAutoCompletion(True) self.uiConditionSoilCombo.lineEdit().setValidator( InListValidator([ self.uiConditionSoilCombo.itemText(i) for i in range(self.uiConditionSoilCombo.count()) ], self.uiConditionSoilCombo.lineEdit(), None, self)) # ComboBox without Model self.mapper.addMapping(self.uiConditionMoistureCombo, self.model.fieldIndex("zustand_feuchtigkeit")) self.uiConditionMoistureCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiConditionMoistureCombo.setAutoCompletion(True) self.uiConditionMoistureCombo.lineEdit().setValidator( InListValidator([ self.uiConditionMoistureCombo.itemText(i) for i in range(self.uiConditionMoistureCombo.count()) ], self.uiConditionMoistureCombo.lineEdit(), None, self)) # ComboBox without Model self.mapper.addMapping(self.uiConditionRainCombo, self.model.fieldIndex("zustand_abgeregnet")) self.uiConditionRainCombo.editTextChanged.connect( self.onLineEditChanged) # FIXME Pyqt5 AutoCompletion #self.uiConditionRainCombo.setAutoCompletion(True) self.uiConditionRainCombo.lineEdit().setValidator( InListValidator([ self.uiConditionRainCombo.itemText(i) for i in range(self.uiConditionRainCombo.count()) ], self.uiConditionRainCombo.lineEdit(), None, self)) def onLineEditChanged(self): sender = self.sender() if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: sender.setStyleSheet( "{0} {{background-color: rgb(153, 204, 255);}}".format( sender.metaObject().className())) self.editorsEdited.append(sender) def onAccept(self): ''' Check DB Save options when pressing OK button Update Plugin Status ''' # Save Settings SetWindowSize("sharding", self.size()) self.accept() def onReject(self): ''' Run some actions when the user closes the dialog ''' if self.editMode: res = self.cancelEdit() if res: SetWindowSize("sharding", self.size()) self.close() else: self.show() else: SetWindowSize("sharding", self.size()) self.close() def addNewSharding(self, siteNumber): self.initalLoad = True self.siteNumber = siteNumber # get new sharding number query = QSqlQuery(self.dbm.db) qryStr = "SELECT CASE WHEN max(begehung) IS NULL THEN 1 ELSE max(begehung)+1 END begehungNeu FROM begehung WHERE fundortnummer='{0}'".format( self.siteNumber) query.exec_(qryStr) query.first() self.shardingNumber = query.value(0) self.model = QSqlRelationalTableModel(self, self.dbm.db) self.model.setTable("begehung") self.model.setFilter("fundortnummer='{0}'".format(self.siteNumber)) res = self.model.select() #self.model.submitAll() while (self.model.canFetchMore()): self.model.fetchMore() row = self.model.rowCount() #QMessageBox.information(None, "begehung", "{0}".format(row)) self.model.insertRow(row) #QMessageBox.information(None, "begehung", "{0}".format(self.model.rowCount())) self.setupMapper() self.mapper.toLast() self.addMode = True self.startEditMode() # self.mapper.submit() # self.model.insertRow(row) # self.mapper.setCurrentIndex(row) self.uiSiteNumberEdit.setText(self.siteNumber) self.uiShardingNumberEdit.setText(str(self.shardingNumber)) now = QDate.currentDate() self.uiShardingDate.setDate(now) self.setKgNameAndCode() #QMessageBox.warning(None, self.tr(u"Neu"), self.tr(u"{0}, {1}".format(siteNumber,nn))) self.initalLoad = False def removeNewSharding(self): self.initalLoad = True row = self.mapper.currentIndex() self.model.removeRow(row + 1) self.model.submitAll() while (self.model.canFetchMore()): self.model.fetchMore() self.mapper.toLast() self.initalLoad = False def saveEdits(self): #Check Mandatory fields flag = False for mEditor in self.mandatoryEditors: cName = mEditor.metaObject().className() if cName == 'QDateEdit': value = mEditor.date().toString("yyyy-MM-dd") elif cName == 'QLineEdit': value = mEditor.text() elif cName == 'QComboBox': if mEditor.isEditable(): value = mEditor.lineEdit().text() else: if mEditor.currentIndex == -1: value = '' else: value = '1' if value.strip() == "": flag = True mEditor.setStyleSheet( "{0} {{background-color: rgb(240, 160, 160);}}".format( cName)) if mEditor not in self.editorsEdited: self.editorsEdited.append(mEditor) else: if mEditor in self.editorsEdited: mEditor.setStyleSheet( "{0} {{background-color: rgb(153, 204, 255);}}".format( cName)) #else: #mEditor.setStyleSheet("") if flag: QMessageBox.warning( self, self.tr(u"Benötigte Felder Ausfüllen"), self. tr(u"Füllen Sie bitte alle Felder aus, die mit * gekennzeichnet sind." )) return False #saveToModel currIdx = self.mapper.currentIndex() #QMessageBox.information(None, "begehung", "{0}".format(currIdx)) #now = QDate.currentDate() #self.uiLastChangesDate.setDate(now) self.mapper.submit() self.mapper.setCurrentIndex(currIdx) # emit signal self.shardingEditsSaved.emit(True) self.endEditMode() return True def cancelEdit(self): currIdx = self.mapper.currentIndex() if self.editMode: result = QMessageBox.question( self, self.tr(u"Änderungen wurden vorgenommen!"), self.tr(u"Möchten Sie die Änerungen speichern?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) #save or not save if result == QMessageBox.Yes: res = self.saveEdits() if res: return True else: return False elif result == QMessageBox.No: if self.addMode: #self.close() self.done(1) self.removeNewSharding() self.endEditMode() return True else: self.mapper.setCurrentIndex(currIdx) self.endEditMode() return True def startEditMode(self): self.editMode = True self.uiOkBtn.setEnabled(False) self.uiSaveBtn.setEnabled(True) self.uiCancelBtn.setEnabled(True) self.editorsEdited = [] self.uiShardingDate.setReadOnly(not self.addMode) if self.uiShardingDate.isReadOnly(): self.uiShardingDate.setStyleSheet( "background-color: rgb(218, 218, 218);") else: self.uiShardingDate.setStyleSheet("") def endEditMode(self): self.editMode = False self.addMode = False self.uiOkBtn.setEnabled(True) self.uiSaveBtn.setEnabled(False) self.uiCancelBtn.setEnabled(False) self.uiShardingDate.setReadOnly(not self.addMode) self.uiShardingDate.setStyleSheet( "background-color: rgb(218, 218, 218);") for editor in self.editorsEdited: cName = editor.metaObject().className() if (cName == "QLineEdit" or cName == "QDateEdit") and editor.isReadOnly(): editor.setStyleSheet( "{0} {{background-color: rgb(218, 218, 218);}}".format( cName)) else: editor.setStyleSheet("") self.editorsEdited = [] def viewPictures(self): dirName = self.settings.value("APIS/insp_image_dir") folderNameType = self.settings.value("APIS/insp_image_foto_dir") folderNameSite = self.getFolderNameSite(self.siteNumber) path = dirName + u'\\' + folderNameSite + u'\\' + folderNameType self.loadInImageViewer(path) def openPictures(self): dirName = self.settings.value("APIS/insp_image_dir") folderNameType = self.settings.value("APIS/insp_image_foto_dir") folderNameSite = self.getFolderNameSite(self.siteNumber) path = dirName + u'\\' + folderNameSite + u'\\' + folderNameType if not OpenFileOrFolder(path): QMessageBox.information( self, u"Begehung", u"Das Verzeichnis '{0}' wurde nicht gefunden.".format(path)) def viewSketches(self): dirName = self.settings.value("APIS/insp_image_dir") folderNameType = self.settings.value("APIS/insp_image_sketch_dir") folderNameSite = self.getFolderNameSite(self.siteNumber) path = dirName + u'\\' + folderNameSite + u'\\' + folderNameType self.loadInImageViewer(path) def openSketches(self): dirName = self.settings.value("APIS/insp_image_dir") folderNameType = self.settings.value("APIS/insp_image_sketch_dir") folderNameSite = self.getFolderNameSite(self.siteNumber) path = dirName + u'\\' + folderNameSite + u'\\' + folderNameType if not OpenFileOrFolder(path): QMessageBox.information( None, u"Begehung", u"Das Verzeichnis '{0}' wurde nicht gefunden.".format(path)) def getFolderNameSite(self, siteNumber): query = QSqlQuery(self.dbm.db) #qryStr = u"SELECT trim(katastralgemeinde) || ' ' || trim(katastralgemeindenummer) || '.' || substr('000' || fundortnummer_nn_legacy, -3, 3) AS folderName FROM fundort f WHERE f.fundortnummer='{0}'".format(siteNumber) query.prepare( u"SELECT land || '\\' || CASE WHEN land = 'AUT' THEN replace(replace(replace(replace(lower(trim(katastralgemeinde)), '.',''), '-', ' '), '(', ''), ')', '') || ' ' ELSE '' END || substr('000000' || fundortnummer_nn, -6, 6) AS folderName FROM fundort f WHERE f.fundortnummer='{0}'" .format(siteNumber)) query.exec_() query.first() return query.value(0) def loadInImageViewer(self, path): dir = QDir(path) if dir.exists(): entryList = dir.entryList(['*.jpg'], QDir.Files) if len(entryList) > 0: # load in thumb viewer # QMessageBox.information(None, u"Begehung", u",".join(entryList)) imagePathList = [] for image in entryList: imagePathList.append(path + u'\\' + image) widget = APISThumbViewer() widget.load(imagePathList) widget.show() if widget.exec_(): pass # app.exec_() else: QMessageBox.information( self, u"Begehung", u"Es wurden keine Dateien [*.jpg] für diesen Fundort gefunden." ) else: QMessageBox.information( self, u"Begehung", u"Das Verzeichnis '{0}' wurde nicht gefunden.".format(path))
class MainWindow(QMainWindow): """ The main window class """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.ui = uic.loadUi("gui/main_window.ui") self.timestamp_filename = None self.video_filename = None self.media_start_time = None self.media_end_time = None self.restart_needed = False self.timer_period = 100 self.is_full_screen = False self.media_started_playing = False self.media_is_playing = False self.original_geometry = None self.mute = False self.timestamp_model = TimestampModel(None, self) self.proxy_model = QSortFilterProxyModel(self) self.ui.list_timestamp.setModel(self.timestamp_model) self.ui.list_timestamp.doubleClicked.connect( lambda event: self.ui.list_timestamp.indexAt(event.pos()).isValid() and self.run() ) self.timer = QTimer() self.timer.timeout.connect(self.update_ui) self.timer.timeout.connect(self.timer_handler) self.timer.start(self.timer_period) self.vlc_instance = vlc.Instance() self.media_player = self.vlc_instance.media_player_new() # if sys.platform == "darwin": # for MacOS # self.ui.frame_video = QMacCocoaViewContainer(0) self.ui.frame_video.doubleClicked.connect(self.toggle_full_screen) self.ui.frame_video.wheel.connect(self.wheel_handler) self.ui.frame_video.keyPressed.connect(self.key_handler) # Set up Labels: # self.ui.lblVideoName. # self.displayed_video_title = bind("self.ui.lblVideoName", "text", str) self.ui.lblVideoName.setText(self.video_filename) self.ui.lblVideoSubtitle.setText("") self.ui.dateTimeEdit.setHidden(True) self.ui.lblCurrentFrame.setText("") self.ui.lblTotalFrames.setText("") self.ui.lblCurrentTime.setText("") self.ui.lblTotalDuration.setText("") self.ui.lblFileFPS.setText("") self.ui.spinBoxFrameJumpMultiplier.value = 1 # Set up buttons self.ui.button_run.clicked.connect(self.run) self.ui.button_timestamp_browse.clicked.connect( self.browse_timestamp_handler ) self.ui.button_timestamp_create.clicked.connect( self.create_timestamp_file_handler ) self.ui.button_video_browse.clicked.connect( self.browse_video_handler ) # Set up directional buttons self.ui.btnSkipLeft.clicked.connect(self.skip_left_handler) self.ui.btnSkipRight.clicked.connect(self.skip_right_handler) self.ui.btnLeft.clicked.connect(self.seek_left_handler) self.ui.btnRight.clicked.connect(self.seek_right_handler) self.play_pause_model = ToggleButtonModel(None, self) self.play_pause_model.setStateMap( { True: { "text": "", "icon": qta.icon("fa.play", scale_factor=0.7) }, False: { "text": "", "icon": qta.icon("fa.pause", scale_factor=0.7) } } ) self.ui.button_play_pause.setModel(self.play_pause_model) self.ui.button_play_pause.clicked.connect(self.play_pause) self.mute_model = ToggleButtonModel(None, self) self.mute_model.setStateMap( { True: { "text": "", "icon": qta.icon("fa.volume-up", scale_factor=0.8) }, False: { "text": "", "icon": qta.icon("fa.volume-off", scale_factor=0.8) } } ) self.ui.button_mute_toggle.setModel(self.mute_model) self.ui.button_mute_toggle.clicked.connect(self.toggle_mute) self.ui.button_full_screen.setIcon( qta.icon("ei.fullscreen", scale_factor=0.6) ) self.ui.button_full_screen.setText("") self.ui.button_full_screen.clicked.connect(self.toggle_full_screen) self.ui.button_speed_up.clicked.connect(self.speed_up_handler) self.ui.button_speed_up.setIcon( qta.icon("fa.arrow-circle-o-up", scale_factor=0.8) ) self.ui.button_speed_up.setText("") self.ui.button_slow_down.clicked.connect(self.slow_down_handler) self.ui.button_slow_down.setIcon( qta.icon("fa.arrow-circle-o-down", scale_factor=0.8) ) self.ui.button_slow_down.setText("") self.ui.button_mark_start.setIcon( qta.icon("fa.quote-left", scale_factor=0.7) ) self.ui.button_mark_start.setText("") self.ui.button_mark_end.setIcon( qta.icon("fa.quote-right", scale_factor=0.7) ) self.ui.button_mark_end.setText("") self.ui.button_add_entry.clicked.connect(self.add_entry) self.ui.button_remove_entry.clicked.connect(self.remove_entry) self.ui.button_mark_start.clicked.connect( lambda: self.set_mark(start_time=int( self.media_player.get_position() * self.media_player.get_media().get_duration())) ) self.ui.button_mark_end.clicked.connect( lambda: self.set_mark(end_time=int( self.media_player.get_position() * self.media_player.get_media().get_duration())) ) self.ui.slider_progress.setTracking(False) self.ui.slider_progress.valueChanged.connect(self.set_media_position) self.ui.slider_volume.valueChanged.connect(self.set_volume) self.ui.entry_description.setReadOnly(True) # Mapper between the table and the entry detail self.mapper = QDataWidgetMapper() self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.ui.button_save.clicked.connect(self.mapper.submit) # Set up default volume self.set_volume(self.ui.slider_volume.value()) self.vlc_events = self.media_player.event_manager() self.vlc_events.event_attach( vlc.EventType.MediaPlayerTimeChanged, self.media_time_change_handler ) # Let our application handle mouse and key input instead of VLC self.media_player.video_set_mouse_input(False) self.media_player.video_set_key_input(False) self.ui.show() def add_entry(self): if not self.timestamp_filename: self._show_error("You haven't chosen a timestamp file yet") row_num = self.timestamp_model.rowCount() self.timestamp_model.insertRow(row_num) start_cell = self.timestamp_model.index(row_num, 0) end_cell = self.timestamp_model.index(row_num, 1) self.timestamp_model.setData(start_cell, TimestampDelta.from_string("")) self.timestamp_model.setData(end_cell, TimestampDelta.from_string("")) def remove_entry(self): if not self.timestamp_filename: self._show_error("You haven't chosen a timestamp file yet") selected = self.ui.list_timestamp.selectionModel().selectedIndexes() if len(selected) == 0: return self.proxy_model.removeRow(selected[0].row()) and self.mapper.submit() def set_media_position(self, position): percentage = position / 10000.0 self.media_player.set_position(percentage) absolute_position = percentage * \ self.media_player.get_media().get_duration() if absolute_position > self.media_end_time: self.media_end_time = -1 def set_mark(self, start_time=None, end_time=None): if len(self.ui.list_timestamp.selectedIndexes()) == 0: blankRowIndex = self.timestamp_model.blankRowIndex() if not blankRowIndex.isValid(): self.add_entry() else: index = self.proxy_model.mapFromSource(blankRowIndex) self.ui.list_timestamp.selectRow(index.row()) selectedIndexes = self.ui.list_timestamp.selectedIndexes() if start_time: self.proxy_model.setData(selectedIndexes[0], TimestampDelta.string_from_int( start_time)) if end_time: self.proxy_model.setData(selectedIndexes[1], TimestampDelta.string_from_int( end_time)) def update_ui(self): self.ui.slider_progress.blockSignals(True) self.ui.slider_progress.setValue( self.media_player.get_position() * 10000 ) #print(self.media_player.get_position() * 10000) self.update_video_file_play_labels() # When the video finishes self.ui.slider_progress.blockSignals(False) if self.media_started_playing and \ self.media_player.get_media().get_state() == vlc.State.Ended: self.play_pause_model.setState(True) # Apparently we need to reset the media, otherwise the player # won't play at all self.media_player.set_media(self.media_player.get_media()) self.set_volume(self.ui.slider_volume.value()) self.media_is_playing = False self.media_started_playing = False self.run() def timer_handler(self): """ This is a workaround, because for some reason we can't call set_time() inside the MediaPlayerTimeChanged handler (as the video just stops playing) """ if self.restart_needed: self.media_player.set_time(self.media_start_time) self.restart_needed = False def key_handler(self, event): if event.key() == Qt.Key_Escape and self.is_full_screen: self.toggle_full_screen() if event.key() == Qt.Key_F: self.toggle_full_screen() if event.key() == Qt.Key_Space: self.play_pause() def wheel_handler(self, event): self.modify_volume(1 if event.angleDelta().y() > 0 else -1) def toggle_mute(self): self.media_player.audio_set_mute(not self.media_player.audio_get_mute()) self.mute = not self.mute self.mute_model.setState(not self.mute) def modify_volume(self, delta_percent): new_volume = self.media_player.audio_get_volume() + delta_percent if new_volume < 0: new_volume = 0 elif new_volume > 40: new_volume = 40 self.media_player.audio_set_volume(new_volume) self.ui.slider_volume.setValue(self.media_player.audio_get_volume()) def set_volume(self, new_volume): self.media_player.audio_set_volume(new_volume) def speed_up_handler(self): self.modify_rate(0.1) def slow_down_handler(self): self.modify_rate(-0.1) def modify_rate(self, delta_percent): new_rate = self.media_player.get_rate() + delta_percent if new_rate < 0.2 or new_rate > 2.0: return self.media_player.set_rate(new_rate) def media_time_change_handler(self, _): if self.media_end_time == -1: return if self.media_player.get_time() > self.media_end_time: self.restart_needed = True def update_slider_highlight(self): if self.ui.list_timestamp.selectionModel().hasSelection(): selected_row = self.ui.list_timestamp.selectionModel(). \ selectedRows()[0] self.media_start_time = self.ui.list_timestamp.model().data( selected_row.model().index(selected_row.row(), 0), Qt.UserRole ) self.media_end_time = self.ui.list_timestamp.model().data( selected_row.model().index(selected_row.row(), 1), Qt.UserRole ) duration = self.media_player.get_media().get_duration() self.media_end_time = self.media_end_time \ if self.media_end_time != 0 else duration if self.media_start_time > self.media_end_time: raise ValueError("Start time cannot be later than end time") if self.media_start_time > duration: raise ValueError("Start time not within video duration") if self.media_end_time > duration: raise ValueError("End time not within video duration") slider_start_pos = (self.media_start_time / duration) * \ (self.ui.slider_progress.maximum() - self.ui.slider_progress.minimum()) slider_end_pos = (self.media_end_time / duration) * \ (self.ui.slider_progress.maximum() - self.ui.slider_progress.minimum()) self.ui.slider_progress.setHighlight( int(slider_start_pos), int(slider_end_pos) ) else: self.media_start_time = 0 self.media_end_time = -1 def run(self): """ Execute the loop """ if self.timestamp_filename is None: self._show_error("No timestamp file chosen") return if self.video_filename is None: self._show_error("No video file chosen") return try: self.update_slider_highlight() self.media_player.play() self.media_player.set_time(self.media_start_time) self.media_started_playing = True self.media_is_playing = True self.play_pause_model.setState(False) except Exception as ex: self._show_error(str(ex)) print(traceback.format_exc()) def play_pause(self): """Toggle play/pause status """ if not self.media_started_playing: self.run() return if self.media_is_playing: self.media_player.pause() else: self.media_player.play() self.media_is_playing = not self.media_is_playing self.play_pause_model.setState(not self.media_is_playing) def update_video_file_play_labels(self): curr_total_fps = self.media_player.get_fps() curr_total_duration = self.media_player.get_length() totalNumFrames = int(curr_total_duration * curr_total_fps) if totalNumFrames > 0: self.ui.lblTotalFrames.setText(str(totalNumFrames)) else: self.ui.lblTotalFrames.setText("--") if curr_total_duration > 0: self.ui.lblTotalDuration.setText(str(curr_total_duration)) # Gets duration in [ms] else: self.ui.lblTotalDuration.setText("--") # Changing Values: Dynamically updated each time the playhead changes curr_percent_complete = self.media_player.get_position() # Current percent complete between 0.0 and 1.0 if curr_percent_complete >= 0: self.ui.lblPlaybackPercent.setText(str(curr_percent_complete)) else: self.ui.lblPlaybackPercent.setText("--") curr_frame = int(round(curr_percent_complete * totalNumFrames)) if curr_frame >= 0: self.ui.lblCurrentFrame.setText(str(curr_frame)) else: self.ui.lblCurrentFrame.setText("--") if self.media_player.get_time() >= 0: self.ui.lblCurrentTime.setText(str(self.media_player.get_time()) + "[ms]") # Gets time in [ms] else: self.ui.lblCurrentTime.setText("-- [ms]") # Gets time in [ms] # Called only when the video file changes: def update_video_file_labels_on_file_change(self): if self.video_filename is None: self.ui.lblVideoName.setText("") else: self.ui.lblVideoName.setText(self.video_filename) # Only updated when the video file is changed: curr_total_fps = self.media_player.get_fps() self.ui.lblFileFPS.setText(str(curr_total_fps)) curr_total_duration = self.media_player.get_length() totalNumFrames = int(curr_total_duration * curr_total_fps) if totalNumFrames > 0: self.ui.lblTotalFrames.setText(str(totalNumFrames)) else: self.ui.lblTotalFrames.setText("--") if curr_total_duration > 0: self.ui.lblTotalDuration.setText(str(curr_total_duration)) # Gets duration in [ms] else: self.ui.lblTotalDuration.setText("--") self.update_video_file_play_labels() def get_frame_multipler(self): return self.ui.spinBoxFrameJumpMultiplier.value # def compute_total_number_frames(self): # self.media_player.get_length() def seek_left_handler(self): print('seek: left') self.seek_frames(-10 * self.get_frame_multipler()) def skip_left_handler(self): print('skip: left') self.seek_frames(-1 * self.get_frame_multipler()) def seek_right_handler(self): print('seek: right') self.seek_frames(10 * self.get_frame_multipler()) def skip_right_handler(self): print('skip: right') self.seek_frames(1 * self.get_frame_multipler()) def seek_frames(self, relativeFrameOffset): """Jump a certain number of frames forward or back """ if self.video_filename is None: self._show_error("No video file chosen") return # if self.media_end_time == -1: # return curr_total_fps = self.media_player.get_fps() relativeSecondsOffset = relativeFrameOffset / curr_total_fps # Desired offset in seconds curr_total_duration = self.media_player.get_length() relative_percent_offset = relativeSecondsOffset / curr_total_duration # percent of the whole that we want to skip totalNumFrames = int(curr_total_duration * curr_total_fps) try: didPauseMedia = False if self.media_is_playing: self.media_player.pause() didPauseMedia = True newPosition = self.media_player.get_position() + relative_percent_offset # newTime = int(self.media_player.get_time() + relativeFrameOffset) # self.update_slider_highlight() # self.media_player.set_time(newTime) self.media_player.set_position(newPosition) if (didPauseMedia): self.media_player.play() # else: # # Otherwise, the media was already paused, we need to very quickly play the media to update the frame with the new time, and then immediately pause it again. # self.media_player.play() # self.media_player.pause() self.media_player.next_frame() print("Setting media playback time to ", newPosition) except Exception as ex: self._show_error(str(ex)) print(traceback.format_exc()) def toggle_full_screen(self): if self.is_full_screen: # TODO Artifacts still happen some time when exiting full screen # in X11 self.ui.frame_media.showNormal() self.ui.frame_media.restoreGeometry(self.original_geometry) self.ui.frame_media.setParent(self.ui.widget_central) self.ui.layout_main.addWidget(self.ui.frame_media, 2, 3, 3, 1) # self.ui.frame_media.ensurePolished() else: self.ui.frame_media.setParent(None) self.ui.frame_media.setWindowFlags(Qt.FramelessWindowHint | Qt.CustomizeWindowHint) self.original_geometry = self.ui.frame_media.saveGeometry() desktop = QApplication.desktop() rect = desktop.screenGeometry(desktop.screenNumber(QCursor.pos())) self.ui.frame_media.setGeometry(rect) self.ui.frame_media.showFullScreen() self.ui.frame_media.show() self.ui.frame_video.setFocus() self.is_full_screen = not self.is_full_screen def browse_timestamp_handler(self): """ Handler when the timestamp browser button is clicked """ tmp_name, _ = QFileDialog.getOpenFileName( self, "Choose Timestamp file", None, "Timestamp File (*.tmsp);;All Files (*)" ) if not tmp_name: return self.set_timestamp_filename(QDir.toNativeSeparators(tmp_name)) def create_timestamp_file_handler(self): """ Handler when the timestamp file create button is clicked """ tmp_name, _ = QFileDialog.getSaveFileName( self, "Create New Timestamp file", None, "Timestamp File (*.tmsp);;All Files (*)" ) if not tmp_name: return try: if (os.stat(QDir.toNativeSeparators(tmp_name)).st_size == 0): # File is empty, create a non-empty one: with open(QDir.toNativeSeparators(tmp_name), "w") as fh: fh.write("[]") # Write the minimal valid JSON string to the file to allow it to be used else: pass # with open(tmp_name, 'r') as fh: # if fh.__sizeof__()>0: # # File is not empty: # pass # else: # # File is empty, create a non-empty one: # fh.close() # with open(tmp_name, "w") as fh: # fh.write("[]") # Write the minimal valid JSON string to the file to allow it to be used except WindowsError: with open(tmp_name, "w") as fh: fh.write("[]") # Write the minimal valid JSON string to the file to allow it to be used # Create new file: self.set_timestamp_filename(QDir.toNativeSeparators(tmp_name)) def _sort_model(self): self.ui.list_timestamp.sortByColumn(0, Qt.AscendingOrder) def _select_blank_row(self, parent, start, end): self.ui.list_timestamp.selectRow(start) def set_timestamp_filename(self, filename): """ Set the timestamp file name """ if not os.path.isfile(filename): self._show_error("Cannot access timestamp file " + filename) return try: self.timestamp_model = TimestampModel(filename, self) self.timestamp_model.timeParseError.connect( lambda err: self._show_error(err) ) self.proxy_model.setSortRole(Qt.UserRole) self.proxy_model.dataChanged.connect(self._sort_model) self.proxy_model.dataChanged.connect(self.update_slider_highlight) self.proxy_model.setSourceModel(self.timestamp_model) self.proxy_model.rowsInserted.connect(self._sort_model) self.proxy_model.rowsInserted.connect(self._select_blank_row) self.ui.list_timestamp.setModel(self.proxy_model) self.timestamp_filename = filename self.ui.entry_timestamp.setText(self.timestamp_filename) self.mapper.setModel(self.proxy_model) self.mapper.addMapping(self.ui.entry_start_time, 0) self.mapper.addMapping(self.ui.entry_end_time, 1) self.mapper.addMapping(self.ui.entry_description, 2) self.ui.list_timestamp.selectionModel().selectionChanged.connect( self.timestamp_selection_changed) self._sort_model() directory = os.path.dirname(self.timestamp_filename) basename = os.path.basename(self.timestamp_filename) timestamp_name_without_ext = os.path.splitext(basename)[0] for file_in_dir in os.listdir(directory): current_filename = os.path.splitext(file_in_dir)[0] found_video = (current_filename == timestamp_name_without_ext and file_in_dir != basename) if found_video: found_video_file = os.path.join(directory, file_in_dir) self.set_video_filename(found_video_file) break except ValueError as err: self._show_error("Timestamp file is invalid") def timestamp_selection_changed(self, selected, deselected): if len(selected) > 0: self.mapper.setCurrentModelIndex(selected.indexes()[0]) self.ui.button_save.setEnabled(True) self.ui.button_remove_entry.setEnabled(True) self.ui.entry_start_time.setReadOnly(False) self.ui.entry_end_time.setReadOnly(False) self.ui.entry_description.setReadOnly(False) else: self.mapper.setCurrentModelIndex(QModelIndex()) self.ui.button_save.setEnabled(False) self.ui.button_remove_entry.setEnabled(False) self.ui.entry_start_time.clear() self.ui.entry_end_time.clear() self.ui.entry_description.clear() self.ui.entry_start_time.setReadOnly(True) self.ui.entry_end_time.setReadOnly(True) self.ui.entry_description.setReadOnly(True) def set_video_filename(self, filename): """ Set the video filename """ if not os.path.isfile(filename): self._show_error("Cannot access video file " + filename) return self.video_filename = filename media = self.vlc_instance.media_new(self.video_filename) media.parse() if not media.get_duration(): self._show_error("Cannot play this media file") self.media_player.set_media(None) self.video_filename = None else: self.media_player.set_media(media) if sys.platform.startswith('linux'): # for Linux using the X Server self.media_player.set_xwindow(self.ui.frame_video.winId()) elif sys.platform == "win32": # for Windows self.media_player.set_hwnd(self.ui.frame_video.winId()) elif sys.platform == "darwin": # for MacOS self.media_player.set_nsobject(self.ui.frame_video.winId()) self.ui.entry_video.setText(self.video_filename) self.update_video_file_labels_on_file_change() self.media_started_playing = False self.media_is_playing = False self.set_volume(self.ui.slider_volume.value()) self.play_pause_model.setState(True) def browse_video_handler(self): """ Handler when the video browse button is clicked """ tmp_name, _ = QFileDialog.getOpenFileName( self, "Choose Video file", None, "All Files (*)" ) if not tmp_name: return self.set_video_filename(QDir.toNativeSeparators(tmp_name)) def _show_error(self, message, title="Error"): QMessageBox.warning(self, title, message)
class DeviceEditDialog(QDialog): def __init__(self, model, row, *args, **kwargs): super(DeviceEditDialog, self).__init__(*args, **kwargs) self.setMinimumWidth(400) self.setWindowTitle("Edit device") self.settings = QSettings() self.settings.beginGroup("Devices") self.mapper = QDataWidgetMapper() self.mapper.setModel(model) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) vl = VLayout() gbTopic = QGroupBox("MQTT Topic") self.topic = QLineEdit() self.topic.setPlaceholderText("unique name of your device") self.mapper.addMapping(self.topic, DevMdl.TOPIC) self.full_topic = QLineEdit() self.full_topic.setPlaceholderText("must contain %prefix% and %topic%") self.mapper.addMapping(self.full_topic, DevMdl.FULL_TOPIC) tfl = QFormLayout() tfl.addRow("Topic", self.topic) tfl.addRow("Full topic", self.full_topic) gbTopic.setLayout(tfl) btnSave = QPushButton("Save") btnCancel = QPushButton("Cancel") hl_btns = HLayout() hl_btns.addStretch(1) hl_btns.addWidgets([btnSave, btnCancel]) vl.addWidgets([gbTopic]) vl.addLayout(hl_btns) self.setLayout(vl) self.mapper.setCurrentIndex(row) btnSave.clicked.connect(self.accept) btnCancel.clicked.connect(self.reject) def accept(self): full_topic = self.full_topic.text() if not full_topic.endswith('/'): self.full_topic.setText(full_topic + "/") if not len(self.topic.text()) > 0: QMessageBox.critical(self, "Error", "Topic is required.") elif not "%topic%" in full_topic: QMessageBox.critical(self, "Error", "%topic% is required in FullTopic.") elif not "%prefix%" in full_topic: QMessageBox.critical(self, "Error", "%prefix% is required in FullTopic.") elif self.topic.text() not in self.settings.childGroups(): self.mapper.submit() self.done(QDialog.Accepted) else: QMessageBox.critical(self, "Error", "Device '{}' already on the device list.".format(self.topic.text())) def reject(self): self.mapper.revert() self.done(QDialog.Rejected)
class ManagerMainui(QMainWindow, Ui_MainWindow): def __init__(self): super(ManagerMainui, self).__init__() self.setupUi(self) self.opendb() self.show() self.lianjie() self.setCentralWidget(self.splitter) self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems) self.table_view.setSelectionMode( QAbstractItemView.SingleSelection) # 一次选择单行还是多行 self.table_view.setAlternatingRowColors(True) # 隔行自动改变颜色 self.table_view.verticalHeader().setDefaultSectionSize(22) self.table_view.horizontalHeader().setDefaultSectionSize(60) # 为菜单添加动作 self.filemenu.addAction(self.exit_Act) self.Updata_Menu.addAction(self.Updata_Act) # 当前页 self.currentPage = 0 # 总页数 self.totalPage = 0 # 总记录数 self.totalRecord = 0 # 每页数据数 self.pageRecord = 10 def opendb(self): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('F:/pycharm项目/图书管理系统NEW/AllDataBase/book.db') if db.open(): self._biaocaozuo() else: QMessageBox.information(self, 'warning', '无法建立与数据库的连接') return False def _biaocaozuo(self): self.model = QtSql.QSqlTableModel() self.model.setTable('BookData') self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) # 设置保存策略 self.table_view.setModel(self.model) self.model.select() self.model.setHeaderData(0, Qt.Horizontal, 'ISBN') self.model.setHeaderData(1, Qt.Horizontal, '书名') self.model.setHeaderData(2, Qt.Horizontal, '作者') self.model.setHeaderData(3, Qt.Horizontal, '出版社') self.model.setHeaderData(4, Qt.Horizontal, '出版日期') self.model.setHeaderData(5, Qt.Horizontal, '评分') self.model.setHeaderData(6, Qt.Horizontal, '照片') self.table_view.setColumnHidden(6, True) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.lineEdit_ISBN, 0) self.mapper.addMapping(self.lineEdit_shuming, 1) self.mapper.addMapping(self.lineEdit_zuozhe, 2) self.mapper.addMapping(self.lineEdit_chubanshe, 3) self.mapper.addMapping(self.lineEdit_chubanriqi, 4) self.mapper.addMapping(self.lineEdit_pingfen, 5) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.model) # 选择模型 self.table_view.setSelectionModel(self.selModel) self.selModel.currentChanged.connect( self.do_currentChanged) # 当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # 选择行变化时 # @pyqtSlot() ##保存修改 def on_p_baocun_triggered(self): result = QMessageBox.question(self, 'warning', '你要保存你的修改吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: res = self.model.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.model.lastError().text()) else: QMessageBox.information(self, "message", "保存成功\n") self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: QMessageBox.information(self, 'message', 'Thanks') # @pyqtSlot() ##取消修改 def on_p_quxiao_triggered(self): # self.model.revertAll() result = QMessageBox.question(self, 'warning', '确认取消之前所有的的操作吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.revertAll() self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) # @pyqtSlot() ##添加记录 def on_p_zengjia_triggered(self): #self.model.insertRows(self.model.rowCount(), 1) self.model.insertRow(self.model.rowCount(), QModelIndex()) # 在末尾添加一个记录 curIndex = self.model.index(self.model.rowCount() - 1, 1) # 创建最后一行的ModelIndex self.selModel.clearSelection() # 清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) # 设置刚插入的行为当前选择行 # 删除记录 def on_p_sanchu_triggered(self): # if self.p_baocun.isEnabled(): result = QMessageBox.question(self, 'warning', '确认删除该记录吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.removeRow(self.table_view.currentIndex().row()) self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) else: self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) # 插入记录 def on_p_charu_triggered(self): curIndex = self.table_view.currentIndex() # QModelIndex self.model.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # 清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) # 对当前行设置图片 def on_p_photo_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) # fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() # QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.model.record(curRecNo) # 获取当前记录QSqlRecord curRec.setValue("Photo", data) # 设置字段数据 self.model.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) # 在界面上显示 def closeEvent(self, event): """ 提示保存 """ if self.p_baocun.isEnabled(): r = QMessageBox.warning(self, "注意", "你还没有保存,现在保存下?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if r == QMessageBox.No: event.accept() else: event.ignore() def chaxun_ISBN(self): text1 = self.lineEdit_chaxun.text() self.model.setFilter(("ISBN='%s'" % (text1))) def chaxun_zuozhe(self): text2 = self.lineEdit_chaxun.text() self.model.setFilter(("author='%s'" % (text2))) def chaxun_shumin(self): text3 = self.lineEdit_chaxun.text() self.model.setFilter(("title='%s'" % (text3))) def chaxun_(self): """ 查找图书 """ searchtext = self.lineEdit_chaxun.text() if searchtext: if self.comboBox_chaxun.currentText() == "ISBN": self.chaxun_ISBN() elif self.comboBox_chaxun.currentText() == "书名": self.chaxun_shumin() elif self.comboBox_chaxun.currentText() == "作者": self.chaxun_zuozhe() else: QMessageBox.information(self, "提示", "请输入搜索关键词!") def on_p_quxiaoCX_triggered(self): self.lineEdit_chaxun.clear() self.model.setFilter("") def do_currentChanged(self, current, previous): # 更新actPost和actCancel 的状态 self.p_baocun.setEnabled(self.model.isDirty()) # 有未保存修改时可用 self.p_quxiao.setEnabled(self.model.isDirty()) def do_currentRowChanged(self, current, previous): # 行切换时的状态控制 self.mapper.setCurrentIndex(current.row()) if (current.isValid() == False): self.dbLabPhoto.clear() # 清除图片显示 return self.mapper.setCurrentIndex(current.row()) # 更新数据映射的行号 curRec = self.model.record(current.row()) # 获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): # 图片字段内容为空 self.dbLabPhoto.clear() else: # data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.size().width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) def scrapy(self): """启动爬虫,在线更新功能""" # cmdline.execute(" scrapy ".split()) self.hide() try: run = ScrapyRun() finally: pass def lianjie(self): self.p_zengjia.clicked.connect(self.on_p_zengjia_triggered) self.p_baocun.clicked.connect(self.on_p_baocun_triggered) self.p_quxiao.clicked.connect(self.on_p_quxiao_triggered) self.p_sanchu.clicked.connect(self.on_p_sanchu_triggered) self.p_charu.clicked.connect(self.on_p_charu_triggered) self.p_photo.clicked.connect(self.on_p_photo_triggered) self.p_chaxun.clicked.connect(self.chaxun_) self.p_quxiaoCX.clicked.connect(self.on_p_quxiaoCX_triggered) self.p_tuichu.clicked.connect(self.close) self.exit_Act.triggered.connect(self.close) self.Updata_Act.triggered.connect(self.scrapy)
class MainWindow(QMainWindow): # 继承主窗口函数的类, 继承编写的主函数 def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # 初始化运行A窗口类下的 setupUi 函数 self.open_alltable() self.enterList = {} self.outList = {} self.name_RFID = { '直齿轮': '01B0E00000020010B1', '斜齿轮': '01B0E00000030010B2', '齿轮轴': '01A0E00000010010A1', '光轴': '01A0E00000010010A2', '曲柄': '01C0E00000020010C1', '摇杆': '01C0E00000010010C2' } self.RFID_kind = dict([(value, key) for (key, value) in self.name_RFID.items()]) self.capacity = { '直齿轮': 18, '斜齿轮': 18, '齿轮轴': 24, '光轴': 24, '曲柄': 36, '摇杆': 36 } self.nameNum = { '直齿轮': 3, '斜齿轮': 3, '齿轮轴': 4, '光轴': 4, '曲柄': 6, '摇杆': 6 } self.locateName = { '直齿轮': ['A', 'B', 1], '斜齿轮': ['C', 'D', 1], '齿轮轴': ['A', 'B', 2], '光轴': ['C', 'D', 2], '曲柄': ['A', 'B', 3], '摇杆': ['C', 'D', 3] } self.get_locState() # 获取所有货位的空闲状态 # 初始化出入库按钮使能状态 self.ui.outScan_pushButton.setEnabled(False) self.ui.enterScan_pushButton.setEnabled(False) self.currentList = self.Absolute_statis('current') # 获取入库记录里各种工件的数量 print(self.currentList) def open_alltable(self): ## tableView显示属性设置 self.ui.current_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.current_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.current_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.current_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.current_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.enter_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.enter_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.enter_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.enter_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.enter_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.out_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.out_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.out_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.out_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.out_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.VICEtableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.VICEtableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.VICEtableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.VICEtableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.VICEtableView.horizontalHeader().setDefaultSectionSize( 240) # 单元格宽度 # 连接到数据库并打开 self.DB = QSqlDatabase.addDatabase( 'QSQLITE') # 建立与数据库的连接,即QSqlDatabase对象 self.DB.setDatabaseName('MES5.db') # 设置数据库名称 self.DB.open() # 打开数据库 print('数据库打开成功') ## 打开现有库存数据表 self.current_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.current_tabModel.setTable('工件信息表') # 设置需要连接的数据表 self.current_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开入库记录表 self.enter_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.enter_tabModel.setTable('入库记录表') # 设置需要连接的数据表 self.enter_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开出库记录表 self.out_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.out_tabModel.setTable('出库记录表') # 设置需要连接的数据表 self.out_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开固定批RFID表======================================================== self.vice_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.vice_tabModel.setTable('新建表') # 设置需要连接的数据表 self.vice_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.ui.VICEtableView.setModel( self.vice_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.vice_tabModel.select() # 建立RFID与组件的映射关系 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.vice_tabModel) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与tabelmodel的具体字段之间的联系 self.mapper.addMapping(self.ui.RFIDlineEdit, 0) self.mapper.toFirst() # 移动到首记录 # 选中数据时信号的发射 self.selModel = QItemSelectionModel(self.vice_tabModel) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.VICEtableView.setSelectionModel(self.selModel) # 设置选择模型 # =============================================================================== self.refresh() print('数据表打开成功') ##获取字段名和序号的字典数据 empty = self.current_tabModel.record() # 得到的是一个空的记录,获取表的字段定义 self.fldNum = {} for i in range(empty.count()): filedname = empty.fieldName(i) empty.field(filedname).setReadOnly(True) # 每个字段设置为只读 self.fldNum.setdefault( filedname, i) # 如果字典中包含有给定键,则返回该键对应的值,否则将键添加到字典中,默认值为None print(self.fldNum) print(self.current_tabModel.record().count()) def do_currentRowChanged(self, current): self.mapper.setCurrentIndex(current.row()) def refresh(self): self.ui.current_tableView.setModel(self.current_tabModel) self.current_tabModel.select() self.ui.enter_tableView.setModel( self.enter_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.enter_tabModel.select() self.ui.out_tableView.setModel( self.out_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.out_tabModel.select() self.inventory_used() #################################路径规划与货位分配########################################### def get_locState(self): # 获取所有货位的状态 self.locationState = {} DecodeLoc = {'A': 0, 'B': 1, 'C': 0, 'D': 1} # 解码字典 for key, value in self.nameNum.items(): self.locationState[key] = np.zeros((2, 3, value), dtype=int) if self.current_tabModel.record(0).isNull('货位') == True: pass else: currentRow = self.current_tabModel.rowCount() # 库存的已记录数 # 根据现有库存更新货位的状态 for i in range(currentRow): curRec = self.current_tabModel.record(i) location = curRec.value('货位') kindname = curRec.value('种类') x = DecodeLoc[location[0]] # 对已有的工件的货位编码进行解码 y = int(location[1]) - 1 z = int(location[5]) - 1 self.locationState[kindname][x][y][z] = 1 #货位分配函数 def allocation(self, name, num, label): # 输入工件名称和对应的数量 if label == 'i': sign = 0 else: sign = 1 itsloc = self.locationState[name] for i in range(3): data = itsloc[:, 0:i + 1] data = data.reshape(1, -1)[0] itsum = sum(data == sign) if itsum >= num: forward_double = i + 1 break idle_loc = np.where( itsloc[:, 0:forward_double] == sign) # 寻找前forward_double对货架的空闲货位 locating = [] # 存放接收工件的货位 for i in range(num): x = idle_loc[0][i] y = idle_loc[1][i] z = idle_loc[2][i] self.locationState[name][x][y][z] = 1 - sign # 分配好后即更新货位状态 locating.append([x, y, z]) EncodeLoc = [] for i in locating: s = self.locateName[name][i[0]] + str(i[1] + 1) + '-' + \ str(self.locateName[name][2]) + '-' + str(i[2] + 1) EncodeLoc.append(s) return EncodeLoc, forward_double def getLocation(self, outList, direct): ALLlocation = [] forwardLocation = {} for key, value in outList.items(): location, position = self.allocation(key, value, direct) ALLlocation = ALLlocation + location forwardLocation[key] = position - 1 return ALLlocation, forwardLocation # 表里的工件统计 def statistics(self, model): n = model.rowCount() # 总行数 all_list = {} for i in range(n): # 统计入库记录里各种工件的数量 rec = model.record(i) if rec.value('种类') in all_list: all_list[rec.value('种类')] += 1 else: all_list[rec.value('种类')] = 1 return all_list def Absolute_statis(self, label): myModel = QSqlQueryModel(self) if label == 'current': myModel.setQuery("select 种类 from 工件信息表") elif label == 'enter': myModel.setQuery("select 种类 from 入库记录表") elif label == 'out': myModel.setQuery("select 种类 from 出库记录表") allnum = myModel.rowCount() all_dict = {} for i in range(allnum): rec = myModel.record(i) if rec.value('种类') in all_dict: all_dict[rec.value('种类')] += 1 else: all_dict[rec.value('种类')] = 1 return all_dict #寻找出入库记录的最大批次 def getMaxbatch(self, label): myModel = QSqlQueryModel(self) if label == 'enter': myModel.setQuery("select 批次 from 入库记录表") elif label == 'out': myModel.setQuery("select 批次 from 出库记录表") n = myModel.rowCount() if myModel.record(0).isNull('批次') == True: # 检测入库记录里的最大批次 max_batch = 0 else: max_batch = myModel.record(n - 1).value('批次') # 批次按顺序排列,查找最大批次 print('共有%d条记录,最大批次为%d' % (n, max_batch)) return max_batch #################################主操作界面#################################################### # 容量不足信息打印 def on_enterNum_spinBox_valueChanged(self): self.enter_Warning() def on_enterKind_comboBox_currentIndexChanged(self): self.enter_Warning() def enter_Warning(self): num = self.ui.enterNum_spinBox.value() enterKind = self.ui.enterKind_comboBox.currentText() if enterKind not in self.currentList: currentNum = 0 else: currentNum = self.currentList[enterKind] if num + currentNum > self.capacity[enterKind]: self.ui.enterList_pushButton.setEnabled(False) # self.ui.enterScan_pushButton.setEnabled(False) warningText = '⚠警告!' + enterKind + '已经超出库容上限!' else: self.ui.enterList_pushButton.setEnabled(True) # self.ui.enterScan_pushButton.setEnabled(True) warningText = '' self.ui.textEdit.setPlainText(warningText) #############入库函数 ##入库添加工件及对应数量 @pyqtSlot() def on_enterList_pushButton_clicked(self): enterKind = self.ui.enterKind_comboBox.currentText() enterNum = self.ui.enterNum_spinBox.value() if enterKind in self.enterList: self.enterList[enterKind] += enterNum else: self.enterList[enterKind] = enterNum # 激活出库按钮使能状态 self.ui.enterScan_pushButton.setEnabled(True) print(self.enterList) ##扫描入库 @pyqtSlot() def on_enterScan_pushButton_clicked(self): # 根据入库记录里的RFID生成新的RFID enteredList = self.statistics(self.enter_tabModel) # 获取入库记录里各种工件的数量 RFID_list = [] for key in self.enterList.keys(): # 对要入库的工件种类进行迭代 for i in range(self.enterList[key]): if key not in enteredList: no = i + 6 else: no = enteredList[key] + i + 6 RFID = self.name_RFID[key] + str(no).zfill(6) RFID_list.append(RFID) # 将RFID写入txt文件来模拟货物 filename = '卸货区' if not os.path.isdir(filename): os.makedirs(filename) with open('卸货区\待入库货工件.txt', 'w', encoding='utf8') as f: for s in RFID_list: f.write(s + '\n') # 从卸货区寻找模拟货物 dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找入库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: enterRFID = f.readlines() all_location, forwardLocation = self.getLocation(self.enterList, 'i') self.ui.widget.get_path(forwardLocation, 'i') # 绘制路径 self.enter(enterRFID, all_location) # 入库操作 def enter(self, enterRFID, all_location): #print('入库函数,enterRFID=',enterRFID,'all_location=',all_location) # 准备写入工件信息表和入库记录表 currentQuery = QSqlQuery(self.DB) enterQuery = QSqlQuery(self.DB) currentQuery.prepare('''INSERT INTO 工件信息表 (RFID,种类,货位,生产商, 批次,入库日期,入库时间,重量) VALUES(:RFID,:kind,:location,:manufacture,:batch, :enterDate,:enterTime,:weight)''') enterQuery.prepare('''INSERT INTO 入库记录表 (RFID,种类,生产商, 批次,入库日期,入库时间) VALUES(:RFID,:kind, :manufacture,:batch, :enterDate,:enterTime)''') # 准备数据(都是恒量) RFID_manufacture = { 'E0000001': '西安交大一厂', 'E0000002': '西安交大二厂', 'E0000003': '西安交大三厂' } max_batch = self.getMaxbatch('enter') enterDate = self.ui.dealdate.date().toString(Qt.ISODate) # 获取日期 enterTime = time.strftime('%H:%M:%S', time.localtime(time.time())) # 获取时分秒 weightList = { '直齿轮': 15, '斜齿轮': 20, '齿轮轴': 7, '光轴': 5, '曲柄': 2, '摇杆': 1 } check = True # 扫码并写入记录表 for i in range(len(enterRFID)): # 解码 s = enterRFID[i].rstrip('\n') kind = self.RFID_kind[s[0:18]] manufacture = RFID_manufacture[s[4:12]] weight = weightList[kind] # 加入现有库存 currentQuery.bindValue(":RFID", s) currentQuery.bindValue(":kind", kind) currentQuery.bindValue(":location", all_location[i]) currentQuery.bindValue(":manufacture", manufacture) currentQuery.bindValue(":batch", max_batch + 1) currentQuery.bindValue(":enterDate", enterDate) currentQuery.bindValue(":enterTime", enterTime) currentQuery.bindValue(":weight", weight) res = currentQuery.exec() # 执行SQL语句 check = check & res # 加入入库记录表 enterQuery.bindValue(":RFID", s) enterQuery.bindValue(":kind", kind) enterQuery.bindValue(":manufacture", manufacture) enterQuery.bindValue(":batch", max_batch + 1) enterQuery.bindValue(":enterDate", enterDate) enterQuery.bindValue(":enterTime", enterTime) enterQuery.exec() # 执行SQL语句 if check == False: QMessageBox.critical( self, "错误", "添加记录出现错误\n" + currentQuery.lastError().text()) print('入库异常') else: self.enterList = {} self.refresh() self.ui.enterScan_pushButton.setEnabled(False) self.ui.textEdit.setPlainText('入库操作成功') #############出库函数 # 库存不足信息打印 def on_outNum_spinBox_valueChanged(self): self.out_Warning() def on_outKind_comboBox_currentIndexChanged(self): self.out_Warning() def out_Warning(self): num = self.ui.outNum_spinBox.value() outKind = self.ui.outKind_comboBox.currentText() self.currentList = self.Absolute_statis('current') if outKind not in self.currentList: # 防止有些工件没有时作为字典的键出错 currentNum = 0 else: currentNum = self.currentList[outKind] if num > currentNum: self.ui.outList_pushButton.setEnabled(False) # self.ui.outScan_pushButton.setEnabled(False) warningText = '⚠警告!' + outKind + '已经超出库存上限!' else: self.ui.outList_pushButton.setEnabled(True) # self.ui.outScan_pushButton.setEnabled(True) warningText = '' self.ui.textEdit.setPlainText(warningText) # 添加出库工件 @pyqtSlot() def on_outList_pushButton_clicked(self): outKind = self.ui.outKind_comboBox.currentText() outNum = self.ui.outNum_spinBox.value() if outKind in self.outList: self.outList[outKind] += outNum else: self.outList[outKind] = outNum self.ui.outScan_pushButton.setEnabled(True) print("要出库的货物:", self.outList) @pyqtSlot() def on_outScan_pushButton_clicked(self): currentRow = self.current_tabModel.rowCount() # 库存的已记录数 RFID_list = [] outLocation, forwardLocation = self.getLocation(self.outList, 'o') # 从现有库存里取符合条件的RFID for i in range(currentRow): curRec = self.current_tabModel.record(i) location = curRec.value('货位') if location in outLocation: RFID_list.append(curRec.value('RFID')) # 将RFID写入模拟货物 filename = '发货区' if not os.path.isdir(filename): os.makedirs(filename) with open('发货区\待出库货工件.txt', 'w', encoding='utf8') as f: for s in RFID_list: f.write(s + '\n') # 从卸货区寻找模拟货物 dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找出库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: outRFID = f.readlines() for i in range(len(outRFID)): # 去除所有的换行符 outRFID[i] = outRFID[i].rstrip('\n') self.ui.widget.get_path(forwardLocation, 'o') #出库路径绘制 self.out(outRFID) def out(self, outRFID): # 出库函数,参数只有RFID列表 currentRow = self.current_tabModel.rowCount() # 库存的已记录数 currentQuery = QSqlQuery(self.DB) # 准备删除 outQuery = QSqlQuery(self.DB) # 准备插入 outQuery.prepare('''INSERT INTO 出库记录表 (RFID,种类,生产商, 批次,出库日期,出库时间) VALUES(:RFID,:kind, :manufacture,:batch, :outDate,:outTime)''') currentQuery.prepare('''DELETE FROM 工件信息表 WHERE RFID=:ID''') # 获取出库记录表里的最大批次 batch = self.getMaxbatch('out') outDate = self.ui.dealdate.date().toString(Qt.ISODate) outTime = time.strftime('%H:%M:%S', time.localtime(time.time())) check = True # 读取要出库的工件的RFID for i in range(currentRow): # 遍历现有库存找到要出库的RFID curRec = self.current_tabModel.record(i) if curRec.value('RFID') in outRFID: outQuery.bindValue(":RFID", curRec.value('RFID')) outQuery.bindValue(":kind", curRec.value('种类')) outQuery.bindValue(":manufacture", curRec.value('生产商')) outQuery.bindValue(":batch", batch + 1) outQuery.bindValue(":outDate", outDate) outQuery.bindValue(":outTime", outTime) outQuery.exec() # 执行SQL语句 currentQuery.bindValue(":ID", curRec.value('RFID')) res = currentQuery.exec() check = check & res if check == False: QMessageBox.critical( self, "错误", "出库记录出现错误\n" + currentQuery.lastError().text()) else: self.refresh() self.outList = {} self.ui.outScan_pushButton.setEnabled(False) self.ui.textEdit.setPlainText('出库操作成功') self.out_Warning() # 库存不足则打印信息,改变按键使能状态 # =================================副操作界面================================================== def getCurrentNum(self): currentQryModel = QSqlQueryModel(self) currentQryModel.setQuery("select RFID from 工件信息表") allnum = currentQryModel.rowCount() return allnum, currentQryModel ##单件扫描 @pyqtSlot() def on_emitPushButton_pressed(self): RFID = self.ui.RFIDlineEdit.text() forwardLocation = {} kind = self.RFID_kind[RFID[0:18]] kindlist = [] kindlist.append(RFID) allnum, currentQryModel = self.getCurrentNum() for i in range(allnum): cur = currentQryModel.record(i) if RFID == cur.value('RFID'): self.out(kindlist) self.get_locState() return # 因为已经指定了RFID,所以出库没有路径规划 location, position = self.allocation(kind, 1, 'i') self.enter(kindlist, location) forwardLocation[kind] = position - 1 self.ui.widget.get_path(forwardLocation, 'i') ##多件扫描 @pyqtSlot() def on_ScanPushButton_pressed(self): outRFID = [] inkindNum = {} dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找入库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: RFID = f.readlines() for i in range(len(RFID)): # 去除所有的换行符 RFID[i] = RFID[i].rstrip('\n') allnum, currentQryModel = self.getCurrentNum() for i in range(allnum): cur = currentQryModel.record(i) if cur.value('RFID') in RFID: outRFID.append(cur.value('RFID')) # 如果现有库存里存在,则添加到出库RFID列表中 RFID.remove(cur.value('RFID')) # 移除 if outRFID != []: self.out(outRFID) self.get_locState() if RFID != []: for i in RFID: kind = self.RFID_kind[i[0:18]] if kind in inkindNum: inkindNum[kind] += 1 else: inkindNum[kind] = 1 inLocation, forwardLocation = self.getLocation(inkindNum, 'i') self.ui.widget.get_path(forwardLocation, 'i') #绘制路径 self.enter(RFID, inLocation) #入库操作 #################################现有库存界面#################################################### ##种类过滤 def on_currentComboBox_currentIndexChanged(self, curText): self.current_filter() ##批次过滤 def on_batchSpinBox_valueChanged(self, num): self.current_filter() ##是否选择批次 def on_batchCheckBox_stateChanged(self): self.current_filter() def current_filter(self): kind = self.ui.currentComboBox.currentText() batch = self.ui.batchSpinBox.value() if kind == '全选': if self.ui.batchCheckBox.isChecked() == True: self.current_tabModel.setFilter("批次='%d'" % (batch)) else: self.current_tabModel.setFilter("") else: if self.ui.batchCheckBox.isChecked() == True: self.current_tabModel.setFilter("批次='%d' and 种类='%s'" % (batch, kind)) else: self.current_tabModel.setFilter("种类='%s'" % (kind)) # 已用库存百分比显示 def inventory_used(self): # 通过进度条表示库存余量 currentQryModel = QSqlQueryModel(self) currentQryModel.setQuery("select RFID from 工件信息表") allnum = currentQryModel.rowCount() progress = int((100 * allnum / 156) + 0.5) self.ui.progressBar.setValue(progress) @pyqtSlot() def on_delete_pushButton_pressed(self): query = QSqlQuery(self.DB) print('开始删除') query.exec("DELETE FROM 工件信息表 WHERE 批次>=0") query.exec("DELETE FROM 出库记录表 WHERE 批次>=0") check = query.exec("DELETE FROM 入库记录表 WHERE 批次>=0") if check == False: QMessageBox.critical(self, "错误", "删除记录出现错误\n" + query.lastError().text()) else: self.refresh() #################################入库记录界面#################################################### ##种类过滤 def on_enterComboBox_currentIndexChanged(self): self.enter_filter() ##日期过滤 def on_enterDate_dateChanged(self): self.enter_filter() ##是否选择批次 def on_enterCheckBox_stateChanged(self): self.enter_filter() def enter_filter(self): kind = self.ui.enterComboBox.currentText() date = self.ui.enterDate.date().toString(Qt.ISODate) if self.ui.enterCheckBox.isChecked() == True: if kind == '全选': self.enter_tabModel.setFilter("入库日期='%s'" % (date)) else: self.enter_tabModel.setFilter("入库日期='%s' and 种类='%s'" % (date, kind)) else: if kind == '全选': self.enter_tabModel.setFilter("") else: self.enter_tabModel.setFilter("种类='%s'" % (kind)) # 入库记录可视化 @pyqtSlot() def on_drawEnter_pushButton_pressed(self): self.recordDemo(self.enter_tabModel) #################################出库记录界面#################################################### ##种类过滤 def on_outComboBox_currentIndexChanged(self): self.out_filter() ##日期过滤 def on_outDate_dateChanged(self): self.out_filter() ##是否选择批次 def on_outCheckBox_stateChanged(self): self.out_filter() def out_filter(self): kind = self.ui.outComboBox.currentText() date = self.ui.outDate.date().toString(Qt.ISODate) if self.ui.outCheckBox.isChecked() == True: if kind == '全选': self.out_tabModel.setFilter("出库日期='%s'" % (date)) else: self.out_tabModel.setFilter("出库日期='%s' and 种类='%s'" % (date, kind)) else: if kind == '全选': self.out_tabModel.setFilter("") else: self.out_tabModel.setFilter("种类='%s'" % (kind)) # 出库记录可视化 @pyqtSlot() def on_drawOut_pushButton_pressed(self): self.recordDemo(self.out_tabModel) #################################数据可视化################################################ # 现有库存可视化 @pyqtSlot() def on_drawCurrent_pushButton_pressed(self): # def demo(self): #self.currentList = self.statistics(self.current_tabModel) self.currentList = self.Absolute_statis('current') labels = [] values = [] for key, value in self.currentList.items(): labels.append(key) values.append(value) if self.ui.pie_radioButton.isChecked() == True: trace = [go.Pie(labels=labels, values=values)] layout = go.Layout(title='工件库存比例配比图', ) elif self.ui.bar_radioButton.isChecked() == True: trace = [go.Bar(x=labels, y=values)] layout = go.Layout(title='工件库存直方图', ) print(labels) print(values) fig = go.Figure(data=trace, layout=layout) file = 'F:\source\python代码\MES库存管理\VisualData' if not os.path.isdir(file): os.makedirs(file) filename = 'F:\source\python代码\MES库存管理\VisualData\\currentDemo.html' pyof.plot(fig, filename=filename, auto_open=False) win = DemoWindow(filename) win.exec() def recordDemo(self, model): record = {} enterRow = model.rowCount() # 库存的已记录数 dateName = model.record().fieldName(4) # 入库日期&出库日期 TimeName = model.record().fieldName(5) for i in range(enterRow): curRec = model.record(i) kind = curRec.value('种类') date = curRec.value(dateName) Time = curRec.value(TimeName) dateTime = date + ' ' + Time dateRecord = {} if kind not in record: # 工件种类第一次检索到,将其添加到reord中 dateRecord[dateTime] = 1 # 同时将对应的日期添加到record中 record[kind] = dateRecord else: if dateTime not in record[kind]: # 种类存在,但是第一次检索到某个日期时 record[kind][dateTime] = 1 else: record[kind][dateTime] += 1 traces = [] for key1 in record.keys(): x = [] y = [] for key2 in record[key1].keys(): x.append(key2) y.append(record[key1][key2]) trace = go.Scatter( x=x, y=y, # mode = 'markers', name=key1) traces.append(trace) layout = go.Layout(title=dateName[0:2] + '记录', ) fig = go.Figure(data=traces, layout=layout) file = 'F:\source\python代码\MES库存管理\VisualData' if not os.path.isdir(file): os.makedirs(file) filename = 'F:\source\python代码\MES库存管理\VisualData\\recordDemo.html' pyof.plot(fig, filename=filename, auto_open=False) win = DemoWindow(filename) win.exec()
class QmyMainWindow(QMainWindow): def __init__(self, parent=None, dbFilename='None'): super().__init__(parent) # Call the parent class. Create window. self.ui = Ui_MainWindow() # Create UI object self.ui.setupUi(self) # Create UI interface self.ui.tabWidget.setVisible(True) self.setCentralWidget(self.ui.tabWidget) # tableView setting self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(44) self.ui.tableView.horizontalHeader().setDefaultSectionSize(70) self.ui.tableView.setSortingEnabled(True) # Initialize chart self.__iniPieChart() # Initialize pie chart self.__iniStackedBar() # Stacked bar mpl.rcParams['font.sans-serif'] = ['Calibri'] mpl.rcParams['font.size'] = 8 # Choose the SQLITE database drive self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) # Set the name of database if self.DB.open(): # Open database self.__openTable() # Open tables else: QMessageBox.warning(self, "Warning", "Failed to open the database.") # ==============Self-defined Fuctions============ def __getFieldNames(self): # Get names of all fields. # Get empty records, only field name. emptyRec = self.tabModel.record() self.fldNum = {} # Dictionary of field name and index. for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i def __openTable(self): # Open the table of database self.tabModel = QSqlTableModel(self, self.DB) # Data model # Set the table of database TODO: Can user insert their own tables from # the database? self.tabModel.setTable("battery") # Data storage,OnManualSubmit , OnRowChange self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) # self.tabModel.setSort( # self.tabModel.fieldIndex("RANDOM"), # Qt.DescendingOrder) # Sorting if (self.tabModel.select() == False): # Failed to query the data QMessageBox.critical( self, "Wrong", "Something wrong. Failed to open the database\n" + self.tabModel.lastError().text()) return # self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.__getFieldNames() # Get the field name and index # Field name shown for i in self.fldNum: self.tabModel.setHeaderData(self.fldNum[i], Qt.Horizontal, i.capitalize()) # Create mappings between interface widget and the field name of data model self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) # Setting data model self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # The relations between interface widget and field name of tabModel self.mapper.addMapping(self.ui.dbEditValue, self.fldNum["Value"]) self.mapper.addMapping(self.ui.dbEditRunit, self.fldNum["Raw_unit"]) self.mapper.addMapping(self.ui.dbComboProperty, self.fldNum["Property"]) self.mapper.addMapping(self.ui.dbEditUnit, self.fldNum["Unit"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbEditEname, self.fldNum["Extracted_name"]) self.mapper.addMapping(self.ui.dbEditRvalue, self.fldNum["Raw_value"]) self.mapper.addMapping(self.ui.dbEditDOI_2, self.fldNum["DOI"]) self.mapper.addMapping(self.ui.dbEditDOI_4, self.fldNum["Date"]) self.mapper.addMapping(self.ui.dbEditDOI_3, self.fldNum["Title"]) self.mapper.addMapping(self.ui.dbEditDOI, self.fldNum["Journal"]) self.mapper.addMapping(self.ui.dbEditTag, self.fldNum["Tag"]) self.mapper.addMapping(self.ui.dbEditInfo, self.fldNum["Info"]) self.mapper.addMapping(self.ui.dbEditType, self.fldNum["Type"]) self.mapper.addMapping(self.ui.dbEditWarning, self.fldNum["Warning"]) self.mapper.addMapping(self.ui.dbEditSpecifier, self.fldNum["Specifier"]) self.mapper.toFirst() # Move to the first record self.selModel = QItemSelectionModel(self.tabModel) # Select model self.selModel.currentChanged.connect( self.do_currentChanged) # Trigger when the current changed self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # Trigger when the current row changed self.ui.tableView.setModel(self.tabModel) # Setting the data model self.ui.tableView.setSelectionModel( self.selModel) # Setting the selection model # self.ui.tableView.setColumnHidden(self.fldNum["TAG"], True) #Hide columns # self.ui.tableView.setColumnHidden(self.fldNum["INFO"], True) #Hide # columns self.ui.tableView.setColumnHidden(self.fldNum["Extracted_name"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Unit"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Num_records"], True) # Hide columns # self.ui.tableView.setColumnHidden( # self.fldNum["RAW_VALUE"], True) # Hide columns # Update the conditions of actions of interface widget self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.btnDrawPieChart.setEnabled(True) # Pie Chart self.ui.spinPieSize.setEnabled(True) self.ui.spinHoleSize.setEnabled(True) self.ui.chkBox_PieLegend.setEnabled(True) self.ui.generateButton_3.setEnabled(True) self.ui.searchButton_3.setEnabled(True) self.ui.clearButton_3.setEnabled(True) self.ui.frame_4.setEnabled(True) self.ui.frame_3.setEnabled(True) self.ui.searchInput.returnPressed.connect(self.ui.searchButton.click) self.ui.searchInput_3.returnPressed.connect( self.ui.searchButton_3.click) def __iniPieChart(self): # Initialize pie chart chart = QChart() # chart.setTitle("Piechart") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewPie.setChart(chart) # Setting chart for chart view self.ui.chartViewPie.setRenderHint(QPainter.Antialiasing) self.ui.chartViewPie.setCursor(Qt.CrossCursor) # Setting cross cursor def __iniStackedBar(self): # Initialize stacked bar chart chart = QChart() # chart.setTitle("Number of property records for each chemical") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewStackedBar.setChart(chart) # Set chart self.ui.chartViewStackedBar.setRenderHint(QPainter.Antialiasing) self.ui.chartViewStackedBar.setCursor(Qt.CrossCursor) # Set mouse # ==========Table tab slot function================== @pyqtSlot() # Save changes def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "Information", "Failed to store the changes. Wrong information. \n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Cancel changes def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Add records def on_actRecAppend_triggered(self): self.tabModel.insertRow( self.tabModel.rowCount(), QModelIndex()) # Add one record in the last row curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) # Create ModelIndex of the last row self.selModel.clearSelection() # Clear selections # Choosing the current row when selection. self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() # Get current row @pyqtSlot() # Insert records def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() # QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # Clear selections self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() # Delete records def on_actRecDelete_triggered(self): # Get the current index of current model curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row(), QModelIndex()) # Delete the current row self.tabModel.submit() @pyqtSlot() # Help message box def on_actHelp_triggered(self): msg = QMessageBox() msg.about( self, "Help", '<div>' '<h3>Table: Query the database according to data types and name or DOI.</h3>' '<ul>' '<li>Search the exact compound name in "<em>Exact Match</em>". Search the element or part of compound name in "<em>Generic Match</em>". </li>' '<li>Click the "<em>Home</em>" or "<em>All</em>" button to view the full database</li>' '<li>Refer to "<em>Correctness</em>" column for database evaluation details. </li>' '<li>You can add your own database entries using the "<em>Insert</em>" tab; click "<em>Save</em>" to save the changes.</li>' '</ul>' '<h3>Figure: Statistical analysis of the database.</h3>' '<ul>' '<li><em>Pie chart</em> shows the proportion of data records.</li>' '<li><em>Stacked bar chart</em> shows the data types for each compound. First input the compound name, click "<em>Add</em>" and then "<em>Generate</em>" data for overview. </li>' '<li><em>Histogram </em>shows the distribution of each data type.</li>' '<li><em>Venn diagram</em> shows the correlation of each data type.</li>' '</ul>' '</div>') @pyqtSlot() # Filtering def on_radioBtnVoltage_clicked(self): flag, sqlmerge = self.merged_or_not() print(sqlmerge) self.tabModel.setFilter("PROPERTY LIKE 'Voltage' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnCoulombic_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Coulombic Efficiency' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnConductivity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Conductivity' AND %s" % sqlmerge) @pyqtSlot() # Filetering def on_radioBtnCapacity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Capacity' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnEnergy_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Energy' AND %s" % sqlmerge) @pyqtSlot() # Cancel Filetering def on_radioBtnAll_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("%s" % sqlmerge) # print(self.tabModel.filter()) # self.tabModel.select() def get_elements(self): element_dic = { "Hydrogen": "H", "Helium": "He", "Lithium": "Li", "Beryllium": "Be", "Boron": "B", "Carbon": "C", "Nitrogen": "N", "Oxygen": "O", "Fluorine": "F", "Neon": "Ne", "Sodium": "Na", "Magnesium": "Mg", "Aluminum": "Al", "Silicon": "Si", "Phosphorus": "P", "Sulfur": "S", "Chlorine": "Cl", "Argon": "Ar", "Potassium": "K", "Calcium": "Ca", "Scandium": "Sc", "Titanium": "Ti", "Vanadium": "V", "Chromium": "Cr", "Manganese": "Mn", "Iron": "Fe", "Cobalt": "Co", "Nickel": "Ni", "Copper": "Cu", "Zinc": "Zn", "Gallium": "Ga", "Germanium": "Ge", "Arsenic": "As", "Selenium": "Se", "Bromine": "Br", "Krypton": "Kr", "Rubidium": "Rb", "Strontium": "Sr", "Yttrium": "Y", "Zirconium": "Zr", "Niobium": "Nb", "Molybdenum": "Mo", "Technetium": "Tc", "Ruthenium": "Ru", "Rhodium": "Rh", "Palladium": "Pd", "Silver": "Ag", "Cadmium": "Cd", "Indium": "In", "Tin": "Sn", "Antimony": "Sb", "Tellurium": "Te", "Iodine": "I", "Xenon": "Xe", "Cesium": "Cs", "Barium": "Ba", "Lanthanum": "La", "Cerium": "Ce", "Praseodymium": "Pr", "Neodymium": "Nd", "Promethium": "Pm", "Samarium": "Sm", "Europium": "Eu", "Gadolinium": "Gd", "Terbium": "Tb", "Dysprosium": "Dy", "Holmium": "Ho", "Erbium": "Er", "Thulium": "Tm", "Ytterbium": "Yb", "Lutetium": "Lu", "Hafnium": "Hf", "Tantalum": "Ta", "Tungsten": "W", "Rhenium": "Re", "Osmium": "Os", "Iridium": "Ir", "Platinum": "Pt", "Gold": "Au", "Mercury": "Hg", "Thallium": "Tl", "Lead": "Pb", "Bismuth": "Bi", "Polonium": "Po", "Astatine": "At", "Radon": "Rn", "Francium": "Fr", "Radium": "Ra", "Actinium": "Ac", "Thorium": "Th", "Protactinium": "Pa", "Uranium": "U", "Neptunium": "Np", "Plutonium": "Pu", "Americium": "Am", "Curium": "Cm", "Berkelium": "Bk", "Californium": "Cf", "Einsteinium": "Es", "Fermium": "Fm", "Mendelevium": "Md", "Nobelium": "No", "Lawrencium": "Lr", "Rutherfordium": "Rf", "Dubnium": "Db", "Seaborgium": "Sg", "Bohrium": "Bh", "Hassium": "Hs", "Meitnerium": "Mt", "Darmstadtium": "Ds", "Roentgenium": "Rg", "Copernicium": "Cn", "Nihonium": "Nh", "Flerovium": "Fl", "Moscovium": "Mc", "Livermorium": "Lv", "Tennessine": "Ts", "Oganesson": "Og" } return element_dic def merged_or_not(self): flag = self.ui.mergeBox.isChecked() if flag: sqlmerge = "NUM_RECORDS NOT LIKE 'NONE'" else: sqlmerge = "NUM_RECORDS LIKE 'NONE'" return flag, sqlmerge @pyqtSlot() def on_homeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.ui.mergeBox.setChecked(False) @pyqtSlot() def on_mergeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS NOT LIKE 'NONE'") self.ui.mergeBox.setChecked(True) @pyqtSlot() def on_searchButton_clicked(self): searchtext = self.ui.searchInput.text() searchclass = self.ui.searchClass.currentText() matchtype = self.ui.matchType.currentText() flag, sqlmerge = self.merged_or_not() if searchclass == "DOI": if matchtype == "Exact Match": self.tabModel.setFilter("DOI LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("DOI LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == "Warning": if matchtype == "Exact Match": self.tabModel.setFilter("WARNING LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("WARNING LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == 'Name': try: searchtext = self.get_elements()[searchtext.capitalize()] except BaseException: pass if matchtype == "Exact Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter("%s LIKE '%s' " % (searchclass, searchtext)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'VOLTAGE' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CAPACITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CONDUCTIVITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'COULOMBIC EFFICIENCY' AND %s " % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'ENERGY'AND %s " % (searchclass, searchtext, sqlmerge)) elif matchtype == "Generic Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter( "(EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s') AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such compounds in the database. Please search new compounds." ) # ============Picture Tab 1, Pie Chart===================== @pyqtSlot() # Draw the pie chart def on_btnDrawPieChart_clicked(self): self.draw_pieChart() @pyqtSlot(float) # Set holeSize def on_spinHoleSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setHoleSize(arg1) @pyqtSlot(float) # Set pieSize def on_spinPieSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setPieSize(arg1) @pyqtSlot(bool) # Set legend checkbox def on_chkBox_PieLegend_clicked(self, checked): self.ui.chartViewPie.chart().legend().setVisible(checked) def pie_data( self): # Return a list of property name and the number of property num_list = [] pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for i in range(len(pro_list)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT(NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro_list[i]) # Query database while query.next(): num_value = query.value(0) # Returned value for each query item = self.ui.treeWidget_2.topLevelItem(i) # The ith row # The 2nd column item.setText(1, str(num_value)) item.setTextAlignment(1, Qt.AlignHCenter) num_list.append(num_value) return pro_list, num_list def draw_pieChart(self): # Draw the pie chart chart = self.ui.chartViewPie.chart() chart.legend().setAlignment(Qt.AlignRight) # AlignRight,AlignBottom chart.removeAllSeries() # Delete all series seriesPie = QPieSeries() # Pie chart series seriesPie.setHoleSize(self.ui.spinHoleSize.value()) # Hole size seriesPie.setPieSize(self.ui.spinPieSize.value()) # Pie size sec_count = 5 # Number of properties seriesPie.setLabelsVisible(True) # Label pro, num = self.pie_data() for i in range(sec_count): seriesPie.append(pro[i], num[i]) seriesPie.setLabelsVisible(True) # Label # Pie hoverd when mouse selected seriesPie.hovered.connect(self.do_pieHovered) chart.addSeries(seriesPie) chart.setTitle("Proportion of data records for each property") font = QFont() font.setPointSize(12) font.setWeight(75) chart.setTitleFont(font) font = QFont() font.setPointSize(12) font.setBold(False) font.setWeight(35) legend = chart.legend() legend.setFont(font) # =========Picture tab 2. StackedBar========= @pyqtSlot() # Draw StackedBar def on_btnStackedBar_clicked(self): self.draw_stackedBar() @pyqtSlot() # Draw horizontal StackedBar def on_btnStackedBarH_clicked(self): self.draw_stackedBar(False) # Search button in the Stacked bar chart tab. Add name to the first column # of the table. @pyqtSlot() def on_searchButton_3_clicked(self): searchtext = self.ui.searchInput_3.text() current_index = self.ui.stackedWidget.topLevelItemCount() item_0 = QtWidgets.QTreeWidgetItem(self.ui.stackedWidget) item_0.setText(0, searchtext) @pyqtSlot() # Generate data using the input chemical names def on_generateButton_3_clicked(self): self.ui.zoominButton.setEnabled(True) self.ui.zoomoutButton.setEnabled(True) self.ui.originalButton.setEnabled(True) self.ui.btnStackedBar.setEnabled(True) # Stacked bar chart self.ui.btnStackedBarH.setEnabled(True) current_index = self.ui.stackedWidget.topLevelItemCount() chemical_list = [ self.ui.stackedWidget.topLevelItem(index).text(0) for index in range(current_index) ] # Get a list of the inputed chemical name pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for row, chemical in enumerate(chemical_list): for index, pro in enumerate(pro_list): query = QSqlQuery( db=self.DB, query= "SELECT SUM(NUM_RECORDS) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NAME LIKE '%s' " % (pro, chemical)) while query.next(): num_value = query.value(0) print(num_value) if num_value == "": num_value = 0 item = self.ui.stackedWidget.topLevelItem( row) # The row'th row item.setText(index + 1, str(num_value)) item.setTextAlignment(index + 1, Qt.AlignHCenter) @pyqtSlot() # Clear button def on_clearButton_3_clicked(self): self.ui.stackedWidget.clear() @pyqtSlot() # Zoom in def on_zoominButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(1.2) @pyqtSlot() # Zoom out def on_zoomoutButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(0.8) @pyqtSlot() # Reset original size def on_originalButton_clicked(self): self.ui.chartViewStackedBar.chart().zoomReset() def draw_stackedBar(self, isVertical=True): # Stacked bar chart chart = self.ui.chartViewStackedBar.chart() chart.removeAllSeries() # Remove all series chart.removeAxis(chart.axisX()) # remove axis chart.removeAxis(chart.axisY()) if isVertical: # Vertical chart.setTitle("Number of property records for each chemical ") chart.legend().setAlignment(Qt.AlignBottom) else: # Horizontal chart.setTitle("Number of property records for each chemical") chart.legend().setAlignment(Qt.AlignRight) # Create data sets setCapacity = QBarSet("Capacity") setConductivity = QBarSet("Conductivity") setCoulombic = QBarSet("Coulombic") setEnergy = QBarSet("Energy") setVoltage = QBarSet("Voltage") chemical_Count = self.ui.stackedWidget.topLevelItemCount() nameList = [] # Chemical lists for i in range(chemical_Count): item = self.ui.stackedWidget.topLevelItem(i) # print(item.text(1)) nameList.append(item.text(0)) setCapacity.append(float(item.text(1))) setConductivity.append(float(item.text(2))) setCoulombic.append(float(item.text(3))) setEnergy.append(float(item.text(4))) setVoltage.append(float(item.text(5))) # Create series if isVertical: seriesBar = QStackedBarSeries() else: seriesBar = QHorizontalStackedBarSeries() seriesBar.append(setCapacity) seriesBar.append(setConductivity) seriesBar.append(setCoulombic) seriesBar.append(setEnergy) seriesBar.append(setVoltage) seriesBar.setLabelsVisible(True) # Show labels for each bar seriesBar.setLabelsFormat("@value") seriesBar.setLabelsPosition(QAbstractBarSeries.LabelsCenter) seriesBar.setBarWidth(0.3) chart.addSeries(seriesBar) axisStud = QBarCategoryAxis() # Category axis axisStud.append(nameList) axisStud.setRange(nameList[0], nameList[chemical_Count - 1]) axisValue = QValueAxis() # Value axis # axisValue.setRange(0, 300) axisValue.setTitleText("Number of records") # axisValue.setTickCount(6) axisValue.applyNiceNumbers() if isVertical: chart.setAxisX(axisStud, seriesBar) chart.setAxisY(axisValue, seriesBar) else: chart.setAxisY(axisStud, seriesBar) chart.setAxisX(axisValue, seriesBar) for marker in chart.legend().markers(): # QLegendMarker lists marker.clicked.connect(self.do_LegendMarkerClicked) # =========Picture tab 3. Histogram========= @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_2_clicked(self, checked): self.ui.widgetHist_2.setToolbarVisible(checked) @pyqtSlot() # Draw def on_histButton_clicked(self): self.__drawHist() def hist_data(self, pro): query = QSqlQuery( db=self.DB, query= "SELECT VALUE FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro) data = [] while query.next(): num_value = query.value(0) data.append(num_value) return data def __drawHist(self): # Histogram pro = self.ui.propertycomboBox_2.currentText() data = self.hist_data(pro) self.ui.widgetHist_2.figure.clear() # Clear figure ax = self.ui.widgetHist_2.figure.add_subplot(1, 1, 1) if pro == 'Capacity': M, bins, patches = ax.hist( [5000 if float(i) > 4999 else float(i) for i in data], bins='auto', color='darkgreen', alpha=0.5, rwidth=1) ax.set_xlim(0, 5001) # ax.set_ylim(0,15000) ax.tick_params(labelsize=12) ax.set_xticklabels(['0', '1000', '2000', '3000', '4000', '5000+']) ax.set_xlabel('Capacity (mAh/g)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Capacity Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Voltage": count1 = [] for i in data: count1.append(float(i)) n, bins, patches = ax.hist(x=count1, bins='auto', range=(0, 8), color='r', alpha=0.5, rwidth=1) ax.set_xticklabels(['0', '1', '2', '3', '4', '5', '6', '7', '8+']) ax.set_xlim(0, 8) ax.tick_params(labelsize=12) ax.set_xlabel('Voltage (V)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Voltage Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Energy": n, bins, patches = ax.hist( [3000 if float(i) > 2999 else float(i) for i in data], bins='auto', color='y', alpha=0.5, rwidth=1) ax.set_xticklabels( ['0', '500', '1000', '1500', '2000', '2500', '3000+']) ax.set_xlim(0, 3001) ax.tick_params(labelsize=12) ax.set_xlabel('Energy (Wh/kg)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Energy Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Coulombic Efficiency": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins='auto', color='c', alpha=0.5, rwidth=1) ax.set_xlim(0, 100) ax.tick_params(labelsize=12) ax.set_xlabel('Coulombic Effciency (%)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Coulombic Effciency Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Conductivity": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins=np.logspace( np.log10(1e-15), np.log10(1)), color='k', alpha=0.5, rwidth=1) ax.set_xlim(1e-20, 10) ax.set_xscale('log') ax.tick_params(labelsize=12) ax.set_xlabel('log10 (Conductivity (S/cm))', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Conductivity Distrbution', fontsize=14) ax.figure.canvas.draw() # =========Picture tab 4. Venn diagram========= @pyqtSlot() # Draw def on_btnVenn_clicked(self): self.__drawVenn() @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_3_clicked(self, checked): self.ui.widgetVenn.setToolbarVisible(checked) def __drawVenn(self): self.ui.widgetVenn.figure.clear() # Clear figure pro_list = [ "Capacity", "Conductivity", "Coulombic Efficiency", "Energy", "Voltage" ] data_dic = {} color_dic = dict(zip(pro_list, ['C0', 'C2', 'C6', 'C8', 'C9'])) for i in pro_list: query = QSqlQuery( db=self.DB, query= "SELECT COUNT(DISTINCT NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % i) while query.next(): num_value = query.value(0) data_dic[i] = num_value for i, combo in enumerate(itertools.combinations(pro_list, 2)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT() FROM (SELECT DISTINCT NAME AS PRO1 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') INNER JOIN (SELECT DISTINCT NAME AS PRO2 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') ON PRO1 = PRO2" % combo) while query.next(): num = query.value(0) x3 = num x1 = data_dic[combo[0]] - x3 x2 = data_dic[combo[1]] - x3 hf = self.ui.widgetVenn.figure # hf.set_figheight(30) # hf.set_figwidth(30) # print(dir(hf)) hf.set_size_inches((10, 10)) ax1 = hf.add_subplot(5, 2, i + 1) v = venn2(subsets=(x1, x2, x3), set_labels=(combo[0], combo[1]), ax=ax1) v.get_patch_by_id('A').set_alpha(1) v.get_patch_by_id('A').set_color(color_dic[combo[0]]) v.get_patch_by_id('B').set_color(color_dic[combo[1]]) ax1.figure.canvas.draw() # =============Self-defined slot function=============================== # Update the conditions of actPost and actCancel def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled( self.tabModel.isDirty()) # Use when not saving changes self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): # Control during row changes self.ui.actRecDelete.setEnabled(current.isValid()) # Update current row index of mapping self.mapper.setCurrentIndex(current.row()) # Get current record,QSqlRecord curRec = self.tabModel.record(current.row()) def do_pieHovered(self, pieSlice, state): # Mouse move in and out in the pie chart pieSlice.setExploded(state) # Pop-up animation if state: # Show the tab of percentages self.__oldLabel = pieSlice.label() # Save original labels pieSlice.setLabel(self.__oldLabel + ": %.1f%%" % (pieSlice.percentage() * 100)) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) else: # show original labels pieSlice.setLabel(self.__oldLabel) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) def do_LegendMarkerClicked(self): # Click legend marker marker = self.sender() # QLegendMarker marker.series().setVisible(not marker.series().isVisible()) marker.setVisible(True) alpha = 1.0 if not marker.series().isVisible(): alpha = 0.5 brush = marker.labelBrush() # QBrush color = brush.color() # QColor color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) pen = marker.pen() # QPen color = pen.color() color.setAlphaF(alpha) pen.setColor(color) marker.setPen(pen)
class SignAnnounceWidget(QWidget): """Widget that displays information about signing a Masternode Announce.""" def __init__(self, parent): super(SignAnnounceWidget, self).__init__(parent) self.dialog = parent self.manager = parent.manager # Displays the status of the masternode. self.status_edit = QLineEdit() self.status_edit.setReadOnly(True) self.alias_edit = QLineEdit() self.collateral_edit = PrevOutWidget() self.delegate_edit = QLineEdit() self.delegate_edit.setFont(QFont(util.MONOSPACE_FONT)) for i in [self.alias_edit, self.collateral_edit, self.delegate_edit]: i.setReadOnly(True) self.mapper = QDataWidgetMapper() self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setModel(self.dialog.masternodes_widget.proxy_model) model = self.dialog.masternodes_widget.model self.mapper.addMapping(self.alias_edit, model.ALIAS) self.mapper.addMapping(self.collateral_edit, model.VIN, b'string') self.mapper.addMapping(self.delegate_edit, model.DELEGATE) self.sign_button = QPushButton(_('Activate Masternode')) self.sign_button.setEnabled(False) self.sign_button.clicked.connect(self.sign_announce) status_box = QHBoxLayout() status_box.setContentsMargins(0, 0, 0, 0) status_box.addWidget(QLabel(_('Status:'))) status_box.addWidget(self.status_edit, stretch=1) vbox = QVBoxLayout() vbox.addLayout(status_box) form = QFormLayout() form.addRow(_('Alias:'), self.alias_edit) form.addRow(_('Collateral vestx Output:'), self.collateral_edit) form.addRow(_('Masternode Private Key:'), self.delegate_edit) vbox.addLayout(form) vbox.addLayout(util.Buttons(self.sign_button)) self.setLayout(vbox) def set_mapper_index(self, row): """Set the row that the data widget mapper should use.""" self.status_edit.clear() self.status_edit.setStyleSheet( util.ColorScheme.DEFAULT.as_stylesheet()) self.mapper.setCurrentIndex(row) mn = self.dialog.masternodes_widget.masternode_for_row(row) # Disable the sign button if the masternode can't be signed (for whatever reason). status_text = '%s can be activated' % mn.alias can_sign = True try: self.manager.check_can_sign_masternode(mn.alias) except Exception as e: status_text = str(e) can_sign = False self.status_edit.setText(_(status_text)) self.sign_button.setEnabled(can_sign) def sign_announce(self): """Set the masternode's vin and sign an announcement.""" self.mapper.submit() self.dialog.sign_announce(str(self.alias_edit.text()))
class EditForm(QWidget, Ui_EditForm): def __init__(self, current_index, model): super().__init__() # uic.loadUi('editForm.ui', self) self.setupUi(self) # self.db = database self.db_map = QDataWidgetMapper(self) self.db_map.setModel(model) self.db_map.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.db_map.addMapping(self.lineEdit_name, 1) self.db_map.addMapping(self.lineEdit_inv_number, 2) self.db_map.addMapping(self.textEdit_comments, 3) self.db_map.addMapping(self.checkBox_on_balance, model.fieldIndex('is_on_balance')) relModel_status = model.relationModel(5) self.comboBox_status.setModel(relModel_status) self.comboBox_status.setModelColumn( relModel_status.fieldIndex("status_name")) self.db_map.setItemDelegate(QSqlRelationalDelegate(self)) self.db_map.addMapping(self.comboBox_status, 5) relModel_type = model.relationModel(6) self.comboBox_type.setModel(relModel_type) self.comboBox_type.setModelColumn( relModel_type.fieldIndex("goods_type_name")) self.db_map.setItemDelegate(QSqlRelationalDelegate(self)) self.db_map.addMapping(self.comboBox_type, 6) relModel_subtype = model.relationModel(7) self.comboBox_subtype.setModel(relModel_subtype) self.comboBox_subtype.setModelColumn( relModel_subtype.fieldIndex("goods_subtype_name")) self.db_map.setItemDelegate(QSqlRelationalDelegate(self)) self.db_map.addMapping(self.comboBox_subtype, 7) relModel_location = model.relationModel(8) self.comboBox_location.setModel(relModel_location) self.comboBox_location.setModelColumn( relModel_location.fieldIndex("location_name")) self.db_map.setItemDelegate(QSqlRelationalDelegate(self)) self.db_map.addMapping(self.comboBox_location, 8) relModel_responsible = model.relationModel(9) self.comboBox_responsible.setModel(relModel_responsible) self.comboBox_responsible.setModelColumn( relModel_responsible.fieldIndex("FIO")) self.db_map.setItemDelegate(QSqlRelationalDelegate(self)) self.db_map.addMapping(self.comboBox_responsible, 9) self.pushBtn_save.clicked.connect(self.save_item) self.pushBtn_cancel.clicked.connect(self.cancel) self.pushBtn_next.clicked.connect(self.db_map.toNext) self.pushBtn_prev.clicked.connect(self.db_map.toPrevious) self.pushBtn_close.clicked.connect(self.close) self.db_map.setCurrentIndex(current_index.row()) def save_item(self): self.db_map.submit() def cancel(self): self.db_map.setCurrentIndex(self.db_map.currentIndex())