class cMainWindow(QMainWindow): def __init__(self): super(cMainWindow, self).__init__() loadUi('MainWindow.ui', self) self.mModel = Model.cModel() self.mainList.setModel( self.mModel ) self.treeView.setModel( self.mModel ) self.mMapper = QDataWidgetMapper() self.mMapper.setModel( self.mModel ) self.mMapper.setOrientation( Qt.Vertical ) self.mMapper.addMapping( self.nameLineEdit, 0) self.mMapper.addMapping( self.weaponComboBox, 1, b"currentText" ) self.mMapper.addMapping( self.shiledSpinBox, 2) self.mainList.selectionModel().currentChanged.connect( self.CombattantSelected ) self.shiledSpinBox.valueChanged.connect( self.SubmitToModel ) self.weaponComboBox.currentIndexChanged.connect( self.SubmitToModel ) weaponList = "Fist", "Dagger", "Sword", "Pike" self.weaponComboBox.addItems( weaponList ) # -------------------------------- def CombattantSelected(self, iIndex): self.mMapper.setRootIndex( iIndex ) self.mMapper.toFirst() # -------------------------------- def SubmitToModel(self, iValue): self.mMapper.submit() # --------------------------------
class TaskDetails(DetailScreen): """ Displays all current Research Plans """ def __init__(self, data_context): super().__init__(data_context) form_layout = QFormLayout() self.source = LinkedLineEdit( self.data_context.sources_model, SourcesModelColumns.AUTOCOMPLETE, SourcesModelColumns.POINTER, ) self.source.link_updated.connect(self.link_updated) form_layout.addRow(QLabel("Source:"), self.source) self.description = QTextEdit() form_layout.addRow(QLabel("Description:"), self.description) self.result = ResultWidget() form_layout.addRow(QLabel("Results:"), self.result) form_group = QGroupBox("Task") form_group.setLayout(form_layout) layout = QVBoxLayout() layout.addWidget(form_group) # Don't add this, we just want to get/set the value self.link = QLineEdit() self.setLayout(layout) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.data_context.data_model) self.mapper.addMapping(self.source, TreeModelCols.TEXT) self.mapper.addMapping(self.description, TreeModelCols.DESCRIPTION, b"plainText") self.mapper.addMapping(self.result, TreeModelCols.RESULT) self.mapper.addMapping(self.link, TreeModelCols.LINK) self.result.result_changed.connect(self.mapper.submit) self.mapper.currentIndexChanged.connect( lambda _: self.source.set_link_visible(self.link.text() != "")) self.data_context.data_model.dataChanged.connect( lambda _, __: self.source.set_link_visible(self.link.text() != "")) self.mapper.toFirst() def link_updated(self, text: str): """ Called when the link needs updating from the LineEdit """ self.link.setText(text) self.mapper.submit()
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 PlanDetails(DetailScreen): """ Displays all current Research Plans """ def __init__(self, data_context): super(PlanDetails, self).__init__(data_context) form_layout = QFormLayout() self.ancestor = LinkedLineEdit( self.data_context.individuals_model, IndividualsModelColumns.AUTOCOMPLETE, IndividualsModelColumns.POINTER, ) self.ancestor.link_updated.connect(self.link_updated) form_layout.addRow(QLabel("Ancestor:"), self.ancestor) self.goal = QTextEdit() form_layout.addRow(QLabel("Goal:"), self.goal) form_group = QGroupBox("Plan") form_group.setLayout(form_layout) layout = QVBoxLayout() layout.addWidget(form_group) # Don't add this, we just want to get/set the value self.link = QLineEdit() self.setLayout(layout) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.data_context.data_model) self.mapper.addMapping(self.ancestor, TreeModelCols.TEXT) self.mapper.addMapping(self.goal, TreeModelCols.DESCRIPTION, b"plainText") self.mapper.addMapping(self.link, TreeModelCols.LINK) self.mapper.currentIndexChanged.connect( lambda _: self.ancestor.set_link_visible(self.link.text() != "") ) self.data_context.data_model.dataChanged.connect( lambda _, __: self.ancestor.set_link_visible(self.link.text() != "") ) self.mapper.toFirst() def link_updated(self, text: str): """ Called when the link needs updating from the LineEdit """ self.link.setText(text) self.mapper.submit()
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 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 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 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 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 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 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())