class ProductDetailsWidget(ProwlerSqlWidget): """Base class for the AmzProductDetailsWidget and VndProductDetailsWidget classes. Doesn't have it's own UI, but depends on certain UI elements (like self.titleLine) to be available. """ def __init__(self, parent=None): super(ProductDetailsWidget, self).__init__(parent=parent) self.setupUi(self) # Populate the columns self.set_source(None) # Set up the data widget mapper self.mapper = QDataWidgetMapper(self) self.mapper.setModel(self.model) self.mapper.setItemDelegate(DataMapperDelegate(self)) self.mapper.currentIndexChanged.connect(self.rewind_lines) self.mapper.addMapping(self.titleLine, self.model.fieldIndex('title')) self.mapper.addMapping(self.brandLine, self.model.fieldIndex('brand')) self.mapper.addMapping(self.modelLine, self.model.fieldIndex('model')) self.mapper.addMapping(self.skuLine, self.model.fieldIndex('sku')) self.mapper.addMapping(self.upcLine, self.model.fieldIndex('upc')) self.mapper.addMapping(self.priceBox, self.model.fieldIndex('price')) self.mapper.addMapping(self.quantityBox, self.model.fieldIndex('quantity')) self.model.modelReset.connect(self.mapper.toFirst) def rewind_lines(self): """Reset text lines to home position.""" self.titleLine.home(False) self.brandLine.home(False) self.modelLine.home(False)
def schema_ui_map(schema, model, form): """Construct a QDataWidgetMapper from the given ``schema`` class. (Function 4) :param schema: The schema to create field-to-widget mappings from. :param model: The model that the QDataWidgetMapper observes. :param form: The UI widget containing stuff to bind to. It is also set as the parent for the widget mapper. """ assert isinstance(model, QAbstractItemModel) mapper = QDataWidgetMapper(form) mapper.setModel(model) s = schema() for i, (name, field) in enumerate(s.fields.items()): try: widget_classes = _widget_type(type(field)) widget = form.findChild(widget_classes, name) if not widget: raise ValueError prop = _widget_property(type(widget)) log.debug("adding map from (`%s', col = %d) to %s (prop = `%s')", name, i, widget, prop) mapper.addMapping(widget, i, bytes(prop, encoding='utf8')) assert mapper.mappedWidgetAt(i) == widget except KeyError: log.error("unknown field type %s", type(field)) except ValueError: # FIXME: is this the correct exception type? log.error("failed to find widget for field `%s'", name) mapper.toFirst() return mapper
def __init__(self, tag_number): super().__init__() print('processing query...') qry = QSqlQuery(db) query = 'SELECT name, ename, startno, starttime FROM name WHERE ecard = %i OR ecard2 = %i' % ( tag_number, tag_number) qry.prepare(query) qry.exec() model = QSqlQueryModel() model.setQuery(qry) print(model.rowCount()) mapper = QDataWidgetMapper() form = QFormLayout() layout = QVBoxLayout() first_name = QLineEdit() start_number = QLineEdit() form.addRow(QLabel("Startnummer"), start_number) form.addRow(QLabel("Fornavn"), first_name) mapper.setModel(model) mapper.addMapping(first_name, 0) mapper.addMapping(start_number, 2) mapper.toFirst() layout.addLayout(form) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget) #controls = QHBoxLayout() '''
class DynamicVariableEditor(DynVarForm, DynVarBase): def __init__(self, parent=None): super(DynamicVariableEditor, self).__init__() self.setupUi(self) self._model = None self._data_mapper = QDataWidgetMapper(self) @property def model(self): return self._model @model.setter def model(self, model): self._model = model self._data_mapper.setModel(model) self._data_mapper.addMapping(self.dyn_var_name, 0) self._data_mapper.addMapping(self.dyn_var_default, 1) self._data_mapper.addMapping(self.dyn_var_start, 2) self._data_mapper.addMapping(self.dyn_var_end, 3) self._data_mapper.addMapping(self.dyn_var_log, 4) self._data_mapper.addMapping(self.dyn_var_send, 5) self._data_mapper.addMapping(self.dyn_var_stepsize, 6, 'text') self.dyn_var_log.clicked.connect(self.dyn_var_log.clearFocus) self.dyn_var_send.clicked.connect(self.dyn_var_send.clearFocus) def selectionChanged(self, current_index, old_index): self._data_mapper.setCurrentIndex(current_index.row())
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 ProposalEditor(QWidget): """Editor for proposals.""" def __init__(self, main_widget, parent=None): super(ProposalEditor, self).__init__(parent) self.main_widget = main_widget self.name_edit = QLineEdit() self.url_edit = QLineEdit() self.start_block_edit = QLineEdit() self.end_block_edit = QLineEdit() self.amount_edit = QLineEdit() self.address_edit = QLineEdit() self.txid_edit = QLineEdit() for i in [self.name_edit, self.url_edit, self.start_block_edit, self.end_block_edit, self.amount_edit, self.address_edit, self.txid_edit]: i.setReadOnly(True) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.main_widget.proxy_model) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.addMapping(self.name_edit, ProposalsModel.NAME) self.mapper.addMapping(self.url_edit, ProposalsModel.URL) self.mapper.addMapping(self.start_block_edit, ProposalsModel.START_BLOCK) self.mapper.addMapping(self.end_block_edit, ProposalsModel.END_BLOCK) self.mapper.addMapping(self.amount_edit, ProposalsModel.AMOUNT) self.mapper.addMapping(self.address_edit, ProposalsModel.ADDRESS) self.mapper.addMapping(self.txid_edit, ProposalsModel.TXID) block_hbox = QHBoxLayout() block_hbox.addWidget(self.start_block_edit) block_hbox.addWidget(QLabel(' - ')) block_hbox.addWidget(self.end_block_edit) self.vote_combo = QComboBox() self.vote_combo.addItem(_('Yes')) self.vote_combo.addItem(_('No')) self.vote_button = QPushButton(_('Vote')) self.vote_button.clicked.connect(self.cast_vote) vote_hbox = util.Buttons(self.vote_combo, self.vote_button) form = QFormLayout() form.addRow(_('Name:'), self.name_edit) form.addRow(_('URL:'), self.url_edit) form.addRow(_('Blocks:'), block_hbox) form.addRow(_('Monthly Payment:'), self.amount_edit) form.addRow(_('Payment Address:'), self.address_edit) form.addRow(_('Fee TxID:'), self.txid_edit) form.addRow(_('Vote:'), vote_hbox) self.setLayout(form) def cast_vote(self): name = str(self.name_edit.text()) vote_yes = True if self.vote_combo.currentIndex() == 0 else False self.main_widget.dialog.cast_vote(name, vote_yes)
class JPMainTableModel(QtSql.QSqlRelationalTableModel): # _tp中存放需要添加到数据映射器中的控件类型 # 要添加的控件,必须用字段名命名(大小写敏感) _tp = (QtWidgets.QLineEdit, QtWidgets.QDateEdit, QtWidgets.QComboBox, QtWidgets.QTextEdit, QtWidgets.QCheckBox, QtWidgets.QSpinBox) def __init__(self, parent: QWidget, tableName: str, filter: str = None, db=QtSql.QSqlDatabase()): '''用于窗体模式进行数据编辑时的主窗体数据模型。\n 会自动增加数据映射器,但是外键字段要使用addComboBoxData方法增加列表文字。 最后要调用tofirst()方法定位编辑的记录。\n 注:数据映射器不能增删记录,只能编辑 ''' super().__init__(parent=parent, db=db) self.parent = parent self.setTable(tableName) if filter: self.setFilter(filter) self.select() rec = self.record() self.mapper = QDataWidgetMapper(parent) self.mapper.setModel(self) self.mapper.setItemDelegate(_JPRelationalDelegate(parent, self.mapper)) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) for i in range(rec.count()): widget = parent.findChild(self._tp, rec.fieldName(i)) if widget: if not isinstance(widget, QComboBox): self.mapper.addMapping(widget, i) def toFirst(self): # 定位到模型中第一条记录 self.mapper.toFirst() def addComboBoxData(self, fieldName: str, dataList: list, displayColumn: int = 0, modelColumn: int = 1): '''给模型中添加一个QComboBox控件的数据源。\n dataList为行来源数据源,列表对象.\n displayColumn为Combobox中要显示文字位于列表中的列号(首列为0)\n modelColumn为要保存到模型中数据在列表中的列号(首列为0)\n ''' widget = self.parent.findChild(QComboBox, fieldName) if widget is not None: widget.setModel( _JPComboBoxModel(widget, dataList, displayColumn, modelColumn)) self.mapper.addMapping(widget, self.fieldIndex(fieldName)) def saveData(self): pass
def __init__(self, contact_list_model, contact_index): super().__init__() ui_file = BASE_UI_PATH / "edit_contact_window.ui" uic.loadUi(ui_file, self) self.gender_editbox.addItems(GENDER_LIST) contact = contact_list_model.contact_by_index(contact_index) mapper = QDataWidgetMapper(self) mapper.setModel(contact_list_model) for section, name in enumerate(contact.field_names()): mapper.addMapping(getattr(self, f"{name}_editbox"), section) mapper.setCurrentIndex(contact_index)
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 VnTreeView(QTreeView): tree_clicked = pyqtSignal(str) def __init__(self, parent=None, treerootNode=None): super().__init__(parent) self.setHeaderHidden(True) self.rootIsDecorated = True #self.setup_treemodel(treerootNode) def setup_treemodel(self, treerootNode=None, treetype='FsTree'): if treetype=='FsTree': treemodel = FsTreeModel(treerootNode) else: treemodel = VnTreeModel(treerootNode) self.setModel(treemodel) self.dataMapper = QDataWidgetMapper() self.dataMapper.setModel(treemodel)
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 CommitDateMaxFilterEditor(QWidget, Ui_CommitDateMaxFilter): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self._data_mapper = QDataWidgetMapper() def setModel(self, model): self._model = model self._data_mapper.setModel(model) self._data_mapper.addMapping(self.uiCommitDateMax, 2) def setSelection(self, current: QModelIndex) -> None: parent = current.parent() self._data_mapper.setRootIndex(parent) self._data_mapper.setCurrentModelIndex(current)
class NodeEditor(QWidget, Ui_FilterNodeProperties): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self._data_mapper = QDataWidgetMapper() def setModel(self, model): self._model = model self._data_mapper.setModel(model) self._data_mapper.addMapping(self.uiName, 0) self._data_mapper.addMapping(self.uiComment, 1) def setSelection(self, current: QModelIndex) -> None: parent = current.parent() self._data_mapper.setRootIndex(parent) self._data_mapper.setCurrentModelIndex(current)
class CommitDateDeltaMinFilterEditor(QWidget, Ui_CommitDateDeltaMinFilter): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self._data_mapper = QDataWidgetMapper() self.uiHelp.clicked.connect(_showTimeDurationHelp) def setModel(self, model): self._model = model self._data_mapper.setModel(model) self._data_mapper.addMapping(self.uiCommitDateDeltaMin, 2) def setSelection(self, current: QModelIndex) -> None: parent = current.parent() self._data_mapper.setRootIndex(parent) self._data_mapper.setCurrentModelIndex(current)
class AuthorFilterEditor(QWidget, Ui_AuthorFilterProperties): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self._data_mapper = QDataWidgetMapper() def setModel(self, model): self._model = model self._data_mapper.setModel(model) self._data_mapper.addMapping(self.uiAuthorName, 2) self._data_mapper.addMapping(self.uiAuthorEmail, 3) def setSelection(self, current: QModelIndex) -> None: parent = current.parent() self._data_mapper.setRootIndex(parent) self._data_mapper.setCurrentModelIndex(current)
def setMappings(mappings): """Set the mappings between the model and widgets. TODO: - Should this be extended to accept other columns? - Check if the already has the model. """ column = 1 mappers = list() for widget, obj in mappings: mapper = QDataWidgetMapper(widget) # logger.debug(obj.model()) mapper.setModel(obj.model()) mapper.addMapping(widget, column) delegate = Delegate(widget) mapper.setItemDelegate(delegate) mapper.setRootIndex(obj.parent().index()) mapper.setCurrentModelIndex(obj.index()) # QDataWidgetMapper needs a focus event to notice a change in the data. # To make sure the model is informed about the change, I connected the # stateChanged signal of the QCheckBox to the submit slot of the # QDataWidgetMapper. The same idea goes for the QComboBox. # https://bugreports.qt.io/browse/QTBUG-1818 if isinstance(widget, QCheckBox): signal = widget.stateChanged try: signal.disconnect() except TypeError: pass signal.connect(mapper.submit) elif isinstance(widget, QComboBox): signal = widget.currentTextChanged try: signal.disconnect() except TypeError: pass signal.connect(mapper.submit) mappers.append(mapper) return mappers
class RecordFormView(QMainWindow): ui_file = '' # type: str model = None def __init__(self, model=None, ui_file: str = None) -> None: super().__init__() loadUi(ui_file or self.ui_file, self) self.model = model or self.model self.data_mapper = QDataWidgetMapper() self.data_mapper.setModel(self.model) for field in model.fields: if hasattr(self, field.name): self.data_mapper.addMapping(getattr(self, field.name), field.index) if hasattr(self, 'setup_ui'): self.setup_ui() def set_record_index(self, index: QModelIndex) -> None: self.data_mapper.setCurrentModelIndex(index)
class Soduku(QtWidgets.QMainWindow, soduku_window.Ui_MainWindow): def __init__(self, parent=None): super(Soduku, self).__init__(parent) self.setupUi(self) self.pushButton_2.clicked.connect(self.solve) self.initLine() self.colums = ["A", "B", "C", "D", "E", "F", "G", "H", "I"] self.progressBar.setMaximum(100) self.progressBar.setMinimum(0) self.progressBar.setValue(0) # self.progressBar.hide() self.pushButton.clicked.connect(self.clear) data = {} for column in self.colums: data[column] = [ v for k, v in sorted({ x.objectName(): x.text() for x in self.findChildren(QtWidgets.QLineEdit, QtCore.QRegExp(f"{column}")) }.items()) ] self.df = pd.DataFrame.from_dict(json.load(open("test.json"))) # self.df = pd.DataFrame.from_dict(data, dtype="int") self.model = manager(self.df) self.mapperA = QDataWidgetMapper() self.mapperA.setModel(self.model) self.mapperA.setCurrentIndex(0) self.mapperA.addMapping(self.A1, 0) self.mapperA.addMapping(self.B1, 1) self.mapperA.addMapping(self.C1, 2) self.mapperA.addMapping(self.D1, 3) self.mapperA.addMapping(self.E1, 4) self.mapperA.addMapping(self.F1, 5) self.mapperA.addMapping(self.G1, 6) self.mapperA.addMapping(self.H1, 7) self.mapperA.addMapping(self.I1, 8) self.mapperB = QDataWidgetMapper() self.mapperB.setModel(self.model) self.mapperB.setCurrentIndex(1) self.mapperB.addMapping(self.A2, 0) self.mapperB.addMapping(self.B2, 1) self.mapperB.addMapping(self.C2, 2) self.mapperB.addMapping(self.D2, 3) self.mapperB.addMapping(self.E2, 4) self.mapperB.addMapping(self.F2, 5) self.mapperB.addMapping(self.G2, 6) self.mapperB.addMapping(self.H2, 7) self.mapperB.addMapping(self.I2, 8) self.mapperC = QDataWidgetMapper() self.mapperC.setModel(self.model) self.mapperC.setCurrentIndex(2) self.mapperC.addMapping(self.A3, 0) self.mapperC.addMapping(self.B3, 1) self.mapperC.addMapping(self.C3, 2) self.mapperC.addMapping(self.D3, 3) self.mapperC.addMapping(self.E3, 4) self.mapperC.addMapping(self.F3, 5) self.mapperC.addMapping(self.G3, 6) self.mapperC.addMapping(self.H3, 7) self.mapperC.addMapping(self.I3, 8) self.mapperD = QDataWidgetMapper() self.mapperD.setModel(self.model) self.mapperD.setCurrentIndex(3) self.mapperD.addMapping(self.A4, 0) self.mapperD.addMapping(self.B4, 1) self.mapperD.addMapping(self.C4, 2) self.mapperD.addMapping(self.D4, 3) self.mapperD.addMapping(self.E4, 4) self.mapperD.addMapping(self.F4, 5) self.mapperD.addMapping(self.G4, 6) self.mapperD.addMapping(self.H4, 7) self.mapperD.addMapping(self.I4, 8) self.mapperE = QDataWidgetMapper() self.mapperE.setModel(self.model) self.mapperE.setCurrentIndex(4) self.mapperE.addMapping(self.A5, 0) self.mapperE.addMapping(self.B5, 1) self.mapperE.addMapping(self.C5, 2) self.mapperE.addMapping(self.D5, 3) self.mapperE.addMapping(self.E5, 4) self.mapperE.addMapping(self.F5, 5) self.mapperE.addMapping(self.G5, 6) self.mapperE.addMapping(self.H5, 7) self.mapperE.addMapping(self.I5, 8) self.mapperF = QDataWidgetMapper() self.mapperF.setModel(self.model) self.mapperF.setCurrentIndex(5) self.mapperF.addMapping(self.A6, 0) self.mapperF.addMapping(self.B6, 1) self.mapperF.addMapping(self.C6, 2) self.mapperF.addMapping(self.D6, 3) self.mapperF.addMapping(self.E6, 4) self.mapperF.addMapping(self.F6, 5) self.mapperF.addMapping(self.G6, 6) self.mapperF.addMapping(self.H6, 7) self.mapperF.addMapping(self.I6, 8) self.mapperG = QDataWidgetMapper() self.mapperG.setModel(self.model) self.mapperG.setCurrentIndex(6) self.mapperG.addMapping(self.A7, 0) self.mapperG.addMapping(self.B7, 1) self.mapperG.addMapping(self.C7, 2) self.mapperG.addMapping(self.D7, 3) self.mapperG.addMapping(self.E7, 4) self.mapperG.addMapping(self.F7, 5) self.mapperG.addMapping(self.G7, 6) self.mapperG.addMapping(self.H7, 7) self.mapperG.addMapping(self.I7, 8) self.mapperH = QDataWidgetMapper() self.mapperH.setModel(self.model) self.mapperH.setCurrentIndex(7) self.mapperH.addMapping(self.A8, 0) self.mapperH.addMapping(self.B8, 1) self.mapperH.addMapping(self.C8, 2) self.mapperH.addMapping(self.D8, 3) self.mapperH.addMapping(self.E8, 4) self.mapperH.addMapping(self.F8, 5) self.mapperH.addMapping(self.G8, 6) self.mapperH.addMapping(self.H8, 7) self.mapperH.addMapping(self.I8, 8) self.mapperI = QDataWidgetMapper() self.mapperI.setModel(self.model) self.mapperI.setCurrentIndex(8) self.mapperI.addMapping(self.A9, 0) self.mapperI.addMapping(self.B9, 1) self.mapperI.addMapping(self.C9, 2) self.mapperI.addMapping(self.D9, 3) self.mapperI.addMapping(self.E9, 4) self.mapperI.addMapping(self.F9, 5) self.mapperI.addMapping(self.G9, 6) self.mapperI.addMapping(self.H9, 7) self.mapperI.addMapping(self.I9, 8) self.threadpool = QThreadPool() index1 = self.model.createIndex(0, 0) index2 = self.model.createIndex(8, 8) self.model.dataChanged.emit(index1, index2) self.view = QtWidgets.QTableView() self.view.setModel(self.model) def clear(self): self.model.clearData() def initLine(self): for child in self.findChildren(QtWidgets.QLineEdit): validator = NotEmptyValidator(child) child.setValidator(validator) def solve(self): self.start = time.time() # Do a easy solve solve = Solve(self.model._data) solve.signals.result.connect(self.result) solve.signals.finished.connect(self.finished) self.threadpool.start(solve) # If easy solved gave no solution, try to to guess one number and solve! def solve_2(self, dataframe): self.progressBar.show() solve2 = Solve2(dataframe) solve2.signals.result.connect(self.result2) solve2.signals.finished.connect(self.finished) solve2.signals.progress.connect(self.progress) self.threadpool.start(solve2) def progress(self, progress): self.progressBar.setValue(progress) def result(self, dataframe): print(dataframe) self.model._data = dataframe index1 = self.model.createIndex(0, 0) index2 = self.model.createIndex(8, 8) self.model.dataChanged.emit(index1, index2) if "" in dataframe.values: pass # If easy solved gave no solution, try to to guess one number and solve! # self.solve_2(dataframe) def result2(self, dataframe): self.model._data = dataframe index1 = self.model.createIndex(0, 0) index2 = self.model.createIndex(8, 8) self.model.dataChanged.emit(index1, index2) if "" in dataframe.values: self.progressBar.setTextVisible(True) self.progressBar.setFormat("Failed to solve current soduku") self.progressBar.setAlignment(Qt.AlignCenter) def finished(self): self.progressBar.setValue(100) self.progressBar.setFormat( f"Solved in {(time.time() - self.start):.2f} s ") self.progressBar.setAlignment(Qt.AlignCenter) def thread_complete(self): print("Done") def product_dict(self, inp): return (dict(zip(inp.keys(), values)) for values in product(*inp.values()))
class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) # Set up the model. self.setupModel() # Set up the widgets. nameLabel = QLabel("Na&me:") nameEdit = QLineEdit() addressLabel = QLabel("&Address:") addressEdit = QTextEdit() typeLabel = QLabel("&Type:") typeComboBox = QComboBox() self.nextButton = QPushButton("&Next") self.previousButton = QPushButton("&Previous") nameLabel.setBuddy(nameEdit) addressLabel.setBuddy(addressEdit) typeLabel.setBuddy(typeComboBox) typeComboBox.setModel(self.typeModel) # Set up the mapper. self.mapper = QDataWidgetMapper(self) self.mapper.setModel(self.model) self.mapper.addMapping(nameEdit, 0) self.mapper.addMapping(addressEdit, 1) self.mapper.addMapping(typeComboBox, 2, 'currentIndex') # Set up connections and layouts. self.previousButton.clicked.connect(self.mapper.toPrevious) self.nextButton.clicked.connect(self.mapper.toNext) self.mapper.currentIndexChanged.connect(self.updateButtons) layout = QGridLayout() layout.addWidget(nameLabel, 0, 0, 1, 1) layout.addWidget(nameEdit, 0, 1, 1, 1) layout.addWidget(self.previousButton, 0, 2, 1, 1) layout.addWidget(addressLabel, 1, 0, 1, 1) layout.addWidget(addressEdit, 1, 1, 2, 1) layout.addWidget(self.nextButton, 1, 2, 1, 1) layout.addWidget(typeLabel, 3, 0, 1, 1) layout.addWidget(typeComboBox, 3, 1, 1, 1) self.setLayout(layout) self.setWindowTitle("Delegate Widget Mapper") self.mapper.toFirst() def setupModel(self): items = ("Home", "Work", "Other") self.typeModel = QStringListModel(items, self) self.model = QStandardItemModel(5, 3, self) names = ("Alice", "Bob", "Carol", "Donald", "Emma") addresses = ("<qt>123 Main Street<br/>Market Town</qt>", "<qt>PO Box 32<br/>Mail Handling Service" "<br/>Service City</qt>", "<qt>The Lighthouse<br/>Remote Island</qt>", "<qt>47338 Park Avenue<br/>Big City</qt>", "<qt>Research Station<br/>Base Camp<br/>Big Mountain</qt>") types = ("0", "1", "2", "0", "2") for row, name in enumerate(names): item = QStandardItem(name) self.model.setItem(row, 0, item) item = QStandardItem(addresses[row]) self.model.setItem(row, 1, item) item = QStandardItem(types[row]) self.model.setItem(row, 2, item) def updateButtons(self, row): self.previousButton.setEnabled(row > 0) self.nextButton.setEnabled(row < self.model.rowCount() - 1)
class MainWindow(QMainWindow): # 继承主窗口函数的类, 继承编写的主函数 def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # 初始化运行A窗口类下的 setupUi 函数 self.open_alltable() self.enterList = {} self.outList = {} self.name_RFID = { '直齿轮': '01B0E00000020010B1', '斜齿轮': '01B0E00000030010B2', '齿轮轴': '01A0E00000010010A1', '光轴': '01A0E00000010010A2', '曲柄': '01C0E00000020010C1', '摇杆': '01C0E00000010010C2' } self.RFID_kind = dict([(value, key) for (key, value) in self.name_RFID.items()]) self.capacity = { '直齿轮': 18, '斜齿轮': 18, '齿轮轴': 24, '光轴': 24, '曲柄': 36, '摇杆': 36 } self.nameNum = { '直齿轮': 3, '斜齿轮': 3, '齿轮轴': 4, '光轴': 4, '曲柄': 6, '摇杆': 6 } self.locateName = { '直齿轮': ['A', 'B', 1], '斜齿轮': ['C', 'D', 1], '齿轮轴': ['A', 'B', 2], '光轴': ['C', 'D', 2], '曲柄': ['A', 'B', 3], '摇杆': ['C', 'D', 3] } self.get_locState() # 获取所有货位的空闲状态 # 初始化出入库按钮使能状态 self.ui.outScan_pushButton.setEnabled(False) self.ui.enterScan_pushButton.setEnabled(False) self.currentList = self.Absolute_statis('current') # 获取入库记录里各种工件的数量 print(self.currentList) def open_alltable(self): ## tableView显示属性设置 self.ui.current_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.current_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.current_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.current_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.current_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.enter_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.enter_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.enter_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.enter_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.enter_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.out_tableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.out_tableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.out_tableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.out_tableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.out_tableView.horizontalHeader().setDefaultSectionSize( 160) # 单元格宽度 self.ui.VICEtableView.setSelectionBehavior( QAbstractItemView.SelectItems) self.ui.VICEtableView.setSelectionMode( QAbstractItemView.SingleSelection) self.ui.VICEtableView.setAlternatingRowColors(True) # 设置相邻记录的不同颜色 self.ui.VICEtableView.verticalHeader().setDefaultSectionSize( 22) # 单元格高度 self.ui.VICEtableView.horizontalHeader().setDefaultSectionSize( 240) # 单元格宽度 # 连接到数据库并打开 self.DB = QSqlDatabase.addDatabase( 'QSQLITE') # 建立与数据库的连接,即QSqlDatabase对象 self.DB.setDatabaseName('MES5.db') # 设置数据库名称 self.DB.open() # 打开数据库 print('数据库打开成功') ## 打开现有库存数据表 self.current_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.current_tabModel.setTable('工件信息表') # 设置需要连接的数据表 self.current_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开入库记录表 self.enter_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.enter_tabModel.setTable('入库记录表') # 设置需要连接的数据表 self.enter_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开出库记录表 self.out_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.out_tabModel.setTable('出库记录表') # 设置需要连接的数据表 self.out_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) ## 打开固定批RFID表======================================================== self.vice_tabModel = QSqlTableModel(self, self.DB) # 创建数据表的模型 self.vice_tabModel.setTable('新建表') # 设置需要连接的数据表 self.vice_tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.ui.VICEtableView.setModel( self.vice_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.vice_tabModel.select() # 建立RFID与组件的映射关系 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.vice_tabModel) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与tabelmodel的具体字段之间的联系 self.mapper.addMapping(self.ui.RFIDlineEdit, 0) self.mapper.toFirst() # 移动到首记录 # 选中数据时信号的发射 self.selModel = QItemSelectionModel(self.vice_tabModel) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.VICEtableView.setSelectionModel(self.selModel) # 设置选择模型 # =============================================================================== self.refresh() print('数据表打开成功') ##获取字段名和序号的字典数据 empty = self.current_tabModel.record() # 得到的是一个空的记录,获取表的字段定义 self.fldNum = {} for i in range(empty.count()): filedname = empty.fieldName(i) empty.field(filedname).setReadOnly(True) # 每个字段设置为只读 self.fldNum.setdefault( filedname, i) # 如果字典中包含有给定键,则返回该键对应的值,否则将键添加到字典中,默认值为None print(self.fldNum) print(self.current_tabModel.record().count()) def do_currentRowChanged(self, current): self.mapper.setCurrentIndex(current.row()) def refresh(self): self.ui.current_tableView.setModel(self.current_tabModel) self.current_tabModel.select() self.ui.enter_tableView.setModel( self.enter_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.enter_tabModel.select() self.ui.out_tableView.setModel( self.out_tabModel) # 为一个QTableView组件设置一个QSqlTabelModel模型 self.out_tabModel.select() self.inventory_used() #################################路径规划与货位分配########################################### def get_locState(self): # 获取所有货位的状态 self.locationState = {} DecodeLoc = {'A': 0, 'B': 1, 'C': 0, 'D': 1} # 解码字典 for key, value in self.nameNum.items(): self.locationState[key] = np.zeros((2, 3, value), dtype=int) if self.current_tabModel.record(0).isNull('货位') == True: pass else: currentRow = self.current_tabModel.rowCount() # 库存的已记录数 # 根据现有库存更新货位的状态 for i in range(currentRow): curRec = self.current_tabModel.record(i) location = curRec.value('货位') kindname = curRec.value('种类') x = DecodeLoc[location[0]] # 对已有的工件的货位编码进行解码 y = int(location[1]) - 1 z = int(location[5]) - 1 self.locationState[kindname][x][y][z] = 1 #货位分配函数 def allocation(self, name, num, label): # 输入工件名称和对应的数量 if label == 'i': sign = 0 else: sign = 1 itsloc = self.locationState[name] for i in range(3): data = itsloc[:, 0:i + 1] data = data.reshape(1, -1)[0] itsum = sum(data == sign) if itsum >= num: forward_double = i + 1 break idle_loc = np.where( itsloc[:, 0:forward_double] == sign) # 寻找前forward_double对货架的空闲货位 locating = [] # 存放接收工件的货位 for i in range(num): x = idle_loc[0][i] y = idle_loc[1][i] z = idle_loc[2][i] self.locationState[name][x][y][z] = 1 - sign # 分配好后即更新货位状态 locating.append([x, y, z]) EncodeLoc = [] for i in locating: s = self.locateName[name][i[0]] + str(i[1] + 1) + '-' + \ str(self.locateName[name][2]) + '-' + str(i[2] + 1) EncodeLoc.append(s) return EncodeLoc, forward_double def getLocation(self, outList, direct): ALLlocation = [] forwardLocation = {} for key, value in outList.items(): location, position = self.allocation(key, value, direct) ALLlocation = ALLlocation + location forwardLocation[key] = position - 1 return ALLlocation, forwardLocation # 表里的工件统计 def statistics(self, model): n = model.rowCount() # 总行数 all_list = {} for i in range(n): # 统计入库记录里各种工件的数量 rec = model.record(i) if rec.value('种类') in all_list: all_list[rec.value('种类')] += 1 else: all_list[rec.value('种类')] = 1 return all_list def Absolute_statis(self, label): myModel = QSqlQueryModel(self) if label == 'current': myModel.setQuery("select 种类 from 工件信息表") elif label == 'enter': myModel.setQuery("select 种类 from 入库记录表") elif label == 'out': myModel.setQuery("select 种类 from 出库记录表") allnum = myModel.rowCount() all_dict = {} for i in range(allnum): rec = myModel.record(i) if rec.value('种类') in all_dict: all_dict[rec.value('种类')] += 1 else: all_dict[rec.value('种类')] = 1 return all_dict #寻找出入库记录的最大批次 def getMaxbatch(self, label): myModel = QSqlQueryModel(self) if label == 'enter': myModel.setQuery("select 批次 from 入库记录表") elif label == 'out': myModel.setQuery("select 批次 from 出库记录表") n = myModel.rowCount() if myModel.record(0).isNull('批次') == True: # 检测入库记录里的最大批次 max_batch = 0 else: max_batch = myModel.record(n - 1).value('批次') # 批次按顺序排列,查找最大批次 print('共有%d条记录,最大批次为%d' % (n, max_batch)) return max_batch #################################主操作界面#################################################### # 容量不足信息打印 def on_enterNum_spinBox_valueChanged(self): self.enter_Warning() def on_enterKind_comboBox_currentIndexChanged(self): self.enter_Warning() def enter_Warning(self): num = self.ui.enterNum_spinBox.value() enterKind = self.ui.enterKind_comboBox.currentText() if enterKind not in self.currentList: currentNum = 0 else: currentNum = self.currentList[enterKind] if num + currentNum > self.capacity[enterKind]: self.ui.enterList_pushButton.setEnabled(False) # self.ui.enterScan_pushButton.setEnabled(False) warningText = '⚠警告!' + enterKind + '已经超出库容上限!' else: self.ui.enterList_pushButton.setEnabled(True) # self.ui.enterScan_pushButton.setEnabled(True) warningText = '' self.ui.textEdit.setPlainText(warningText) #############入库函数 ##入库添加工件及对应数量 @pyqtSlot() def on_enterList_pushButton_clicked(self): enterKind = self.ui.enterKind_comboBox.currentText() enterNum = self.ui.enterNum_spinBox.value() if enterKind in self.enterList: self.enterList[enterKind] += enterNum else: self.enterList[enterKind] = enterNum # 激活出库按钮使能状态 self.ui.enterScan_pushButton.setEnabled(True) print(self.enterList) ##扫描入库 @pyqtSlot() def on_enterScan_pushButton_clicked(self): # 根据入库记录里的RFID生成新的RFID enteredList = self.statistics(self.enter_tabModel) # 获取入库记录里各种工件的数量 RFID_list = [] for key in self.enterList.keys(): # 对要入库的工件种类进行迭代 for i in range(self.enterList[key]): if key not in enteredList: no = i + 6 else: no = enteredList[key] + i + 6 RFID = self.name_RFID[key] + str(no).zfill(6) RFID_list.append(RFID) # 将RFID写入txt文件来模拟货物 filename = '卸货区' if not os.path.isdir(filename): os.makedirs(filename) with open('卸货区\待入库货工件.txt', 'w', encoding='utf8') as f: for s in RFID_list: f.write(s + '\n') # 从卸货区寻找模拟货物 dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找入库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: enterRFID = f.readlines() all_location, forwardLocation = self.getLocation(self.enterList, 'i') self.ui.widget.get_path(forwardLocation, 'i') # 绘制路径 self.enter(enterRFID, all_location) # 入库操作 def enter(self, enterRFID, all_location): #print('入库函数,enterRFID=',enterRFID,'all_location=',all_location) # 准备写入工件信息表和入库记录表 currentQuery = QSqlQuery(self.DB) enterQuery = QSqlQuery(self.DB) currentQuery.prepare('''INSERT INTO 工件信息表 (RFID,种类,货位,生产商, 批次,入库日期,入库时间,重量) VALUES(:RFID,:kind,:location,:manufacture,:batch, :enterDate,:enterTime,:weight)''') enterQuery.prepare('''INSERT INTO 入库记录表 (RFID,种类,生产商, 批次,入库日期,入库时间) VALUES(:RFID,:kind, :manufacture,:batch, :enterDate,:enterTime)''') # 准备数据(都是恒量) RFID_manufacture = { 'E0000001': '西安交大一厂', 'E0000002': '西安交大二厂', 'E0000003': '西安交大三厂' } max_batch = self.getMaxbatch('enter') enterDate = self.ui.dealdate.date().toString(Qt.ISODate) # 获取日期 enterTime = time.strftime('%H:%M:%S', time.localtime(time.time())) # 获取时分秒 weightList = { '直齿轮': 15, '斜齿轮': 20, '齿轮轴': 7, '光轴': 5, '曲柄': 2, '摇杆': 1 } check = True # 扫码并写入记录表 for i in range(len(enterRFID)): # 解码 s = enterRFID[i].rstrip('\n') kind = self.RFID_kind[s[0:18]] manufacture = RFID_manufacture[s[4:12]] weight = weightList[kind] # 加入现有库存 currentQuery.bindValue(":RFID", s) currentQuery.bindValue(":kind", kind) currentQuery.bindValue(":location", all_location[i]) currentQuery.bindValue(":manufacture", manufacture) currentQuery.bindValue(":batch", max_batch + 1) currentQuery.bindValue(":enterDate", enterDate) currentQuery.bindValue(":enterTime", enterTime) currentQuery.bindValue(":weight", weight) res = currentQuery.exec() # 执行SQL语句 check = check & res # 加入入库记录表 enterQuery.bindValue(":RFID", s) enterQuery.bindValue(":kind", kind) enterQuery.bindValue(":manufacture", manufacture) enterQuery.bindValue(":batch", max_batch + 1) enterQuery.bindValue(":enterDate", enterDate) enterQuery.bindValue(":enterTime", enterTime) enterQuery.exec() # 执行SQL语句 if check == False: QMessageBox.critical( self, "错误", "添加记录出现错误\n" + currentQuery.lastError().text()) print('入库异常') else: self.enterList = {} self.refresh() self.ui.enterScan_pushButton.setEnabled(False) self.ui.textEdit.setPlainText('入库操作成功') #############出库函数 # 库存不足信息打印 def on_outNum_spinBox_valueChanged(self): self.out_Warning() def on_outKind_comboBox_currentIndexChanged(self): self.out_Warning() def out_Warning(self): num = self.ui.outNum_spinBox.value() outKind = self.ui.outKind_comboBox.currentText() self.currentList = self.Absolute_statis('current') if outKind not in self.currentList: # 防止有些工件没有时作为字典的键出错 currentNum = 0 else: currentNum = self.currentList[outKind] if num > currentNum: self.ui.outList_pushButton.setEnabled(False) # self.ui.outScan_pushButton.setEnabled(False) warningText = '⚠警告!' + outKind + '已经超出库存上限!' else: self.ui.outList_pushButton.setEnabled(True) # self.ui.outScan_pushButton.setEnabled(True) warningText = '' self.ui.textEdit.setPlainText(warningText) # 添加出库工件 @pyqtSlot() def on_outList_pushButton_clicked(self): outKind = self.ui.outKind_comboBox.currentText() outNum = self.ui.outNum_spinBox.value() if outKind in self.outList: self.outList[outKind] += outNum else: self.outList[outKind] = outNum self.ui.outScan_pushButton.setEnabled(True) print("要出库的货物:", self.outList) @pyqtSlot() def on_outScan_pushButton_clicked(self): currentRow = self.current_tabModel.rowCount() # 库存的已记录数 RFID_list = [] outLocation, forwardLocation = self.getLocation(self.outList, 'o') # 从现有库存里取符合条件的RFID for i in range(currentRow): curRec = self.current_tabModel.record(i) location = curRec.value('货位') if location in outLocation: RFID_list.append(curRec.value('RFID')) # 将RFID写入模拟货物 filename = '发货区' if not os.path.isdir(filename): os.makedirs(filename) with open('发货区\待出库货工件.txt', 'w', encoding='utf8') as f: for s in RFID_list: f.write(s + '\n') # 从卸货区寻找模拟货物 dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找出库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: outRFID = f.readlines() for i in range(len(outRFID)): # 去除所有的换行符 outRFID[i] = outRFID[i].rstrip('\n') self.ui.widget.get_path(forwardLocation, 'o') #出库路径绘制 self.out(outRFID) def out(self, outRFID): # 出库函数,参数只有RFID列表 currentRow = self.current_tabModel.rowCount() # 库存的已记录数 currentQuery = QSqlQuery(self.DB) # 准备删除 outQuery = QSqlQuery(self.DB) # 准备插入 outQuery.prepare('''INSERT INTO 出库记录表 (RFID,种类,生产商, 批次,出库日期,出库时间) VALUES(:RFID,:kind, :manufacture,:batch, :outDate,:outTime)''') currentQuery.prepare('''DELETE FROM 工件信息表 WHERE RFID=:ID''') # 获取出库记录表里的最大批次 batch = self.getMaxbatch('out') outDate = self.ui.dealdate.date().toString(Qt.ISODate) outTime = time.strftime('%H:%M:%S', time.localtime(time.time())) check = True # 读取要出库的工件的RFID for i in range(currentRow): # 遍历现有库存找到要出库的RFID curRec = self.current_tabModel.record(i) if curRec.value('RFID') in outRFID: outQuery.bindValue(":RFID", curRec.value('RFID')) outQuery.bindValue(":kind", curRec.value('种类')) outQuery.bindValue(":manufacture", curRec.value('生产商')) outQuery.bindValue(":batch", batch + 1) outQuery.bindValue(":outDate", outDate) outQuery.bindValue(":outTime", outTime) outQuery.exec() # 执行SQL语句 currentQuery.bindValue(":ID", curRec.value('RFID')) res = currentQuery.exec() check = check & res if check == False: QMessageBox.critical( self, "错误", "出库记录出现错误\n" + currentQuery.lastError().text()) else: self.refresh() self.outList = {} self.ui.outScan_pushButton.setEnabled(False) self.ui.textEdit.setPlainText('出库操作成功') self.out_Warning() # 库存不足则打印信息,改变按键使能状态 # =================================副操作界面================================================== def getCurrentNum(self): currentQryModel = QSqlQueryModel(self) currentQryModel.setQuery("select RFID from 工件信息表") allnum = currentQryModel.rowCount() return allnum, currentQryModel ##单件扫描 @pyqtSlot() def on_emitPushButton_pressed(self): RFID = self.ui.RFIDlineEdit.text() forwardLocation = {} kind = self.RFID_kind[RFID[0:18]] kindlist = [] kindlist.append(RFID) allnum, currentQryModel = self.getCurrentNum() for i in range(allnum): cur = currentQryModel.record(i) if RFID == cur.value('RFID'): self.out(kindlist) self.get_locState() return # 因为已经指定了RFID,所以出库没有路径规划 location, position = self.allocation(kind, 1, 'i') self.enter(kindlist, location) forwardLocation[kind] = position - 1 self.ui.widget.get_path(forwardLocation, 'i') ##多件扫描 @pyqtSlot() def on_ScanPushButton_pressed(self): outRFID = [] inkindNum = {} dbFilename, flt = QFileDialog.getOpenFileName(self, "寻找入库货物", "", "模拟货物(*.txt)") if (dbFilename == ''): return with open(dbFilename, 'r', encoding='utf8') as f: RFID = f.readlines() for i in range(len(RFID)): # 去除所有的换行符 RFID[i] = RFID[i].rstrip('\n') allnum, currentQryModel = self.getCurrentNum() for i in range(allnum): cur = currentQryModel.record(i) if cur.value('RFID') in RFID: outRFID.append(cur.value('RFID')) # 如果现有库存里存在,则添加到出库RFID列表中 RFID.remove(cur.value('RFID')) # 移除 if outRFID != []: self.out(outRFID) self.get_locState() if RFID != []: for i in RFID: kind = self.RFID_kind[i[0:18]] if kind in inkindNum: inkindNum[kind] += 1 else: inkindNum[kind] = 1 inLocation, forwardLocation = self.getLocation(inkindNum, 'i') self.ui.widget.get_path(forwardLocation, 'i') #绘制路径 self.enter(RFID, inLocation) #入库操作 #################################现有库存界面#################################################### ##种类过滤 def on_currentComboBox_currentIndexChanged(self, curText): self.current_filter() ##批次过滤 def on_batchSpinBox_valueChanged(self, num): self.current_filter() ##是否选择批次 def on_batchCheckBox_stateChanged(self): self.current_filter() def current_filter(self): kind = self.ui.currentComboBox.currentText() batch = self.ui.batchSpinBox.value() if kind == '全选': if self.ui.batchCheckBox.isChecked() == True: self.current_tabModel.setFilter("批次='%d'" % (batch)) else: self.current_tabModel.setFilter("") else: if self.ui.batchCheckBox.isChecked() == True: self.current_tabModel.setFilter("批次='%d' and 种类='%s'" % (batch, kind)) else: self.current_tabModel.setFilter("种类='%s'" % (kind)) # 已用库存百分比显示 def inventory_used(self): # 通过进度条表示库存余量 currentQryModel = QSqlQueryModel(self) currentQryModel.setQuery("select RFID from 工件信息表") allnum = currentQryModel.rowCount() progress = int((100 * allnum / 156) + 0.5) self.ui.progressBar.setValue(progress) @pyqtSlot() def on_delete_pushButton_pressed(self): query = QSqlQuery(self.DB) print('开始删除') query.exec("DELETE FROM 工件信息表 WHERE 批次>=0") query.exec("DELETE FROM 出库记录表 WHERE 批次>=0") check = query.exec("DELETE FROM 入库记录表 WHERE 批次>=0") if check == False: QMessageBox.critical(self, "错误", "删除记录出现错误\n" + query.lastError().text()) else: self.refresh() #################################入库记录界面#################################################### ##种类过滤 def on_enterComboBox_currentIndexChanged(self): self.enter_filter() ##日期过滤 def on_enterDate_dateChanged(self): self.enter_filter() ##是否选择批次 def on_enterCheckBox_stateChanged(self): self.enter_filter() def enter_filter(self): kind = self.ui.enterComboBox.currentText() date = self.ui.enterDate.date().toString(Qt.ISODate) if self.ui.enterCheckBox.isChecked() == True: if kind == '全选': self.enter_tabModel.setFilter("入库日期='%s'" % (date)) else: self.enter_tabModel.setFilter("入库日期='%s' and 种类='%s'" % (date, kind)) else: if kind == '全选': self.enter_tabModel.setFilter("") else: self.enter_tabModel.setFilter("种类='%s'" % (kind)) # 入库记录可视化 @pyqtSlot() def on_drawEnter_pushButton_pressed(self): self.recordDemo(self.enter_tabModel) #################################出库记录界面#################################################### ##种类过滤 def on_outComboBox_currentIndexChanged(self): self.out_filter() ##日期过滤 def on_outDate_dateChanged(self): self.out_filter() ##是否选择批次 def on_outCheckBox_stateChanged(self): self.out_filter() def out_filter(self): kind = self.ui.outComboBox.currentText() date = self.ui.outDate.date().toString(Qt.ISODate) if self.ui.outCheckBox.isChecked() == True: if kind == '全选': self.out_tabModel.setFilter("出库日期='%s'" % (date)) else: self.out_tabModel.setFilter("出库日期='%s' and 种类='%s'" % (date, kind)) else: if kind == '全选': self.out_tabModel.setFilter("") else: self.out_tabModel.setFilter("种类='%s'" % (kind)) # 出库记录可视化 @pyqtSlot() def on_drawOut_pushButton_pressed(self): self.recordDemo(self.out_tabModel) #################################数据可视化################################################ # 现有库存可视化 @pyqtSlot() def on_drawCurrent_pushButton_pressed(self): # def demo(self): #self.currentList = self.statistics(self.current_tabModel) self.currentList = self.Absolute_statis('current') labels = [] values = [] for key, value in self.currentList.items(): labels.append(key) values.append(value) if self.ui.pie_radioButton.isChecked() == True: trace = [go.Pie(labels=labels, values=values)] layout = go.Layout(title='工件库存比例配比图', ) elif self.ui.bar_radioButton.isChecked() == True: trace = [go.Bar(x=labels, y=values)] layout = go.Layout(title='工件库存直方图', ) print(labels) print(values) fig = go.Figure(data=trace, layout=layout) file = 'F:\source\python代码\MES库存管理\VisualData' if not os.path.isdir(file): os.makedirs(file) filename = 'F:\source\python代码\MES库存管理\VisualData\\currentDemo.html' pyof.plot(fig, filename=filename, auto_open=False) win = DemoWindow(filename) win.exec() def recordDemo(self, model): record = {} enterRow = model.rowCount() # 库存的已记录数 dateName = model.record().fieldName(4) # 入库日期&出库日期 TimeName = model.record().fieldName(5) for i in range(enterRow): curRec = model.record(i) kind = curRec.value('种类') date = curRec.value(dateName) Time = curRec.value(TimeName) dateTime = date + ' ' + Time dateRecord = {} if kind not in record: # 工件种类第一次检索到,将其添加到reord中 dateRecord[dateTime] = 1 # 同时将对应的日期添加到record中 record[kind] = dateRecord else: if dateTime not in record[kind]: # 种类存在,但是第一次检索到某个日期时 record[kind][dateTime] = 1 else: record[kind][dateTime] += 1 traces = [] for key1 in record.keys(): x = [] y = [] for key2 in record[key1].keys(): x.append(key2) y.append(record[key1][key2]) trace = go.Scatter( x=x, y=y, # mode = 'markers', name=key1) traces.append(trace) layout = go.Layout(title=dateName[0:2] + '记录', ) fig = go.Figure(data=traces, layout=layout) file = 'F:\source\python代码\MES库存管理\VisualData' if not os.path.isdir(file): os.makedirs(file) filename = 'F:\source\python代码\MES库存管理\VisualData\\recordDemo.html' pyof.plot(fig, filename=filename, auto_open=False) win = DemoWindow(filename) win.exec()
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) # tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) ## self.ui.tableView.resizeColumnsToContents() ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.qryModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##查询数据 self.qryModel = QSqlQueryModel(self) self.qryModel.setQuery( '''SELECT empNo, Name, Gender, Birthday, Province, Department, Salary FROM employee ORDER BY empNo''' ) if self.qryModel.lastError().isValid(): QMessageBox.critical( self, "错误", "数据表查询错误,错误信息\n" + self.qryModel.lastError().text()) return self.ui.statusBar.showMessage("记录条数:%d" % self.qryModel.rowCount()) self.__getFieldNames() #获取字段名和序号 ##设置字段显示名,直接使用序号 self.qryModel.setHeaderData(0, Qt.Horizontal, "工号") self.qryModel.setHeaderData(1, Qt.Horizontal, "姓名") self.qryModel.setHeaderData(2, Qt.Horizontal, "性别") self.qryModel.setHeaderData(3, Qt.Horizontal, "出生日期") self.qryModel.setHeaderData(4, Qt.Horizontal, "省份") self.qryModel.setHeaderData(5, Qt.Horizontal, "部门") self.qryModel.setHeaderData(6, Qt.Horizontal, "工资") ## self.qryModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") ## self.qryModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") ## self.qryModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") ## self.qryModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") ## self.qryModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") ## self.qryModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") ## self.qryModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") ##创建界面组件与数据模型的字段之间的数据映射 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.qryModel) #设置数据模型 ## self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与qryModel的具体字段之间的联系 ## self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) ## self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) ## self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) ## self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) ## self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"] ) ## self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"] ) ## self.mapper.addMapping(self.ui.dbSpinSalary,self.fldNum["Salary"] ) self.mapper.addMapping(self.ui.dbSpinEmpNo, 0) self.mapper.addMapping(self.ui.dbEditName, 1) self.mapper.addMapping(self.ui.dbComboSex, 2) self.mapper.addMapping(self.ui.dbEditBirth, 3) self.mapper.addMapping(self.ui.dbComboProvince, 4) self.mapper.addMapping(self.ui.dbComboDep, 5) self.mapper.addMapping(self.ui.dbSpinSalary, 6) self.mapper.toFirst() #移动到首记录 self.selModel = QItemSelectionModel(self.qryModel) #关联选择模型 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) #选择行变化时 self.ui.tableView.setModel(self.qryModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 self.ui.actOpenDB.setEnabled(False) def __refreshTableView(self): ##刷新tableView显示 index = self.mapper.currentIndex() curIndex = self.qryModel.index(index, 1) #QModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开数据库”按钮 def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQL LITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##首记录 def on_actRecFirst_triggered(self): self.mapper.toFirst() self.__refreshTableView() @pyqtSlot() ##前一记录 def on_actRecPrevious_triggered(self): self.mapper.toPrevious() self.__refreshTableView() @pyqtSlot() ##后一条记录 def on_actRecNext_triggered(self): self.mapper.toNext() self.__refreshTableView() @pyqtSlot() ##最后一条记录 def on_actRecLast_triggered(self): self.mapper.toLast() self.__refreshTableView() ## =============自定义槽函数=============================== def do_currentRowChanged(self, current, previous): ##记录移动时触发 if (current.isValid() == False): self.ui.dbLabPhoto.clear() #清除图片显示 return self.mapper.setCurrentIndex(current.row()) #更新数据映射的行号 first = (current.row() == 0) #是否首记录 last = (current.row() == self.qryModel.rowCount() - 1) #是否尾记录 self.ui.actRecFirst.setEnabled(not first) #更新使能状态 self.ui.actRecPrevious.setEnabled(not first) self.ui.actRecNext.setEnabled(not last) self.ui.actRecLast.setEnabled(not last) curRec = self.qryModel.record(current.row()) #获取当前记录,QSqlRecord类型 empNo = curRec.value("EmpNo") #不需要加 toInt()函数 query = QSqlQuery(self.DB) query.prepare( '''SELECT EmpNo, Memo, Photo FROM employee WHERE EmpNo = :ID''') query.bindValue(":ID", empNo) ## if not query.exec_(): #注意,在PyQt5.11.2之前的版本里只能使用exec_()函数 if not query.exec( ): #注意,在PyQt5.11.2添加了遗漏的overload型exec()函数,在PyQt5.11.2里没问题了 QMessageBox.critical(self, "错误", "执行SQL语句错误\n" + query.lastError().text()) return else: query.first() picData = query.value("Photo") if (picData == None): #图片字段内容为空 self.ui.dbLabPhoto.clear() else: #显示照片 pic = QPixmap() pic.loadFromData(picData) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) memoData = query.value("Memo") #显示备注 self.ui.dbEditMemo.setPlainText(memoData)
class MyMainWindow(VCPMainWindow): """Main window class for the VCP.""" def __init__(self, *args, **kwargs): super(MyMainWindow, self).__init__(*args, **kwargs) self.threadFormFwdBtn.clicked.connect(self.threadFormFwd) self.threadFormBackBtn.clicked.connect(self.threadFormBack) self.threadClassFwdBtn.clicked.connect(self.threadClassFwd) self.threadClassBackBtn.clicked.connect(self.threadClassBack) self.threadSizeFwdBtn.clicked.connect(self.threadSizeFwd) self.threadSizeBackBtn.clicked.connect(self.threadSizeBack) self.sptmSizeFwdBtn.clicked.connect(self.sptmSizeFwd) self.sptmSizeBackBtn.clicked.connect(self.sptmSizeBack) self.drillSizeFwdBtn.clicked.connect(self.drillSizeFwd) self.drillSizeBackBtn.clicked.connect(self.drillSizeBack) self.numPassesSP.valueChanged.connect(self.numPassesCalc) self.holeDiaSb.valueChanged.connect(self.holeDiaCalc) self.linearFeedSb.valueChanged.connect(self.linearFeedCalc) self.genGcodeBtn.clicked.connect(self.genGcode) self.threadCountSb.valueChanged.connect(self.threadHeightCalc) self.testFwdBtn.clicked.connect(self.testFwd) self.testBackBtn.clicked.connect(self.testBack) self.saveFilePb.clicked.connect(self.saveFile) if not self.open_db(): print('Failed to Open Database') self.sptmSizeInit() self.threadFormInit() self.numPassesCalc() def open_db(self): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName(current_path + 'threads.db') db.open() return db def threadFormInit(self): self.formMapper = QDataWidgetMapper(self) self.formModel = QSqlQueryModel(self) self.formModel.setQuery('SELECT DISTINCT form FROM internal_threads') self.formMapper.setModel(self.formModel) self.formMapper.addMapping(self.threadFormLbl, 0, b'text') self.formMapper.toLast() self.formsLast = self.formMapper.currentIndex() self.formMapper.toFirst() self.threadClassInit() def threadFormFwd(self): if self.formMapper.currentIndex() != self.formsLast: self.formMapper.toNext() else: self.formMapper.toFirst() self.threadClassInit() def threadFormBack(self): if self.formMapper.currentIndex() != 0: self.formMapper.toPrevious() else: self.formMapper.toLast() self.threadClassInit() def threadClassInit(self): self.classMapper = QDataWidgetMapper(self) self.classModel = QSqlQueryModel(self) form = self.threadFormLbl.text() classSelect = "SELECT DISTINCT class FROM internal_threads \ WHERE form = '{}'".format(form) self.classModel.setQuery(classSelect) self.classMapper.setModel(self.classModel) self.classMapper.addMapping(self.threadClassLbl, 0, b'text') self.classMapper.toLast() self.classLast = self.classMapper.currentIndex() self.classMapper.toFirst() self.threadSizeInit() def threadClassFwd(self): if self.classMapper.currentIndex() != self.classLast: self.classMapper.toNext() else: self.classMapper.toFirst() self.threadSizeInit(self.sizeMapper.currentIndex()) def threadClassBack(self): if self.classMapper.currentIndex() != 0: self.classMapper.toPrevious() else: self.classMapper.toLast() self.threadSizeInit(self.sizeMapper.currentIndex()) def threadSizeInit(self, index = 0): self.sizeMapper = QDataWidgetMapper(self) self.sizeModel = QSqlQueryModel(self) form = self.threadFormLbl.text() threadClass = self.threadClassLbl.text() sizeSelect = "SELECT size, pitch, major_dia, \ min_major_dia, max_minor_dia, min_minor_dia, \ max_pitch_dia, min_pitch_dia FROM internal_threads WHERE form \ = '{}' AND class = '{}'".format(form, threadClass) self.sizeModel.setQuery(sizeSelect) self.sizeMapper.setModel(self.sizeModel) self.sizeMapper.addMapping(self.threadSizeLbl, 0, b'text') self.sizeMapper.addMapping(self.threadTPILbl, 1, b'text') self.sizeMapper.addMapping(self.threadMajorDiaLbl, 2, b'text') self.sizeMapper.addMapping(self.minMajorDiaLbl, 3, b'text') self.sizeMapper.addMapping(self.maxMinorDiaLbl, 4, b'text') self.sizeMapper.addMapping(self.minMinorDiaLbl, 5, b'text') self.sizeMapper.addMapping(self.maxPitchDiaLbl, 6, b'text') self.sizeMapper.addMapping(self.minPitchDiaLbl, 7, b'text') self.sizeMapper.toLast() self.sizeLast = self.sizeMapper.currentIndex() self.sizeMapper.setCurrentIndex(index) self.drillSizeInit() self.threadSizeCalc() self.numPassesCalc() self.threadHeightCalc() def threadSizeFwd(self): if self.sizeMapper.currentIndex() != self.sizeLast: self.sizeMapper.toNext() else: self.sizeMapper.toFirst() self.drillSizeInit() self.threadSizeCalc() self.numPassesCalc() self.threadHeightCalc() def threadSizeBack(self): if self.sizeMapper.currentIndex() != 0: self.sizeMapper.toPrevious() else: self.sizeMapper.toLast() self.drillSizeInit() self.threadSizeCalc() self.numPassesCalc() self.threadHeightCalc() def threadSizeCalc(self): # PDO calculations threadMajorDia = float(self.threadMajorDiaLbl.text()) drillDia = float(self.holeDiaSb.value()) standardPDO = threadMajorDia - drillDia self.sptmThreadingPDOLbl.setText('{:.04f}'.format(standardPDO)) # Actual thread height = 1/2 PDO threadHeightStandard = standardPDO / 2 self.threadHeightStdLbl.setText('{:.04f}'.format(threadHeightStandard)) threadTrangleHeight = threadHeightStandard / 0.625 self.threadTriangleHeightLbl.setText('{:.04f}'.format(threadTrangleHeight)) threadPushOutAdj = threadTrangleHeight * 0.125 self.threadPushOutAdjLbl.setText('{:.04f}'.format(threadPushOutAdj)) threadPDOAdjustOut = threadPushOutAdj * 2 self.threadPDOAdjustOutLbl.setText('{:.04f}'.format(threadPDOAdjustOut)) # -2*(Crest*(SQRT(3)/2)) sptmCrest = float(self.sptmCrestLbl.text()) threadPDOCrestAdj = -2 * (sptmCrest * (sqrt(3)/2)) self.threadPDOCrestAdjLbl.setText('{:.04f}'.format(threadPDOCrestAdj)) finalPDO = standardPDO + threadPDOAdjustOut + threadPDOCrestAdj self.threadFinalPDOLbl.setText('{:.04f}'.format(finalPDO)) # if final PDO is greater than tip height check if self.sptmTipHeight > finalPDO: self.sptmTipOkLbl.setText('OK') else: self.sptmTipOkLbl.setText('Tip Too Small') # set maximum number of threads threadTPI = float(self.threadTPILbl.text()) threadPitch = 1.0 / threadTPI maxDepth = float(self.sptmMaxDepthLbl.text()) maxThreads = int(maxDepth / threadPitch) def sptmSizeInit(self): self.sptmMapper = QDataWidgetMapper(self) self.sptmModel = QSqlQueryModel(self) self.sptmModel.setQuery('SELECT * FROM sptm') self.sptmMapper.setModel(self.sptmModel) self.sptmMapper.addMapping(self.sptmSizeLbl, 0, b'text') self.sptmMapper.addMapping(self.sptmDiaLbl, 1, b'text') self.sptmMapper.addMapping(self.sptmCrestLbl, 2, b'text') self.sptmMapper.addMapping(self.sptmMaxDepthLbl, 3, b'text') self.sptmMapper.addMapping(self.sptmFlutesLbl, 4, b'text') self.sptmMapper.addMapping(self.sptmNeckDiaLbl, 5, b'text') self.sptmMapper.toLast() self.sptmLast = self.sptmMapper.currentIndex() self.sptmMapper.toFirst() def sptmSizeFwd(self): if self.sptmMapper.currentIndex() != self.sptmLast: self.sptmMapper.toNext() else: self.sptmMapper.toFirst() self.sptmCalc() def sptmSizeBack(self): if self.sptmMapper.currentIndex() != 0: self.sptmMapper.toPrevious() else: self.sptmMapper.toLast() self.sptmCalc() def sptmCalc(self): drillDiameter = float(self.holeDiaSb.value()) sptmCutterDia = float(self.sptmDiaLbl.text()) if sptmCutterDia < drillDiameter: self.sptmDiaOkLbl.setText('Ok') else: self.sptmDiaOkLbl.setText('TOO BIG!') sptmNeckDia = float(self.sptmNeckDiaLbl.text()) self.sptmTipHeight = sptmCutterDia - sptmNeckDia self.sptmTipHeightLbl.setText('{:.4f}'.format(self.sptmTipHeight)) def drillSizeInit(self): self.drillMapper = QDataWidgetMapper(self) self.drillQueryModel = QSqlQueryModel(self) minMinorDia = str(self.minMinorDiaLbl.text()) maxMinorDia = str(self.maxMinorDiaLbl.text()) drillSelect = "SELECT * FROM drills WHERE dia >= '{}' \ AND dia <= '{}'".format(minMinorDia, maxMinorDia) self.drillQueryModel.setQuery(drillSelect) self.drillMapper.setModel(self.drillQueryModel) self.drillMapper.addMapping(self.drillTypeLbl, 0, b'text') self.drillMapper.addMapping(self.drillSizeLbl, 1, b'text') self.drillMapper.addMapping(self.drillDiaLbl, 2, b'text') self.drillMapper.toLast() self.drillLast = self.drillMapper.currentIndex() self.drillMapper.toFirst() # setup hole dia spinbox self.holeDiaSb.setMinimum(float(self.minMinorDiaLbl.text())) self.holeDiaSb.setMaximum(float(self.maxMinorDiaLbl.text())) # setup pitch dia spinbox self.pitchDiaSb.setMinimum(float(self.minPitchDiaLbl.text())) self.pitchDiaSb.setMaximum(float(self.maxPitchDiaLbl.text())) self.sptmCalc() self.threadPercent() def drillSizeFwd(self): if self.drillMapper.currentIndex() != self.drillLast: self.drillMapper.toNext() else: self.drillMapper.toFirst() self.sptmCalc() self.threadPercent() self.threadSizeCalc() def drillSizeBack(self): if self.drillMapper.currentIndex() != 0: self.drillMapper.toPrevious() else: self.drillMapper.toLast() self.sptmCalc() self.threadPercent() self.threadSizeCalc() def threadPercent(self): majorDia = float(self.threadMajorDiaLbl.text()) minorDia = float(self.drillDiaLbl.text()) # note for metric convert to TPI threadPitch = float(self.threadTPILbl.text()) threadEngagement = ((majorDia - minorDia) * threadPitch) / 0.01299 self.threadPercentLbl.setText('{:.0f}%'.format(threadEngagement)) def threadHeightCalc(self): pitch = 1.0 / float(self.threadTPILbl.text()) height = self.threadCountSb.value() * pitch self.threadsHeight.setText('{:.4f}"'.format(height)) def numPassesCalc(self): majorDia = float(self.threadMajorDiaLbl.text()) minorDia = float(self.holeDiaSb.value()) threadDepth = (majorDia - minorDia) if self.numPassesSP.value() == 1: self.passDiaLbl_0.setText('{:.4f}'.format(majorDia)) self.passDiaLbl_1.setText('') self.passDiaLbl_2.setText('') self.passDiaLbl_3.setText('') self.passPercentLbl_1.setText('100%') self.passPercentLbl_2.setText('') self.passPercentLbl_3.setText('') self.passPercentLbl_4.setText('') if self.numPassesSP.value() == 2: self.passDiaLbl_0.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.65))) self.passDiaLbl_1.setText('{:.4f}'.format(majorDia)) self.passDiaLbl_2.setText('') self.passDiaLbl_3.setText('') self.passPercentLbl_1.setText('65%') self.passPercentLbl_2.setText('35%') self.passPercentLbl_3.setText('') self.passPercentLbl_4.setText('') if self.numPassesSP.value() == 3: self.passDiaLbl_0.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.50))) self.passDiaLbl_1.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.80))) self.passDiaLbl_2.setText('{:.4f}'.format(majorDia)) self.passDiaLbl_3.setText('') self.passPercentLbl_1.setText('50%') self.passPercentLbl_2.setText('30%') self.passPercentLbl_3.setText('20%') self.passPercentLbl_4.setText('') if self.numPassesSP.value() == 4: self.passDiaLbl_0.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.40))) self.passDiaLbl_1.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.67))) self.passDiaLbl_2.setText('{:.4f}'.format(minorDia \ + (threadDepth * 0.87))) self.passDiaLbl_3.setText('{:.4f}'.format(majorDia)) self.passPercentLbl_1.setText('40%') self.passPercentLbl_2.setText('27%') self.passPercentLbl_3.setText('20%') self.passPercentLbl_4.setText('13%') def holeDiaCalc(self): majorDia = float(self.threadMajorDiaLbl.text()) minorDia = self.holeDiaSb.value() # note for metric convert to TPI threadPitch = float(self.threadTPILbl.text()) threadEngagement = ((majorDia - minorDia) * threadPitch) / 0.01299 self.threadPerLbl.setText('{:.1f}%'.format(threadEngagement)) def genGcode(self): # make sure your using the hole size as the starting point self.gcodeText.setPlainText("; JT's Thread Mill Wizard") self.gcodeText.append('; Thread {}'.format(self.threadSizeLbl.text())) self.gcodeText.append('F25') xCoord = self.xCoord.text() yCoord = self.yCoord.text() self.gcodeText.append('G0 X{} Y{} Z0.125'.format(xCoord, yCoord)) zStart = self.zStart.text() pitch = 1.0 / float(self.threadTPILbl.text()) threadsHeight = (self.threadCountSb.value() + 1) * pitch zEnd = float(zStart) + threadsHeight self.gcodeText.append('G1 Z{}'.format(zEnd)) self.gcodeText.append('G91') self.gcodeText.append('; Number of thread passes {}' \ .format(self.numPassesSP.value())) for i in range(self.numPassesSP.value()): passDiameter = float(getattr(self, 'passDiaLbl_' + str(i)).text()) # go to hole bottom threadTPI = float(self.threadTPILbl.text()) threadPitch = 1.0 / threadTPI self.gcodeText.append('G0 Z-{}'.format(threadsHeight)) # go to start of lead in arc cutterClearance = 0.020 minorDia = float(self.holeDiaSb.value()) toolDia = float(self.sptmDiaLbl.text()) yStartLeadIn = -((minorDia - (cutterClearance * 2)) - toolDia) / 2 self.gcodeText.append('G1 Y{:.4f}'.format(yStartLeadIn)) # lead in arc sptmCutterDia = float(self.sptmDiaLbl.text()) leadInYEnd = ((passDiameter - sptmCutterDia) / 2) + abs(yStartLeadIn) leadInZEnd = threadPitch / 2 leadInJOffset = leadInYEnd / 2 self.gcodeText.append('G3 X0.0 Y{:.4f} Z{:.4f} J{:.4f}' \ .format(leadInYEnd, leadInZEnd, leadInJOffset)) # spiral up threadCount = int(self.threadCountSb.text()) finalZ = threadPitch * threadCount jOffset = -(passDiameter - sptmCutterDia) / 2 self.gcodeText.append('G3 J{} Z{} P{}'. \ format(jOffset, finalZ, threadCount)) # lead out arc leadOutYEnd = -((passDiameter - sptmCutterDia) / 2) - abs(yStartLeadIn) leadOutZEnd = threadPitch / 2 leadOutJOffset = -(leadInYEnd / 2) self.gcodeText.append('G3 X0.0 Y{:.4f} Z{:.4f} J{:.4f}' \ .format(leadOutYEnd, leadOutZEnd, leadOutJOffset)) # go to center yCenter = ((minorDia - (cutterClearance * 2)) - toolDia) / 2 self.gcodeText.append('G1 Y{:.4f}'.format(yCenter)) self.gcodeText.append('G90') #self.gcodeText.append('G0 X2 Y2') self.gcodeText.append('M2') def linearFeedCalc(self): # Internal Threads # Linear feed * ((Major thread dia - Tool dia) / Major thread dia) minorDia = float(self.holeDiaSb.value()) majorDia = float(self.threadMajorDiaLbl.text()) cutterDia = float(self.sptmDiaLbl.text()) linearFeed = self.linearFeedSb.value() circularFeed = ((majorDia - cutterDia) / majorDia) * linearFeed self.circularFeedLbl.setText('{:.1f}'.format(circularFeed)) def testFwd(self): self.gcodeText.append('line du') def testBack(self): pass def saveFile(self): ngcFile = os.path.join(os.getenv("HOME"), 'linuxcnc/nc_files', 'jt.ngc') with open(ngcFile, 'w') as f: f.write(str(self.gcodeText.toPlainText())) """
class ManagerMainui(QMainWindow, Ui_MainWindow): def __init__(self): super(ManagerMainui, self).__init__() self.setupUi(self) self.opendb() self.show() self.lianjie() self.setCentralWidget(self.splitter) self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems) self.table_view.setSelectionMode( QAbstractItemView.SingleSelection) # 一次选择单行还是多行 self.table_view.setAlternatingRowColors(True) # 隔行自动改变颜色 self.table_view.verticalHeader().setDefaultSectionSize(22) self.table_view.horizontalHeader().setDefaultSectionSize(60) # 为菜单添加动作 self.filemenu.addAction(self.exit_Act) self.Updata_Menu.addAction(self.Updata_Act) # 当前页 self.currentPage = 0 # 总页数 self.totalPage = 0 # 总记录数 self.totalRecord = 0 # 每页数据数 self.pageRecord = 10 def opendb(self): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('F:/pycharm项目/图书管理系统NEW/AllDataBase/book.db') if db.open(): self._biaocaozuo() else: QMessageBox.information(self, 'warning', '无法建立与数据库的连接') return False def _biaocaozuo(self): self.model = QtSql.QSqlTableModel() self.model.setTable('BookData') self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) # 设置保存策略 self.table_view.setModel(self.model) self.model.select() self.model.setHeaderData(0, Qt.Horizontal, 'ISBN') self.model.setHeaderData(1, Qt.Horizontal, '书名') self.model.setHeaderData(2, Qt.Horizontal, '作者') self.model.setHeaderData(3, Qt.Horizontal, '出版社') self.model.setHeaderData(4, Qt.Horizontal, '出版日期') self.model.setHeaderData(5, Qt.Horizontal, '评分') self.model.setHeaderData(6, Qt.Horizontal, '照片') self.table_view.setColumnHidden(6, True) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.lineEdit_ISBN, 0) self.mapper.addMapping(self.lineEdit_shuming, 1) self.mapper.addMapping(self.lineEdit_zuozhe, 2) self.mapper.addMapping(self.lineEdit_chubanshe, 3) self.mapper.addMapping(self.lineEdit_chubanriqi, 4) self.mapper.addMapping(self.lineEdit_pingfen, 5) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.model) # 选择模型 self.table_view.setSelectionModel(self.selModel) self.selModel.currentChanged.connect( self.do_currentChanged) # 当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # 选择行变化时 # @pyqtSlot() ##保存修改 def on_p_baocun_triggered(self): result = QMessageBox.question(self, 'warning', '你要保存你的修改吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: res = self.model.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.model.lastError().text()) else: QMessageBox.information(self, "message", "保存成功\n") self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: QMessageBox.information(self, 'message', 'Thanks') # @pyqtSlot() ##取消修改 def on_p_quxiao_triggered(self): # self.model.revertAll() result = QMessageBox.question(self, 'warning', '确认取消之前所有的的操作吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.revertAll() self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) # @pyqtSlot() ##添加记录 def on_p_zengjia_triggered(self): #self.model.insertRows(self.model.rowCount(), 1) self.model.insertRow(self.model.rowCount(), QModelIndex()) # 在末尾添加一个记录 curIndex = self.model.index(self.model.rowCount() - 1, 1) # 创建最后一行的ModelIndex self.selModel.clearSelection() # 清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) # 设置刚插入的行为当前选择行 # 删除记录 def on_p_sanchu_triggered(self): # if self.p_baocun.isEnabled(): result = QMessageBox.question(self, 'warning', '确认删除该记录吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.removeRow(self.table_view.currentIndex().row()) self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) else: self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) # 插入记录 def on_p_charu_triggered(self): curIndex = self.table_view.currentIndex() # QModelIndex self.model.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # 清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) # 对当前行设置图片 def on_p_photo_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) # fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() # QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.model.record(curRecNo) # 获取当前记录QSqlRecord curRec.setValue("Photo", data) # 设置字段数据 self.model.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) # 在界面上显示 def closeEvent(self, event): """ 提示保存 """ if self.p_baocun.isEnabled(): r = QMessageBox.warning(self, "注意", "你还没有保存,现在保存下?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if r == QMessageBox.No: event.accept() else: event.ignore() def chaxun_ISBN(self): text1 = self.lineEdit_chaxun.text() self.model.setFilter(("ISBN='%s'" % (text1))) def chaxun_zuozhe(self): text2 = self.lineEdit_chaxun.text() self.model.setFilter(("author='%s'" % (text2))) def chaxun_shumin(self): text3 = self.lineEdit_chaxun.text() self.model.setFilter(("title='%s'" % (text3))) def chaxun_(self): """ 查找图书 """ searchtext = self.lineEdit_chaxun.text() if searchtext: if self.comboBox_chaxun.currentText() == "ISBN": self.chaxun_ISBN() elif self.comboBox_chaxun.currentText() == "书名": self.chaxun_shumin() elif self.comboBox_chaxun.currentText() == "作者": self.chaxun_zuozhe() else: QMessageBox.information(self, "提示", "请输入搜索关键词!") def on_p_quxiaoCX_triggered(self): self.lineEdit_chaxun.clear() self.model.setFilter("") def do_currentChanged(self, current, previous): # 更新actPost和actCancel 的状态 self.p_baocun.setEnabled(self.model.isDirty()) # 有未保存修改时可用 self.p_quxiao.setEnabled(self.model.isDirty()) def do_currentRowChanged(self, current, previous): # 行切换时的状态控制 self.mapper.setCurrentIndex(current.row()) if (current.isValid() == False): self.dbLabPhoto.clear() # 清除图片显示 return self.mapper.setCurrentIndex(current.row()) # 更新数据映射的行号 curRec = self.model.record(current.row()) # 获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): # 图片字段内容为空 self.dbLabPhoto.clear() else: # data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.size().width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) def scrapy(self): """启动爬虫,在线更新功能""" # cmdline.execute(" scrapy ".split()) self.hide() try: run = ScrapyRun() finally: pass def lianjie(self): self.p_zengjia.clicked.connect(self.on_p_zengjia_triggered) self.p_baocun.clicked.connect(self.on_p_baocun_triggered) self.p_quxiao.clicked.connect(self.on_p_quxiao_triggered) self.p_sanchu.clicked.connect(self.on_p_sanchu_triggered) self.p_charu.clicked.connect(self.on_p_charu_triggered) self.p_photo.clicked.connect(self.on_p_photo_triggered) self.p_chaxun.clicked.connect(self.chaxun_) self.p_quxiaoCX.clicked.connect(self.on_p_quxiaoCX_triggered) self.p_tuichu.clicked.connect(self.close) self.exit_Act.triggered.connect(self.close) self.Updata_Act.triggered.connect(self.scrapy)
class ArmorEditor(ArmorEditorWidgetBase, ArmorEditorWidget): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self.model = None self.parts_tree_model = ArmorSetTreeModel() self.skill_model = SkillTranslationModel() self.armor_item_mapper = QDataWidgetMapper(self) self.armor_item_mapper.setItemDelegate(ItemDelegate()) self.armor_item_mapper.setModel(self.parts_tree_model) self.parts_tree_view.setModel(self.parts_tree_model) self.parts_tree_view.activated.connect( self.handle_parts_tree_activated) self.import_export_manager = ImportExportManager(self.parts_tree_view) self.import_export_manager.connect_custom_context_menu() for it in ("set_skill1_value", "set_skill2_value", "skill1_value", "skill2_value", "skill3_value"): getattr(self, it).setModel(self.skill_model) mappings = [ (self.id_value, Column.id, b"text"), (self.name_value, Column.gmd_name_index, b"text"), (self.description_value, Column.gmd_desc_index, b"text"), (self.setid_value, Column.set_id), (self.set_group_value, Column.set_group), (self.type_value, Column.type, b"currentIndex"), (self.order_value, Column.order), (self.variant_value, Column.variant, b"currentIndex"), (self.equip_slot_value, Column.equip_slot, b"currentIndex"), (self.gender_value, Column.gender, b"currentIndex"), (self.mdl_main_id_value, Column.mdl_main_id), (self.mdl_secondary_id_value, Column.mdl_secondary_id), (self.icon_color_value, Column.icon_color), (self.defense_value, Column.defense), (self.rarity_value, Column.rarity), (self.cost_value, Column.cost), (self.fire_res_value, Column.fire_res), (self.water_res_value, Column.water_res), (self.thunder_res_value, Column.thunder_res), (self.ice_res_value, Column.ice_res), (self.dragon_res_value, Column.dragon_res), (self.set_skill1_value, Column.set_skill1), (self.set_skill1_lvl_value, Column.set_skill1_lvl), (self.set_skill2_value, Column.set_skill2), (self.set_skill2_lvl_value, Column.set_skill2_lvl), (self.skill1_value, Column.skill1), (self.skill1_lvl_value, Column.skill1_lvl), (self.skill2_value, Column.skill2), (self.skill2_lvl_value, Column.skill2_lvl), (self.skill3_value, Column.skill3), (self.skill3_lvl_value, Column.skill3_lvl), (self.num_gem_slots, Column.num_gem_slots), (self.gem_slot1_lvl_value, Column.gem_slot1_lvl), (self.gem_slot2_lvl_value, Column.gem_slot2_lvl), (self.gem_slot3_lvl_value, Column.gem_slot3_lvl), ] for mapping in mappings: self.armor_item_mapper.addMapping(*mapping) def handle_parts_tree_activated(self, qindex: QModelIndex): if isinstance(qindex.internalPointer(), ArmorSetNode): return self.armor_item_mapper.setRootIndex(qindex.parent()) self.armor_item_mapper.setCurrentModelIndex(qindex) entry = qindex.internalPointer().ref self.crafting_requirements_editor.set_current(entry.id) def set_model(self, model): self.model = model if self.model is None: self.parts_tree_model = None self.parts_tree_view.setModel(None) return self.skill_model.update(model.get_relation_data("t9n_skill_pt")) self.crafting_requirements_editor.set_model(model, None) self.parts_tree_model.update(model) self.configure_tree_view() def configure_tree_view(self): header = self.parts_tree_view.header() header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setStretchLastSection(False) for i in range(2, self.parts_tree_model.columnCount(None)): header.hideSection(i)
spin_box.setMinimum(-10000) spin_box.setMaximum(10000) line_edit = QLineEdit() vbox = QVBoxLayout() vbox.addWidget(table_view) vbox.addWidget(spin_box) vbox.addWidget(line_edit) window.setLayout(vbox) ### mapper = QDataWidgetMapper() # <-- mapper.setModel(my_model) # <-- mapper.addMapping(spin_box, 0) # <-- mapper.addMapping(line_edit, 1) # <-- mapper.toFirst() # <-- def update_selection(selected, deselected): # <-- index = table_view.selectionModel().currentIndex() # <-- mapper.setCurrentIndex(index.row()) # <-- print("Index: ", index.row()) table_view.selectionModel().selectionChanged.connect(update_selection) # <-- ### window.show()
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 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 MainWindow(QMainWindow): def __init__(self, data): super().__init__() self.resize(400, 600) self.setWindowTitle('Logger Skeleton') self.statusBar().showMessage("Ready", 2000) # Make widgets #################################### self.tabs = QTabWidget(self) self.setCentralWidget(self.tabs) # Add tabs self.table_tab = QWidget(self) self.stats_tab = QWidget(self) self.tabs.addTab(self.table_tab, "Table") self.tabs.addTab(self.stats_tab, "Stats") # Table tab ########################################################### self.table_view = QTableView(self.table_tab) self.text_edit = QPlainTextEdit() self.btn_add_row = QPushButton("Add a row") #self.btn_remove_row = QPushButton("Remove selected rows") table_tab_vbox = QVBoxLayout() table_tab_vbox.addWidget(self.table_view) table_tab_vbox.addWidget(self.text_edit) table_tab_vbox.addWidget(self.btn_add_row) #table_tab_vbox.addWidget(self.btn_remove_row) self.table_tab.setLayout(table_tab_vbox) # Set model ####################################### my_model = DataQtModel(data, parent=self) # TODO: right use of "parent" ? # Proxy model ##################################### proxy_model = QSortFilterProxyModel(parent=self) # TODO: right use of "parent" ? proxy_model.setSourceModel(my_model) self.table_view.setModel(proxy_model) #self.table_view.setModel(my_model) # Set the view #################################### self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) # Select the full row when a cell is selected (See http://doc.qt.io/qt-5/qabstractitemview.html#selectionBehavior-prop ) #self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) # Set selection mode. See http://doc.qt.io/qt-5/qabstractitemview.html#selectionMode-prop self.table_view.setAlternatingRowColors(True) self.table_view.setSortingEnabled(True) self.table_view.setColumnWidth(0, 200) # TODO: automatically get the best width self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # https://stackoverflow.com/q/17535563 self.table_view.setColumnHidden(COMMENT_COLUMN_INDEX, True) delegate = Delegate() self.table_view.setItemDelegate(delegate) # Set key shortcut ################################ # see https://stackoverflow.com/a/17631703 and http://doc.qt.io/qt-5/qaction.html#details # Add row action add_action = QAction(self.table_view) add_action.setShortcut(Qt.CTRL | Qt.Key_N) add_action.triggered.connect(self.add_row_btn_callback) self.table_view.addAction(add_action) # Delete action del_action = QAction(self.table_view) del_action.setShortcut(Qt.Key_Delete) del_action.triggered.connect(self.remove_row_callback) self.table_view.addAction(del_action) # Set QDataWidgetMapper ########################### self.mapper = QDataWidgetMapper() self.mapper.setModel(proxy_model) # WARNING: do not use `my_model` here otherwise the index mapping will be wrong! self.mapper.addMapping(self.text_edit, COMMENT_COLUMN_INDEX) self.mapper.toFirst() # TODO: is it a good idea ? self.table_view.selectionModel().selectionChanged.connect(self.update_selection) # Set slots ####################################### self.btn_add_row.clicked.connect(self.add_row_btn_callback) #self.btn_remove_row.clicked.connect(self.remove_row_callback) #self.table_view.setColumnHidden(1, True) # Stats tab ########################################################### # See https://matplotlib.org/examples/user_interfaces/embedding_in_qt5.html stats_tab_layout = QVBoxLayout(self.stats_tab) self.plot_canvas = PlotCanvas(data, self.stats_tab, width=5, height=4, dpi=100) stats_tab_layout.addWidget(self.plot_canvas) ################################################### #proxy_model.dataChanged.connect(plot_canvas.update_figure) #proxy_model.rowsInserted.connect(plot_canvas.update_figure) # TODO #proxy_model.rowsRemoved.connect(plot_canvas.update_figure) # TODO self.tabs.currentChanged.connect(self.updatePlot) # Update the stats plot when the tabs switch to the stats tab # Show ############################################ self.show() def update_selection(self, selected, deselected): index = self.table_view.selectionModel().currentIndex() self.mapper.setCurrentIndex(index.row()) print("Index: ", index.row()) def updatePlot(self, index): """ Parameters ---------- index Returns ------- """ if index == self.tabs.indexOf(self.stats_tab): self.plot_canvas.update_figure() def add_row_btn_callback(self): parent = QModelIndex() # More useful with e.g. tree structures #row_index = 0 # Insert new rows to the begining row_index = self.table_view.model().rowCount(parent) # Insert new rows to the end self.table_view.model().insertRows(row_index, 1, parent) def remove_row_callback(self): parent = QModelIndex() # More useful with e.g. tree structures # See http://doc.qt.io/qt-5/model-view-programming.html#handling-selections-in-item-views #current_index = self.table_view.selectionModel().currentIndex() #print("Current index:", current_index.row(), current_index.column()) selection_index_list = self.table_view.selectionModel().selectedRows() selected_row_list = [selection_index.row() for selection_index in selection_index_list] print("Current selection:", selected_row_list) #row_index = 0 # Remove the first row #row_index = self.table_view.model().rowCount(parent) - 1 # Remove the last row # WARNING: the list of rows to remove MUST be sorted in reverse order # otherwise the index of rows to remove may change at each iteration of the for loop! # TODO: there should be a lock mechanism to avoid model modifications from external sources while iterating this loop... # Or as a much simpler alternative, modify the ItemSelectionMode to forbid the non contiguous selection of rows and remove the following for loop for row_index in sorted(selected_row_list, reverse=True): # Remove rows one by one to allow the removql of non-contiguously selected rows (e.g. "rows 0, 2 and 3") success = self.table_view.model().removeRows(row_index, 1, parent) if not success: raise Exception("Unknown error...") # TODO
class WpDatGEditor(WeaponGunEditorWidgetBase, WeaponGunEditorWidget): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self.model = None self.item_model = WpDatGTableModel(self) self.weapon_tree_view.setModel(self.item_model) self.shell_table_model = ShellTableModel() self.shell_table_view.setModel(self.shell_table_model) self.bottle_table_model = BottleTableModel(self) self.mapper = QDataWidgetMapper(self) self.mapper.setItemDelegate(ItemDelegate()) self.mapper.setModel(self.item_model) self.bottle_mapper = QDataWidgetMapper(self) self.bottle_mapper.setItemDelegate(ItemDelegate()) self.bottle_mapper.setModel(self.bottle_table_model) self.weapon_tree_view.activated.connect( self.handle_weapon_tree_view_activated) self.skill_model = SkillTranslationModel() self.skill_id_value.setModel(self.skill_model) self.import_export_manager = ImportExportManager( self.weapon_tree_view, WpDatGPlugin.import_export.get("safe_attrs")) self.import_export_manager.connect_custom_context_menu() mappings = [ (self.id_value, WpDatGEntry.id.index, b"text"), (self.name_value, WpDatGEntry.gmd_name_index.index, b"text"), (self.description_value, WpDatGEntry.gmd_description_index.index, b"text"), (self.order_value, WpDatGEntry.order.index), (self.tree_id_value, WpDatGEntry.tree_id.index), (self.tree_position_value, WpDatGEntry.tree_position.index), (self.is_fixed_upgrade_value, WpDatGEntry.is_fixed_upgrade.index, b"checked"), (self.base_model_id_value, WpDatGEntry.base_model_id.index), (self.part1_id_value, WpDatGEntry.part1_id.index), (self.part2_id_value, WpDatGEntry.part2_id.index), (self.color_value, WpDatGEntry.color.index), (self.muzzle_type_value, WpDatGEntry.muzzle_type.index, b"currentIndex"), (self.barrel_type_value, WpDatGEntry.barrel_type.index, b"currentIndex"), (self.magazine_type_value, WpDatGEntry.magazine_type.index, b"currentIndex"), (self.scope_type_value, WpDatGEntry.scope_type.index, b"currentIndex"), (self.rarity_value, WpDatGEntry.rarity.index), (self.cost_value, WpDatGEntry.crafting_cost.index), (self.raw_damage_value, WpDatGEntry.raw_damage.index), (self.affinity_value, WpDatGEntry.affinity.index), (self.defense_value, WpDatGEntry.defense.index), (self.deviation_value, WpDatGEntry.deviation.index, b"currentIndex"), (self.special_ammo_type_value, WpDatGEntry.special_ammo_type.index, b"currentIndex"), (self.element_id_value, WpDatGEntry.element_id.index, b"currentIndex"), (self.element_damage_value, WpDatGEntry.element_damage.index), (self.hidden_element_id_value, WpDatGEntry.hidden_element_id.index, b"currentIndex"), (self.hidden_element_damage_value, WpDatGEntry.hidden_element_damage.index), (self.elderseal_value, WpDatGEntry.elderseal.index, b"currentIndex"), (self.num_gem_slots, WpDatGEntry.num_gem_slots.index), (self.gem_slot1_lvl_value, WpDatGEntry.gem_slot1_lvl.index), (self.gem_slot2_lvl_value, WpDatGEntry.gem_slot2_lvl.index), (self.gem_slot3_lvl_value, WpDatGEntry.gem_slot3_lvl.index), (self.skill_id_value, WpDatGEntry.skill_id.index), ] for mapping in mappings: self.mapper.addMapping(*mapping) mappings = [ (self.close_range_value, BbtblEntry.close_range.index, b"currentIndex"), (self.power_value, BbtblEntry.power.index, b"currentIndex"), (self.paralysis_value, BbtblEntry.paralysis.index, b"currentIndex"), (self.poison_value, BbtblEntry.poison.index, b"currentIndex"), (self.sleep_value, BbtblEntry.sleep.index, b"currentIndex"), (self.blast_value, BbtblEntry.blast.index, b"currentIndex"), ] for mapping in mappings: self.bottle_mapper.addMapping(*mapping) @property def is_bow_type(self): return self.model.attrs.get("equip_type") == WeaponType.Bow def handle_weapon_tree_view_activated(self, qindex: QModelIndex): self.mapper.setCurrentModelIndex(qindex) entry = self.item_model.entries[qindex.row()] self.tabs_weapon_details.setTabText( 0, self.item_model.data(qindex, Qt.DisplayRole)) self.crafting_requirements_editor.set_current(entry.id) if self.is_bow_type: index = self.bottle_table_model.index(entry.special_ammo_type, 0) self.bottle_mapper.setCurrentModelIndex(index) else: index = self.shell_table_model.index(entry.shell_table_id, 0, QModelIndex()) self.shell_table_view.setRootIndex(index) self.shell_table_view.expandAll() def set_model(self, model): self.model = model self.item_model.update(model) self.skill_model.update(model.get_relation_data("t9n_skill_pt")) self.crafting_requirements_editor\ .set_model(model, model.attrs.get("equip_type")) if self.is_bow_type: self.bottle_table_model.update(model) self.hide_tab(self.tab_shell_table) self.special_ammo_type_value.deleteLater() self.special_ammo_type_label.deleteLater() else: self.shell_table_model.update(model) self.hide_tab(self.tab_bottle_table) self.configure_tree_view() def configure_tree_view(self): for index, col in enumerate(self.item_model.fields): self.weapon_tree_view.hideColumn(index) self.weapon_tree_view.showColumn(WpDatGEntry.id.index) self.weapon_tree_view.showColumn(WpDatGEntry.gmd_name_index.index) header = self.weapon_tree_view.header() header.setSectionResizeMode(WpDatGEntry.gmd_name_index.index, QHeaderView.Stretch) header.setSectionResizeMode(WpDatGEntry.id.index, QHeaderView.ResizeToContents) def hide_tab(self, widget): index = self.tabs_weapon_details.indexOf(widget) self.tabs_weapon_details.removeTab(index)
class SettingsController(QObject, LogMixin): settingsClosed = pyqtSignal() # send after model or backend data has been updated def __init__(self, parent): """ Initiate settings editing Signals * settingsClosed :param parent: parent window of settings dialog :return: """ super().__init__(parent) self._parent = parent print("controller/SettingsController parent: ", self._parent, " -> self: ", self) if oPB.PRINTHIER else None self.logger.debug("Initialize settings dialog") self.model = None # data models self.datamapper = None # QDataWidgetMapper object for field mapping self._modelDataChanged = False # is connect via model_data_changed function to itemChanged Signal of QStandardItemModel, will be reset in close_project() # create main window and logic self.ui = SettingsDialog(self._parent) # build special button groups for False/True choice self.optionGroupSrvVersion = SpecialOptionButtonGroup(self.ui.rdOpsiSrvNew, self.ui.rdOpsiSrvOld, [self.ui.rdSUDOWithPass, self.ui.rdSUDOWithoutPass], [self.ui.inpRootPass]) self.optionGroupSUDO = SpecialOptionButtonGroup(self.ui.rdSUDOWithPass, self.ui.rdSUDOWithoutPass) self.optionGroupEditorTyp = SpecialOptionButtonGroup(self.ui.rdEditorInternal, self.ui.rdEditorExternal, [self.ui.chkSyntaxHighlight, self.ui.chkCodeFolding], [self.ui.btnExternalEditor, self.ui.inpExternalEditor]) self.optionGroupDepotFuncs = SpecialOptionButtonGroup(self.ui.chkUseDepotFunctions, None, [], [self.ui.inpInstallCommand, self.ui.inpInstSetupCommand, self.ui.inpUninstallCommand, self.ui.inpUploadCommand]) self.optionGroupProxy = SpecialOptionButtonGroup(self.ui.chkUseProxy, None, [self.ui.inpProxyServer, self.ui.inpProxyPort, self.ui.inpProxyUser, self.ui.inpProxyPass], []) self.optionGroupSSHKeyFile = SpecialOptionButtonGroup(self.ui.chkUseKeyFile, None, [self.ui.btnSetKeyFile, self.ui.inpKeyFile], []) self.optionGroupLogFile = SpecialOptionButtonGroup(self.ui.chkWriteLog, None, [self.ui.btnLogFile, self.ui.inpLogFile, self.ui.cmbLogLevel], []) self.generate_model() self.connect_signals() # open modal dialog self.ui.tabWidget.setCurrentIndex(0) def generate_model(self): """Create data models and assign field mappings""" self.logger.debug("Generate configuration model") self.model = QtGui.QStandardItemModel(self.ui) self.model.setItem(0, 0, QtGui.QStandardItem(ConfigHandler.cfg.opsi_server)) self.model.setItem(0, 1, QtGui.QStandardItem(ConfigHandler.cfg.opsi_user)) self.model.setItem(0, 2, QtGui.QStandardItem(ConfigHandler.cfg.opsi_pass)) self.model.setItem(0, 3, QtGui.QStandardItem(ConfigHandler.cfg.root_pass)) self.model.setItem(0, 4, QtGui.QStandardItem(ConfigHandler.cfg.usenetdrive)) self.model.setItem(0, 5, QtGui.QStandardItem(ConfigHandler.cfg.age)) self.model.setItem(0, 6, QtGui.QStandardItem(ConfigHandler.cfg.sudo)) self.model.setItem(0, 7, QtGui.QStandardItem(ConfigHandler.cfg.sshport)) self.model.setItem(0, 8, QtGui.QStandardItem(ConfigHandler.cfg.usekeyfile)) self.model.setItem(0, 9, QtGui.QStandardItem(ConfigHandler.cfg.keyfilename)) self.model.setItem(0, 10, QtGui.QStandardItem(ConfigHandler.cfg.packagemaintainer)) self.model.setItem(0, 11, QtGui.QStandardItem(ConfigHandler.cfg.mailaddress)) self.model.setItem(0, 12, QtGui.QStandardItem(ConfigHandler.cfg.dev_dir)) self.model.setItem(0, 13, QtGui.QStandardItem(ConfigHandler.cfg.buildcommand)) self.model.setItem(0, 14, QtGui.QStandardItem(ConfigHandler.cfg.installcommand)) self.model.setItem(0, 15, QtGui.QStandardItem(ConfigHandler.cfg.uninstallcommand)) self.model.setItem(0, 16, QtGui.QStandardItem(ConfigHandler.cfg.showoutput)) self.model.setItem(0, 17, QtGui.QStandardItem(ConfigHandler.cfg.reload_for_at)) self.model.setItem(0, 18, QtGui.QStandardItem(ConfigHandler.cfg.wol_lead_time)) self.model.setItem(0, 19, QtGui.QStandardItem(ConfigHandler.cfg.uploadcommand)) self.model.setItem(0, 20, QtGui.QStandardItem(ConfigHandler.cfg.instsetupcommand)) self.model.setItem(0, 21, QtGui.QStandardItem(ConfigHandler.cfg.use_depot_funcs)) self.model.setItem(0, 22, QtGui.QStandardItem(ConfigHandler.cfg.use_extended_changelog)) self.model.setItem(0, 23, QtGui.QStandardItem(ConfigHandler.cfg.scripteditor)) self.model.setItem(0, 24, QtGui.QStandardItem(ConfigHandler.cfg.chlog_block_marker)) self.model.setItem(0, 25, QtGui.QStandardItem(ConfigHandler.cfg.editor_intern)) self.model.setItem(0, 26, QtGui.QStandardItem(ConfigHandler.cfg.editor_use_styling)) self.model.setItem(0, 27, QtGui.QStandardItem(ConfigHandler.cfg.editor_use_folding)) self.model.setItem(0, 28, QtGui.QStandardItem(ConfigHandler.cfg.chlog_on_build)) self.model.setItem(0, 29, QtGui.QStandardItem(ConfigHandler.cfg.chlog_on_save)) self.model.setItem(0, 30, QtGui.QStandardItem(ConfigHandler.cfg.no_error_msg)) self.model.setItem(0, 31, QtGui.QStandardItem(ConfigHandler.cfg.no_warning_msg)) self.model.setItem(0, 32, QtGui.QStandardItem(ConfigHandler.cfg.no_info_msg)) self.model.setItem(0, 33, QtGui.QStandardItem(ConfigHandler.cfg.no_at_warning_msg)) self.model.setItem(0, 34, QtGui.QStandardItem(ConfigHandler.cfg.language)) self.model.setItem(0, 35, QtGui.QStandardItem(ConfigHandler.cfg.useproxy)) self.model.setItem(0, 36, QtGui.QStandardItem(ConfigHandler.cfg.updatecheck)) self.model.setItem(0, 37, QtGui.QStandardItem(ConfigHandler.cfg.proxy_server)) self.model.setItem(0, 38, QtGui.QStandardItem(ConfigHandler.cfg.proxy_port)) self.model.setItem(0, 39, QtGui.QStandardItem(ConfigHandler.cfg.proxy_user)) self.model.setItem(0, 40, QtGui.QStandardItem(ConfigHandler.cfg.proxy_pass)) self.model.setItem(0, 41, QtGui.QStandardItem(ConfigHandler.cfg.log_always)) self.model.setItem(0, 42, QtGui.QStandardItem(ConfigHandler.cfg.log_file)) self.model.setItem(0, 43, QtGui.QStandardItem(ConfigHandler.cfg.log_level)) self.logger.debug("Create data widget mapper") self.datamapper = QDataWidgetMapper(self.ui) self.datamapper.setModel(self.model) self.datamapper.addMapping(self.ui.inpConfigServer, 0) self.datamapper.addMapping(self.ui.inpOpsiUser, 1) self.datamapper.addMapping(self.ui.inpOpsiPass, 2) self.datamapper.addMapping(self.ui.inpRootPass, 3) self.datamapper.addMapping(self.ui.chkUseNetworkDrive, 4, "checked") self.datamapper.addMapping(self.optionGroupSrvVersion, 5, "checked") self.datamapper.addMapping(self.optionGroupSUDO, 6, "checked") self.datamapper.addMapping(self.ui.inpSSHPort, 7) self.datamapper.addMapping(self.optionGroupSSHKeyFile, 8, "checked") self.datamapper.addMapping(self.ui.inpKeyFile, 9) self.datamapper.addMapping(self.ui.inpMaintainer, 10) self.datamapper.addMapping(self.ui.inpMailAddress, 11) self.datamapper.addMapping(self.ui.inpDevFolder, 12) self.datamapper.addMapping(self.ui.inpBuildCommand, 13) self.datamapper.addMapping(self.ui.inpInstallCommand, 14) self.datamapper.addMapping(self.ui.inpUninstallCommand, 15) self.datamapper.addMapping(self.ui.chkShowOutput, 16, "checked") self.datamapper.addMapping(self.ui.chkAlwaysReload, 17, "checked") self.datamapper.addMapping(self.ui.inpWOLLeadTime, 18) self.datamapper.addMapping(self.ui.inpUploadCommand, 19) self.datamapper.addMapping(self.ui.inpInstSetupCommand, 20) #self.datamapper.addMapping(self.settings.chkUseDepotFunctions, 21, "checked") self.datamapper.addMapping(self.optionGroupDepotFuncs, 21, "checked") self.datamapper.addMapping(self.ui.chkExtendedEditor, 22, "checked") self.datamapper.addMapping(self.ui.inpExternalEditor, 23) self.datamapper.addMapping(self.ui.inpBlockMarker, 24) self.datamapper.addMapping(self.optionGroupEditorTyp, 25, "checked") self.datamapper.addMapping(self.ui.chkSyntaxHighlight, 26, "checked") self.datamapper.addMapping(self.ui.chkCodeFolding, 27, "checked") self.datamapper.addMapping(self.ui.chkForceEntryBuild, 28, "checked") self.datamapper.addMapping(self.ui.chkForceEntrySave, 29, "checked") self.datamapper.addMapping(self.ui.chkMsgError, 30, "checked") self.datamapper.addMapping(self.ui.chkMsgWarning, 31, "checked") self.datamapper.addMapping(self.ui.chkMsgInfo, 32, "checked") self.datamapper.addMapping(self.ui.chkMsgAT, 33, "checked") self.datamapper.addMapping(self.ui.cmbLanguage, 34, "currentText") self.datamapper.addMapping(self.optionGroupProxy, 35, "checked") self.datamapper.addMapping(self.ui.chkUpdates, 36, "checked") self.datamapper.addMapping(self.ui.inpProxyServer, 37) self.datamapper.addMapping(self.ui.inpProxyPort, 38) self.datamapper.addMapping(self.ui.inpProxyUser, 39) self.datamapper.addMapping(self.ui.inpProxyPass, 40) self.datamapper.addMapping(self.optionGroupLogFile, 41, "checked") self.datamapper.addMapping(self.ui.inpLogFile, 42) self.datamapper.addMapping(self.ui.cmbLogLevel, 43) self.datamapper.toFirst() def connect_signals(self): """Connect signals""" self.logger.debug("Connect signals") self.model.itemChanged.connect(self.model_data_changed) self.ui.btnSave.clicked.connect(self.save_config) self.ui.dataChanged.connect(self.datamapper.submit) self.ui.settingsAboutToBeClosed.connect(self.close_dialog) self.ui.rdOpsiSrvNew.clicked.connect(self.set_model_data) self.ui.rdOpsiSrvOld.clicked.connect(self.set_model_data) self.ui.rdSUDOWithPass.clicked.connect(self.set_model_data) self.ui.rdSUDOWithoutPass.clicked.connect(self.set_model_data) self.ui.rdEditorInternal.clicked.connect(self.set_model_data) self.ui.rdEditorExternal.clicked.connect(self.set_model_data) self.ui.chkUseDepotFunctions.clicked.connect(self.set_model_data) self.ui.chkUseProxy.clicked.connect(self.set_model_data) self.ui.chkUseKeyFile.clicked.connect(self.set_model_data) self.ui.chkWriteLog.clicked.connect(self.set_model_data) @pyqtSlot() def model_data_changed(self): """Update model changed marker""" self.logger.debug("Model data changed") self._modelDataChanged = True @pyqtSlot() def set_model_data(self): """Whenever a special radio button or checkbox is clicked, the corresponding model data element will be set accordingly. This has to be done like so, because radio buttons and checkboxes are not directly linked to the model, but via a SpecialOptionButtonGroup object. """ self.logger.debug("Set model data values from button: " + self.sender().objectName()) # radio buttons if self.sender().objectName() == "rdOpsiSrvNew": if self.ui.rdOpsiSrvNew.isChecked(): self.model.item(0, 5).setText("True") if self.sender().objectName() == "rdOpsiSrvOld": if self.ui.rdOpsiSrvOld.isChecked(): self.model.item(0, 5).setText("False") if self.sender().objectName() == "rdSUDOWithPass": if self.ui.rdSUDOWithPass.isChecked(): self.model.item(0, 6).setText("True") if self.sender().objectName() == "rdSUDOWithoutPass": if self.ui.rdSUDOWithoutPass.isChecked(): self.model.item(0, 6).setText("False") if self.sender().objectName() == "rdEditorInternal": if self.ui.rdEditorInternal.isChecked(): self.model.item(0, 25).setText("True") if self.sender().objectName() == "rdEditorExternal": if self.ui.rdEditorExternal.isChecked(): self.model.item(0, 25).setText("False") # check boxes if self.sender().objectName() == "chkUseKeyFile": if self.ui.chkUseKeyFile.isChecked(): self.model.item(0, 8).setText("True") else: self.model.item(0, 8).setText("False") if self.sender().objectName() == "chkUseDepotFunctions": if self.ui.chkUseDepotFunctions.isChecked(): self.model.item(0, 21).setText("True") else: self.model.item(0, 21).setText("False") if self.sender().objectName() == "chkUseProxy": if self.ui.chkUseProxy.isChecked(): self.model.item(0, 35).setText("True") else: self.model.item(0, 35).setText("False") if self.sender().objectName() == "chkWriteLog": if self.ui.chkWriteLog.isChecked(): self.model.item(0, 41).setText("True") else: self.model.item(0, 41).setText("False") def update_backend_data(self): self.logger.debug("Update config backend") ConfigHandler.cfg.opsi_server = self.model.item(0, 0).text() ConfigHandler.cfg.opsi_user = self.model.item(0, 1).text() ConfigHandler.cfg.opsi_pass = self.model.item(0, 2).text() ConfigHandler.cfg.root_pass = self.model.item(0, 3).text() ConfigHandler.cfg.usenetdrive = self.model.item(0, 4).text().title() ConfigHandler.cfg.age = self.model.item(0, 5).text().title() ConfigHandler.cfg.sudo = self.model.item(0, 6).text().title() ConfigHandler.cfg.cfg.sshport = self.model.item(0, 7).text() ConfigHandler.cfg.usekeyfile = self.model.item(0, 8).text().title() ConfigHandler.cfg.keyfilename = self.model.item(0, 9).text() ConfigHandler.cfg.packagemaintainer = self.model.item(0, 10).text() ConfigHandler.cfg.mailaddress = self.model.item(0, 11).text() ConfigHandler.cfg.dev_dir = self.model.item(0, 12).text() ConfigHandler.cfg.buildcommand = self.model.item(0, 13).text() ConfigHandler.cfg.installcommand = self.model.item(0, 14).text() ConfigHandler.cfg.uninstallcommand = self.model.item(0, 15).text() ConfigHandler.cfg.showoutput = self.model.item(0, 16).text().title() ConfigHandler.cfg.reload_for_at = self.model.item(0, 17).text().title() ConfigHandler.cfg.wol_lead_time = self.model.item(0, 18).text() ConfigHandler.cfg.uploadcommand = self.model.item(0, 19).text() ConfigHandler.cfg.instsetupcommand = self.model.item(0, 20).text() ConfigHandler.cfg.use_depot_funcs = self.model.item(0, 21).text().title() ConfigHandler.cfg.use_extended_changelog = self.model.item(0, 22).text().title() ConfigHandler.cfg.scripteditor = self.model.item(0, 23).text() ConfigHandler.cfg.chlog_block_marker = self.model.item(0, 24).text() ConfigHandler.cfg.editor_intern = self.model.item(0, 25).text().title() ConfigHandler.cfg.editor_use_styling = self.model.item(0, 26).text().title() ConfigHandler.cfg.editor_use_folding = self.model.item(0, 27).text().title() ConfigHandler.cfg.chlog_on_build = self.model.item(0, 28).text().title() ConfigHandler.cfg.chlog_on_save = self.model.item(0, 29).text().title() ConfigHandler.cfg.no_error_msg = self.model.item(0, 30).text().title() ConfigHandler.cfg.no_warning_msg = self.model.item(0, 31).text().title() ConfigHandler.cfg.no_info_msg = self.model.item(0, 32).text().title() ConfigHandler.cfg.no_at_warning_msg = self.model.item(0, 33).text().title() ConfigHandler.cfg.language = self.model.item(0, 34).text() ConfigHandler.cfg.useproxy = self.model.item(0, 35).text().title() ConfigHandler.cfg.updatecheck = self.model.item(0, 36).text().title() ConfigHandler.cfg.proxy_server = self.model.item(0, 37).text() ConfigHandler.cfg.proxy_port = self.model.item(0, 38).text() ConfigHandler.cfg.proxy_user = self.model.item(0, 39).text() ConfigHandler.cfg.proxy_pass = self.model.item(0, 40).text() ConfigHandler.cfg.log_always = self.model.item(0, 41).text().title() ConfigHandler.cfg.log_file = self.model.item(0, 42).text() ConfigHandler.cfg.log_level = self.model.item(0, 43).text() def close_dialog(self): """Close settings dialog""" ignoreChanges = True if self._modelDataChanged == True: retval = QMessageBox.question(None, translate("settingsController", "Question"), translate("settingsController", "There are unsaved changes! Do you really want to continue?"), QMessageBox.Yes, QMessageBox.No) if retval == QMessageBox.No: self.logger.debug("Unsaved changes have been ignored.") ignoreChanges = False if ignoreChanges: self.logger.debug("Close settings dialog") self.ui.close() self.logger.debug("Emit signal settingsClosed") self.settingsClosed.emit() @pyqtSlot() def save_config(self): """Get field values and initiate saving of configuration""" if self._modelDataChanged: self.update_backend_data() ConfigHandler.cfg.save() self._modelDataChanged = None self.close_dialog()
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 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 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)