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() # --------------------------------
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 JPMainTableModel(QtSql.QSqlRelationalTableModel): # _tp中存放需要添加到数据映射器中的控件类型 # 要添加的控件,必须用字段名命名(大小写敏感) _tp = (QtWidgets.QLineEdit, QtWidgets.QDateEdit, QtWidgets.QComboBox, QtWidgets.QTextEdit, QtWidgets.QCheckBox, QtWidgets.QSpinBox) def __init__(self, parent: QWidget, tableName: str, filter: str = None, db=QtSql.QSqlDatabase()): '''用于窗体模式进行数据编辑时的主窗体数据模型。\n 会自动增加数据映射器,但是外键字段要使用addComboBoxData方法增加列表文字。 最后要调用tofirst()方法定位编辑的记录。\n 注:数据映射器不能增删记录,只能编辑 ''' super().__init__(parent=parent, db=db) self.parent = parent self.setTable(tableName) if filter: self.setFilter(filter) self.select() rec = self.record() self.mapper = QDataWidgetMapper(parent) self.mapper.setModel(self) self.mapper.setItemDelegate(_JPRelationalDelegate(parent, self.mapper)) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) for i in range(rec.count()): widget = parent.findChild(self._tp, rec.fieldName(i)) if widget: if not isinstance(widget, QComboBox): self.mapper.addMapping(widget, i) def toFirst(self): # 定位到模型中第一条记录 self.mapper.toFirst() def addComboBoxData(self, fieldName: str, dataList: list, displayColumn: int = 0, modelColumn: int = 1): '''给模型中添加一个QComboBox控件的数据源。\n dataList为行来源数据源,列表对象.\n displayColumn为Combobox中要显示文字位于列表中的列号(首列为0)\n modelColumn为要保存到模型中数据在列表中的列号(首列为0)\n ''' widget = self.parent.findChild(QComboBox, fieldName) if widget is not None: widget.setModel( _JPComboBoxModel(widget, dataList, displayColumn, modelColumn)) self.mapper.addMapping(widget, self.fieldIndex(fieldName)) def saveData(self): pass
class 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 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 QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.splitter) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) def __openTable(self): self.tabModel = QSqlTableModel(self, self.DB) self.tabModel.setTable("employee") self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) if(self.tabModel.select()==False): QMessageBox.critical(self, "错误信息", "打开数据表错误,错误信息\n"+self.tabModel.lastError().text()) return self.__getFieldNames() self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.tabModel) self.selModel.currentChanged.connect(self.do_currentChanged) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.tableView.setModel(self.tabModel) self.ui.tableView.setSelectionModel(self.selModel) self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) strList = ("男", "女") self.__delegatesex = QmyComboBoxDelegate() self.__delegatesex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegatesex) strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) self.ui.actOpenDB.setEnabled(False) self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) def __getFieldNames(self): emptyRec = self.tabModel.record() self.fldNum = {} for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName]=i print(self.fldNum) def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if(current.isValid() == False): self.ui.dbLabPhoto.clear() return self.mapper.setCurrentIndex(current.row()) curRec = self.tabModel.record(current.row()) if(curRec.isNull("Photo")): self.ui.dbLabPhoto.clear() else: data = curRec.value("Photo") pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename ,flt = QFileDialog.getOpenFileName(self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if(res == False): QMessageBox.information(self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) curIndex = self.tabModel.index(self.tabModel.rowCount()-1, 1) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000+self.tabModel.rowCount()) self.tabModel.setData(self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row()) @pyqtSlot() def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setNull("Photo") self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() @pyqtSlot() def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg") if(fileName==''): return file=QFile(fileName) file.open(QIODevice.ReadOnly) try: data = file.readAll() finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setValue("Photo", data) self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actScan_triggered(self): if(self.tabModel.rowCount()==0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) salary = aRec.value("Salary") salary = salary*1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if(self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕了") @pyqtSlot() def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") @pyqtSlot() def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女'") @pyqtSlot() def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("")
class EmployeeEditView(QWidget, Ui_FormEditDeleteEmployee): def __init__(self, employee_model, employee_controller, main_window): super().__init__() self.setupUi(self) self.employee_model: EmployeeModel = employee_model self.employee_controller: EmployeeController = employee_controller self.main_window: MainWindowView = main_window self.mapper = QDataWidgetMapper() self.set_employee_editor() def show_editor(self, s=None): # load role combobox self.load_employee_positions_combobox() # Set the selected employee to the editor i = self.is_selected() if i & i >= 0: self.mapper.setCurrentIndex(i) self.show() def clear_fields(self): self.lineEditEmployeeID.clear() self.dteEditEmployeeDate.clear() self.spinEditEmployeeSalary.clear() self.cmbEditEmployeeRole.clear() self.load_employee_positions_combobox() def load_employee_positions_combobox(self): positions = self.employee_model.get_employee_positions() self.cmbEditEmployeePosition.addItem("") self.cmbEditEmployeePosition.addItems(positions) def is_selected(self, s=None) -> int: # print(self.selection_model.hasSelection()) # print(self.selection_model.isRowSelected(0)) index = self.main_window.tblEmployees.selectionModel().currentIndex().row() return index def set_employee_editor(self, index=0): self.btnEditEmployeePrevious.clicked.connect(self.mapper.toPrevious) self.btnEditEmployeeNext.clicked.connect(self.mapper.toNext) self.btnEditEmployeeSave.clicked.connect(self.update_button_action) self.btnEditEmployeeDelete.clicked.connect(self.delete_button_action) self.mapper.setModel(self.main_window.employee_sql_query_model) self.mapper.addMapping(self.lineEditEmployeeID, 0) self.mapper.addMapping(self.lineEditEmployeeName, 1) self.mapper.addMapping(self.cmbEditEmployeePosition, 2) self.mapper.addMapping(self.spinEditEmployeeSalary, 3) self.mapper.addMapping(self.dteEditEmployeeDate, 4) # self.set_student_table(self.model) # if index > 0: # self.mapper.setCurrentIndex(index) self.mapper.toFirst() def update_button_action(self, saved): employee_id: int = int(self.lineEditEmployeeID.text()) employee_name: str = self.lineEditEmployeeName.text() employee_role: str = self.cmbEditEmployeePosition.currentText() employee_salary: float = float(self.spinEditEmployeeSalary.cleanText()) employee_date: str = self.dteEditEmployeeDate.text() employee: Employee = Employee(employee_name, employee_role, employee_salary, employee_date) self.employee_controller.update_employee(employee, employee_id) def delete_button_action(self): dialog_alert_view: DialogAlertView = DialogAlertView() employee_id = int(self.lineEditEmployeeID.text()) dialog_alert_view.show_dialog(True, 4) self.employee_controller.delete_employee(employee_id)
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 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)
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() # The mainloop of the application. The event handling starts from this point. # The exec_() method has an underscore. It is because the exec is a Python keyword. And thus, exec_() was used instead. exit_code = app.exec_()
class MovieInfoView(QWidget): ID_DESC = 0 ID_CHECK = 1 ID_CTRL = 2 def __init__(self): super().__init__() self.initWidgets() self.mapWidget2Model() def initWidgets(self): from collections import OrderedDict self.controls = OrderedDict() self.controls['path'] = ['Path:', QLineEdit()] self.controls['title'] = ['Title:', QLineEdit()] self.controls['originaltitle'] = ['Original Title:', QLineEdit()] self.controls['rating'] = ['Rating:', RatingWidget()] self.controls['year'] = ['Year:', QLineEdit()] self.controls['releasedate'] = ['Release Date:', QLineEdit()] self.controls['id'] = ['ID:', QLineEdit()] self.controls['studio'] = ['Studio:', QLineEdit()] self.controls['set'] = ['Movie Set:', QLineEdit()] self.controls['plot'] = ['Plot:', QPlainTextEdit()] self.controls['genre'] = ['Genre:', GenreWidget()] self.controls['actor'] = ['Actor:', GenreWidget()] left = QGridLayout() row = 0 for _, ctrl in self.controls.items(): check = QCheckBox() check.stateChanged.connect(self.onChecked) ctrl.insert(self.ID_CHECK, check) left.addWidget(ctrl[self.ID_CHECK], row, 0, Qt.AlignTop | Qt.AlignRight) left.addWidget(QLabel(ctrl[self.ID_DESC]), row, 1, Qt.AlignTop | Qt.AlignRight) left.addWidget(ctrl[self.ID_CTRL], row, 2) row += 1 right = QVBoxLayout() self.poster = CoverWidget('poster') self.fanart = CoverWidget('fanart', False) right.addWidget(self.poster) right.addWidget(self.fanart) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addLayout(left) layout.addLayout(right) self.setLayout(layout) def mapWidget2Model(self): self.mapper = QDataWidgetMapper(self) self.model = MovieInfoModel() self.mapper.setModel(self.model) index = 0 for key, ctrl in self.controls.items(): if 'genre' == key or 'actor' == key or 'rating' == key: self.mapper.addMapping(ctrl[self.ID_CTRL], index, b'Value') else: self.mapper.addMapping(ctrl[self.ID_CTRL], index) index += 1 self.mapper.addMapping(self.poster, index) index += 1 self.mapper.addMapping(self.fanart, index) self.mapper.setItemDelegate(MovieInfoDelegate()) def onChecked(self, state): columnFilter = [] for key, val in self.controls.items(): if val[self.ID_CHECK].isChecked(): columnFilter.append(key) self.model.setColumnFilter(columnFilter) def setMovieInfo(self, movieinfo, bypassFilter): if not movieinfo: return self.model.setMovieInfo(movieinfo, bypassFilter) self.mapper.toFirst() def clearMovieInfo(self, forceClear=True, keepColumnFilter=False): self.model.clearMovieInfo(forceClear, keepColumnFilter) if not keepColumnFilter: for _, val in self.controls.items(): val[self.ID_CHECK].setCheckState(Qt.Unchecked) self.mapper.toFirst() def saveMovieInfo(self): import traceback #print('updateMovie') try: self.model.saveMovieInfo() except: traceback.print_exc()
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) ## tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.tabModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##打开数据表 self.tabModel = QSqlTableModel(self, self.DB) #数据模型 self.tabModel.setTable("employee") #设置数据表 self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit ) #数据保存方式,OnManualSubmit , OnRowChange self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) #排序 if (self.tabModel.select() == False): #查询数据失败 QMessageBox.critical( self, "错误信息", "打开数据表错误,错误信息\n" + self.tabModel.lastError().text()) return self.__getFieldNames() #获取字段名和序号 ##字段显示名 self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") #这两个字段不在tableView中显示 self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("empNo"), Qt.Horizontal, "工号") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Name"), Qt.Horizontal, "姓名") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Gender"), Qt.Horizontal, "性别") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Birthday"), Qt.Horizontal, "出生日期") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Province"), Qt.Horizontal, "省份") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Department"),Qt.Horizontal, "部门") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Salary"), Qt.Horizontal, "工资") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Memo"), Qt.Horizontal, "备注") #这两个字段不在tableView中显示 ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Photo"), Qt.Horizontal, "照片") ##创建界面组件与数据模型的字段之间的数据映射 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) #设置数据模型 self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与tabModel的具体字段之间的联系 self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() #移动到首记录 self.selModel = QItemSelectionModel(self.tabModel) #选择模型 self.selModel.currentChanged.connect(self.do_currentChanged) #当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) #选择行变化时 self.ui.tableView.setModel(self.tabModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) #隐藏列 self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) #隐藏列 ##tableView上为“性别”和“部门”两个字段设置自定义代理组件 strList = ("男", "女") self.__delegateSex = QmyComboBoxDelegate() self.__delegateSex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn( self.fldNum["Gender"], self.__delegateSex) #Combbox选择型 strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) ##更新actions和界面组件的使能状态 self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##选择数据库,打开数据表 def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQLITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##保存修改 def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##取消修改 def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##添加记录 def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) #在末尾添加一个记录 curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) #创建最后一行的ModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置刚插入的行为当前选择行 currow = curIndex.row() #获得当前行 self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000 + self.tabModel.rowCount()) #自动生成编号 self.tabModel.setData( self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() ##插入记录 def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() #QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() #清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除记录 def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() #获取当前选择单元格的模型索引 self.tabModel.removeRow(curIndex.row()) #删除当前行 @pyqtSlot() ##清除照片 def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录,QSqlRecord curRec.setNull("Photo") #设置为空值 self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() #清除界面上的图片显示 @pyqtSlot() ##设置照片 def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) #fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() #QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录QSqlRecord curRec.setValue("Photo", data) #设置字段数据 self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) #在界面上显示 @pyqtSlot() ##涨工资,遍历数据表所有记录 def on_actScan_triggered(self): if (self.tabModel.rowCount() == 0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) #获取当前记录 ## salary=aRec.value("Salary").toFloat() #错误,无需再使用toFloat()函数 salary = aRec.value("Salary") salary = salary * 1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if (self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕") @pyqtSlot(int) ##排序字段变化 def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##升序 def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() ##降序 def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##过滤,男 def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##数据过滤,女 def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女' ") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##取消数据过滤 def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("") ## print(self.tabModel.filter()) ## self.tabModel.select() ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): ##更新actPost和actCancel 的状态 self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) #有未保存修改时可用 self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): #行切换时的状态控制 self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if (current.isValid() == False): self.ui.dbLabPhoto.clear() #清除图片显示 return self.mapper.setCurrentIndex(current.row()) #更新数据映射的行号 curRec = self.tabModel.record(current.row()) #获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): #图片字段内容为空 self.ui.dbLabPhoto.clear() else: ## data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W))
class MyDialog(QDialog): def __init__(self, parent=None): super(MyDialog, self).__init__(parent) self.datas() self.selfSetUp() self.setUpWidgets() self.setUpLayouts() self.setUpModels() self.setUpMapper() self.setUpConnections() def datas(self): self.kinds = ['vegetale', 'animale'] def selfSetUp(self): self.setWindowTitle('TableView v 0.2') self.setMinimumSize(QSize(int(680 * 1.5), int(600 * 1.5))) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) sizePolicy.setWidthForHeight(self.sizePolicy().hasWidthForHeight()) self.setSizePolicy(sizePolicy) def setUpWidgets(self): # self.table = Mytable(self) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) sizePolicy.setWidthForHeight(self.sizePolicy().hasWidthForHeight()) self.table = QLineEdit() self.table.setStyleSheet("background-color: rgb(255, 255, 127)") labSize = QSize(100, 30) self.table.setFixedSize(labSize) self.table.setSizePolicy(sizePolicy) self.combo = ComboSenzaFreccia(self) botSize = QSize(30, 30) self.bot_next = QPushButton(text='Next', parent=self) self.bot_prev = QPushButton(text='Prev', parent=self) self.bot_prev.setSizePolicy(sizePolicy) self.bot_next.setSizePolicy(sizePolicy) self.bot_prev.setFixedSize(botSize) self.bot_next.setFixedSize(botSize) def setUpLayouts(self): self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.finalLay = QHBoxLayout() self.finalLay.setObjectName("finalLay") self.prevLay = QVBoxLayout() self.prevLay.setObjectName("prevLay") self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_3.addItem(spacerItem) self.bot_prev.setObjectName("bot_prev") self.horizontalLayout_3.addWidget(self.bot_prev) self.prevLay.addLayout(self.horizontalLayout_3) spacerItem1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.prevLay.addItem(spacerItem1) self.finalLay.addLayout(self.prevLay) self.centLay = QVBoxLayout() self.centLay.setObjectName("centLay") self.verticalLayout_2 = QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.combo.setMaxVisibleItems(12) self.combo.setMaxCount(12) self.combo.setObjectName("ComboSenzaFreccia") self.verticalLayout_2.addWidget(self.combo) # self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setObjectName("table") # self.table.horizontalHeader().setStretchLastSection(True) self.verticalLayout_2.addWidget(self.table) self.centLay.addLayout(self.verticalLayout_2) self.finalLay.addLayout(self.centLay) self.nextLay = QVBoxLayout() self.nextLay.setObjectName("nextLay") self.horizontalLayout_4 = QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.bot_next.setObjectName("bot_next") self.horizontalLayout_4.addWidget(self.bot_next) spacerItem2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem2) self.nextLay.addLayout(self.horizontalLayout_4) spacerItem3 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.nextLay.addItem(spacerItem3) self.finalLay.addLayout(self.nextLay) self.horizontalLayout.addLayout(self.finalLay) self.setLayout(self.horizontalLayout) def setUpLayouts_old(self): vboxPrev = QVBoxLayout() self.prevLay = QHBoxLayout() sp = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) vboxPrev.addItem(sp) vboxPrev.addWidget(self.bot_prev) sp = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.prevLay.addLayout(vboxPrev) self.prevLay.addItem(sp) vboxPrev = QVBoxLayout() self.nextLay = QHBoxLayout() sp = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) vboxPrev.addWidget(self.bot_next) vboxPrev.addItem(sp) sp = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.nextLay.addLayout(vboxPrev) self.nextLay.addItem(sp) self.centLay = QVBoxLayout() self.centLay.addWidget(self.combo) self.centLay.addWidget(self.table) self.finalLay = QHBoxLayout() self.finalLay.addLayout(self.prevLay) self.finalLay.addLayout(self.centLay) self.finalLay.addLayout(self.nextLay) self.setLayout(self.finalLay) def setUpModels(self): ## sono gli items che devo essere nella combo (es: i mesi) items = ['cane', 'gatto', 'melo', 'pero'] #i numeri 4 e 2 passati come argomenti per il modello base indicano le righe e le colonne del modello # basate sulla len di items e di self.kinds self.model = QStandardItemModel(4, 2, self) # questo è il modello passato alla combo, che ha come elementi gli items self.typeModel = QStringListModel(items, self) self.combo.setModel(self.typeModel) # a ogni elemento di items deve corrispondere un valore, self.kinds, ricavato dall'indice types = ("1", "1", "0", "0") # per ogni item viene assegnato il valore nel modello matrice # la mappatura ha nella colonna 0 la combobox e gli viene assegnato un item # alla colonna 1 del modello viene assegnato il riferimento a self.kinds passato attraverso types for row, item in enumerate(items): self.model.setItem(row, 0, QStandardItem(item)) self.model.setItem(row, 1, QStandardItem(self.kinds[int(types[row])])) # self.model.setItem(row, 1, QStandardItem(types[row])) # for row, col in enumerate() def setUpMapper(self): self.mapper = QDataWidgetMapper(self) self.mapper.setModel(self.model) # self.mapper.addMapping(self.table,1,b'currentIndex') self.mapper.addMapping(self.table, 1) # self.mapper.addMapping(self.combo,0,b'currentIndex') self.mapper.addMapping(self.combo, 0) self.mapper.currentIndexChanged.connect(self.updateButtons) self.mapper.toFirst() def setUpConnections(self): self.bot_next.clicked.connect(self.mapper.toNext) self.bot_prev.clicked.connect(self.mapper.toPrevious) try: self.combo.currentIndexChanged.connect( lambda x: self.mapper.setCurrentIndex(x)) pass except: print(fex()) # self.combo.currentIndexChanged.connect(lambda x: self.mapper.model().c) def updateButtons(self, row): self.bot_prev.setEnabled(row > 0) self.bot_next.setEnabled(row < self.model.rowCount() - 1) print('mapper current index', self.mapper.currentIndex()) print('combo current index', self.combo.currentIndex()) # print('mapper model rows count', self.mapper.model().rowCount()) print('-' * 20)
class DBControl(QObject, SqlalchemyDBControl, DB_Util1): def __init__(self, parent=None): super().__init__() import Client self.mw: Client.MainWindow = parent self.__initDB() self.__initUI() def __initDB(self): self._initDB() self._createDB() self._createModel() self._selectModel() # <editor-fold desc="qtdb"> def _initDB(self): self.orm_db = self.create_session() self.qt_db = QSqlDatabase.addDatabase('QSQLITE') self.qt_db.setDatabaseName(self.db_path) if not self.qt_db.open(): print("数据库连接失败") return False return self.qt_db def _createDB(self): self.create_table() print(self.qt_db.database().tables()) def _createModel(self): def createModel(tbName): model = QSqlTableModel() # model.setEditStrategy(QSqlTableModel.OnFieldChange) model.setTable(tbName) model.select() setattr( self, tbName + "_model", model ) # here will create [self.watching_show_model ... ] and other model return model def createView(title, model, delegate=None): view = QQTableView(self.mw) view.setModel(model) view.setItemDelegateForColumn( 0, delegate) if delegate is not None else None view.REMOVE_SIGNAL.connect(self.del_watching) return view def createTab(title): tab_form = self.mw.repo_tabWidget new_tab = QWidget() layout = QHBoxLayout(new_tab) layout.setContentsMargins(1, 1, 1, 1) tab_form.addTab(new_tab, title) return layout def createItem(title): comb = self.mw.filter_cb comb.addItem(title) tables = self.qt_db.database().tables() for tableName in tables: if tableName[-4:] == "show": # print(tableName) model = createModel(tableName) delegate = URLDelegate(self) view_ = createView(tableName, model, delegate) new_tab = createTab(tableName) _ = createItem(tableName) new_tab.addWidget(view_) # modeln self.filterModel = QSqlTableModel() # model1 self.urlTableModel = QSqlTableModel() self.urlTableModel.setTable(WEBSITE_TABLE_NAME) # model2 self.unameTableModel = QSqlTableModel() self.unameTableModel.setTable(USER_TABLE_NAME) if isinstance( self.unameTableModel, QSqlTableModel) else None # model3 self.upwdQueryModel = QSqlQueryModel(self) """ ###self.url_mapper = QDataWidgetMapper() # 数据映射 ###self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # 提交策略 ###self.url_mapper.setModel(self.urlQueryModel) # 映射的模型源 ###self.url_mapper.addMapping(self.mw.web_site_cb,0, b"currentIndex") ###self.mw.web_site_cb.setModel(self.urlQueryModel) # model4 self.realationModel = QSqlRelationalTableModel(self) self.realationModel.setEditStrategy(QSqlTableModel.OnFieldChange) self.realationModel.setTable(REPOSITORIES_TABLE_NAME) self.realationModel.setRelation(1, QSqlRelation(WEBSITE_TABLE_NAME, 'id', 'url')) # TW_TITLE = [""] # for i in range(len(TW_TITLE)): # self.realationModel.setHeaderData(i + 1, Qt.Horizontal, TW_TITLE[i]) self.realationModel.select() """ def _selectModel(self): # <editor-fold desc="1"> self.mw.web_site_cb.setModel(self.urlTableModel) # self.mw.web_user_cb.setModelColumn(1) # </editor-fold> # <editor-fold desc="2"> self.mw.web_user_cb.setModel(self.unameTableModel) self.mw.web_user_cb.setModelColumn(1) # </editor-fold> # <editor-fold desc="3"> self.upwd_mapper = QDataWidgetMapper() # 数据映射 self.upwd_mapper.setModel(self.upwdQueryModel) # 映射的模型源 self.upwd_mapper.addMapping(self.mw.web_pwd_le, 0) ## self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # 提交策略 # </editor-fold> # <editor-fold desc="4"> self.mw.filter_tv.setModel(self.filterModel) self.mw.filter_tv.setItemDelegateForColumn(0, URLDelegate()) # </editor-fold> # </editor-fold> def __initUI(self): self._pre_url_ = "---" self._pre_uname_ = "---" # 1 . self.mw.web_site_cb.activated.connect(self.on_web_uname_query) self.mw.web_user_cb.activated.connect(self.on_web_upwd_query) def on_web_site_url_query(self): print("查网址") # self.urlTableModel.setQuery(QSqlQuery(self.ORM2SQL(select([WebSite.url])))) self.reloadTableModel(self.urlTableModel, False) self.mw.web_site_cb.setModelColumn(1) # 如果没有item, setCurrentIndex(0) #qtbug 是不起作用的, index还是-1 self.mw.web_site_cb.setCurrentIndex(0) self.on_web_uname_query() def on_web_uname_query(self, index=None): """ 网址索引改变的时候触发. :return: """ print("查用户") c_url = self.get_c_url() curl_id = self.get_curl_id(c_url) self.mw.web_pwd_le.setText("") # 有结果 if curl_id is not None: print("查到用户") self.unameTableModel.setFilter(f"""user_info.url_id = {curl_id}""") self.reloadTableModel(self.unameTableModel) self.mw.web_user_cb.setCurrentIndex(0) self.on_web_upwd_query() # else: def on_web_upwd_query(self): print("查密码") c_url, c_uname, c_upwd, curl_id = self.get_c_all() self.upwdQueryModel.setQuery( QSqlQuery( self.ORM2SQL( select([UserTable.upwd]).where( and_(UserTable.url_id == curl_id, UserTable.uname == c_uname))))) # 设置密码UI self.upwd_mapper.toFirst() # loadtask self._set_pre_cb_text(c_url, c_uname) # -------------user------------- ↓ def create_or_update_webInfo(self): c_url, c_uname, c_upwd, curl_id = self.get_c_all() if curl_id is None: model = self.urlTableModel row = model.rowCount() ret = model.insertRows(row, 1) print('insertRows=%s' % str(ret), model.columnCount()) record_count = model.record().count() print(record_count) for col in range(model.columnCount()): index = model.index(row, col) # record = model.record(row) # field = record.field(col) field_col = self.urlTableModel.fieldIndex("url") if col == field_col: model.setData(index, c_url) self.urlTableModel.submit() self.mw.web_site_cb.setModelColumn(1) # self.urlTableModel.select() # qt bug # web_obj = WebSite(url=c_url) # self.orm_db.add(web_obj) # self.orm_db.flush() # curl_id = web_obj.id curl_id = model.index(row, 0).data() # 法一: 双主键, 有则更新 , 无则创建 # self.orm_db.merge(User(uname=c_uname, upwd=c_upwd, url_id=curl_id)) # self.orm_db.commit() # 改变索引 # self.reloadTableModel(self.urlTableModel) # self.reloadTableModel(self.unameTableModel) ## 法二 model = self.unameTableModel row = model.rowCount() ret = model.insertRows(row, 1) print('insertRows=%s' % str(ret), model.columnCount()) for col in range(model.columnCount()): index = model.index(row, col) if col == 0: model.setData(index, curl_id) elif col == 1: model.setData(index, c_uname) elif col == 2: model.setData(index, c_upwd) elif col == 3: model.setData(index, "{}") self.unameTableModel.submitAll() def delete_webInfo(self): """ :return: """ c_url, c_uname, c_upwd, curl_id = self.get_c_all() # 找到网址 if curl_id is not None: # 删网址 if c_uname == '' and c_upwd == '': obj = self.orm_db.query(WebSiteTable).filter_by( url=c_url).one() self.orm_db.delete(obj) self.orm_db.commit() self.on_web_site_url_query() # 删账户 else: try: obj = self.orm_db.query(UserTable).filter_by( uname=c_uname, upwd=c_upwd, url_id=curl_id).one() self.orm_db.delete(obj) self.orm_db.commit() self.mw.web_pwd_le.setText("") self.reloadTableModel(self.unameTableModel, False) # self.mw.web_user_cb.setCurrentIndex(0) except: pass # -------------user------------- ↑ # -------------table query------------- ↓ def get_watching(self): # try: WatchIterator = github.get_watching(self) self.mw.thread_pools.spawn(self.get_watching_querydb, WatchIterator) def get_watching_querydb(self, WatchIterator): for watched_repo in WatchIterator: query_field = "/" + watched_repo.full_name WatchingTable_obj: WatchingTable = self.orm_db.query( WatchingTable).filter_by(query_field=query_field).first() if WatchingTable_obj is None: uname = self.get_c_uname() self.orm_db.add( WatchingTable(query_field=query_field, uname=uname)) self.orm_db.commit() model: QSqlTableModel = self.watching_show_model model.select() # TODO:重新载入, 可能可以改成局部刷新. else: pass def del_watching(self, tv, data_dict): data = data_dict.get("data") # type:list del_watching_repo = data[0] print(f"{del_watching_repo}") print(self.get_c_tableName()) if self.get_c_tableName() == WATCHING_TABLE_NAME: print(f"delete") github.del_watching(del_watching_repo) else: pass # TODO:查数据库 ,可能用协程 def re_select_model(self): model = self.get_c_model() model.select() def filterAll(self): """ 过滤 :return: """ query_fields = self.mw.filter_le.text().strip() # 小注释 : e.g."py test " query_field_list = query_fields.split(" ") # 小注释 : space split tableName = self.mw.filter_cb.currentText() # 小注释 : 单表 if tableName != "All": TableClass = getTable(tableName) ##type:BaseComment self.filterModel.setQuery( QSqlQuery( self.ORM2SQL( select([TableClass]).where( or_(*tuple( map( lambda query_field: TableClass.comment. like(f'%{query_field}%'), query_field_list)) # 小注释 : # TableClass.comment.like(f'%{query_field_list[0]}%'), # TableClass.comment.like(f'%{query_field_list[1]}%'), # TableClass.comment.like(f'%{query_field_list[2]}%'), ))))) # 小注释 : 多表 union else: tw = self.mw.repo_tabWidget tabCount = tw.count() end = tabCount - 1 sql = "" for i in range(tabCount): tableName = tw.tabText(i) condition = " OR ".join( list( map( lambda query_field: f"""{tableName}.comment LIKE '%{query_field}%' """, query_field_list))) sql += f"""SELECT * From {tableName} WHERE {condition}""" if i != end: sql += " UNION ALL " if self.mw.filter_ckBox.isChecked( ) else " UNION " # print(sql) self.filterModel.setQuery(QSqlQuery(sql)) # -------------table query------------- ↑ def ORM2SQL(self, statement): """ :param statement: :return: """ query = statement.compile(dialect=sqlite.dialect()) query_str = str(query) query_paras: dict = query.params query_raw_sql = query_str.replace('?', r"%r") % tuple( query_paras.values()) # print("==query_raw_sql==↓:") # print(query_raw_sql) # print("==query_raw_sql==↑:") return query_raw_sql
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 MainWindow(QMainWindow): def __init__(self): super().__init__() layout = QVBoxLayout() form = QFormLayout() self.track_id = QSpinBox() self.track_id.setDisabled(True) self.name = QLineEdit() self.album = QComboBox() self.media_type = QComboBox() self.genre = QComboBox() self.composer = QLineEdit() self.milliseconds = QSpinBox() self.milliseconds.setRange(0, 2147483647) self.milliseconds.setSingleStep(1) self.bytes = QSpinBox() self.bytes.setRange(0, 2147483647) self.bytes.setSingleStep(1) self.unit_price = QDoubleSpinBox() self.unit_price.setRange(0, 999) self.unit_price.setSingleStep(0.01) self.unit_price.setPrefix("$") form.addRow(QLabel("Track ID"), self.track_id) form.addRow(QLabel("Track name"), self.name) form.addRow(QLabel("Composer"), self.composer) form.addRow(QLabel("Milliseconds"), self.milliseconds) form.addRow(QLabel("Bytes"), self.bytes) form.addRow(QLabel("Unit Price"), self.unit_price) self.model = QSqlTableModel(db=db) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.addMapping(self.track_id, 0) self.mapper.addMapping(self.name, 1) self.mapper.addMapping(self.composer, 5) self.mapper.addMapping(self.milliseconds, 6) self.mapper.addMapping(self.bytes, 7) self.mapper.addMapping(self.unit_price, 8) self.model.setTable("Track") self.model.select() self.mapper.toFirst() self.setMinimumSize(QSize(400, 400)) controls = QHBoxLayout() prev_rec = QPushButton("Previous") prev_rec.clicked.connect(self.mapper.toPrevious) next_rec = QPushButton("Next") next_rec.clicked.connect(self.mapper.toNext) save_rec = QPushButton("Save Changes") save_rec.clicked.connect(self.mapper.submit) controls.addWidget(prev_rec) controls.addWidget(next_rec) controls.addWidget(save_rec) layout.addLayout(form) layout.addLayout(controls) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget)
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 APISFilm(QDialog, FORM_CLASS): FIRST, PREV, NEXT, LAST = range(4) OBLIQUE, VERTICAL = range(2) def __init__(self, iface, dbm, imageRegistry, apisLayer, parent=None): """Constructor.""" super(APISFilm, self).__init__(parent) self.iface = iface self.dbm = dbm self.imageRegistry = imageRegistry self.apisLayer = apisLayer self.parent = parent self.setupUi(self) # Initial window size/pos last saved. Use default values for first time if GetWindowSize("film"): self.resize(GetWindowSize("film")) if GetWindowPos("film"): self.move(GetWindowPos("film")) self.printingOptionsDlg = None self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat) self.editMode = False self.addMode = False self.initalLoad = True #self.uiInitalEntryQgsDate.setCalendarPopup(False) #self.uiLastChangesQgsDate.setCalendarPopup(False) # Signals/Slot Connections self.rejected.connect(self.onReject) self.uiOkBtn.clicked.connect(self.onAccept) self.uiCancelBtn.clicked.connect(self.cancelEdit) self.uiSaveBtn.clicked.connect(self.saveEdits) self.uiFilmSelectionBtn.clicked.connect(self.openFilmSelectionDialog) self.uiNewFilmBtn.clicked.connect(self.openNewFilmDialog) self.uiSearchFilmBtn.clicked.connect(self.openSearchFilmDialog) self.uiEditWeatherBtn.clicked.connect(self.openEditWeatherDialog) self.uiExportPdfBtn.clicked.connect(self.exportDetailsPdf) self.uiShowFlightPathBtn.clicked.connect(lambda: self.openFlightPathDialog([self.uiCurrentFilmNumberEdit.text()])) # For LaLe Mode if self.settings.value("APIS/disable_site_and_findspot", "0") != "1": self.uiListSitesOfFilmBtn.setEnabled(True) self.uiListSitesOfFilmBtn.clicked.connect(self.openSiteSelectionListDialog) else: self.uiListSitesOfFilmBtn.setEnabled(False) self.uiListImagesOfFilmBtn.clicked.connect(self.openImageSelectionListDialog) self.uiExtractGpsFromImagesBtn.clicked.connect(self.extractGpsFromImages) self.uiWeatherCodeEdit.textChanged.connect(self.generateWeatherCode) self.uiFilmModeCombo.currentIndexChanged.connect(self.onFilmModeChanged) self.uiEditProjectTableBtn.clicked.connect(lambda: self.openSystemTableEditorDialog("projekt", self.uiProjectSelectionCombo)) self.uiEditCopyrightTableBtn.clicked.connect(lambda: self.openSystemTableEditorDialog("copyright", self.uiCopyrightCombo)) # self.uiEditProjectTableBtn.clicked.connect(lambda: VersionToCome()) # self.uiEditCopyrightTableBtn.clicked.connect(lambda: VersionToCome()) # init Project Btn self.uiAddProjectBtn.clicked.connect(self.addProject) self.uiRemoveProjectBtn.clicked.connect(self.removeProject) # Setup Sub-Dialogs self.filmSelectionDlg = APISFilmNumberSelection(self) self.newFilmDlg = APISFilmNew(parent=self) self.searchFilmDlg = APISFilmSearch(self.dbm, self) # (self.iface, self.dbm) self.editWeatherDlg = APISWeather(self.iface, self.dbm, self) self.flightPathDlg = APISFlightPath(self.iface, self.dbm, self.apisLayer, self) self.siteSelectionListDlg = APISSiteSelectionList(self.iface, self.dbm, self.imageRegistry, self.apisLayer, self) self.imageSelectionListDlg = APISImageSelectionList(self.iface, self.dbm, self.imageRegistry, self.apisLayer, parent=self) self.systemTableEditorDlg = None # Setup film model self.model = QSqlRelationalTableModel(self, self.dbm.db) self.model.setTable("film") self.model.select() while (self.model.canFetchMore()): self.model.fetchMore() self.setupMapper() self.setupComboBox(self.uiProjectSelectionCombo, "projekt", 0, None) self.setupComboBox(self.newFilmDlg.uiProducerCombo, "hersteller", 2, None) self.setupNavigation() self.mapper.toFirst() self.initalLoad = False def setupMapper(self): self.mapper = QDataWidgetMapper(self) self.mapper.currentIndexChanged.connect(self.onCurrentIndexChanged) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setItemDelegate(FilmDelegate()) self.mapper.setModel(self.model) self.mandatoryEditors = [self.uiImageCountEdit, self.uiCameraCombo, self.uiFilmMakeCombo, self.uiFilmModeCombo] self.disableEditorsIfOblique = [self.uiCameraNumberEdit, self.uiCalibratedFocalLengthEdit] # LineEdits & PlainTextEdits self.intValidator = QIntValidator() self.doubleValidator = QDoubleValidator() self.lineEditMaps = { "filmnummer": { "editor": self.uiCurrentFilmNumberEdit }, "hersteller": { "editor": self.uiProducerEdit }, "anzahl_bilder": { "editor": self.uiImageCountEdit, "validator": self.intValidator }, "militaernummer": { "editor": self.uiMilitaryNumberEdit }, "militaernummer_alt": { "editor": self.uiOldMilitaryNumberEdit }, "form1": { "editor": self.uiFormat1Edit }, "form2": { "editor": self.uiFormat2Edit }, "kalibrierungsnummer": { "editor": self.uiCameraNumberEdit }, "kammerkonstante": { "editor": self.uiCalibratedFocalLengthEdit, "validator": self.doubleValidator }, "kassettennummer": { "editor": self.uiCassetteEdit }, "art_ausarbeitung": { "editor": self.uiFilmMakeEdit }, "fotograf": { "editor": self.uiPhotographerEdit }, "pilot": { "editor": self.uiPilotEdit }, "flugzeug": { "editor": self.uiAirplaneEdit }, "abflug_flughafen": { "editor": self.uiDepartureAirportEdit }, "ankunft_flughafen": { "editor": self.uiArrivalAirportEdit }, "flugzeit": { "editor": self.uiFlightDurationEdit }, "wetter": { "editor": self.uiWeatherCodeEdit }, "kommentar": { "editor": self.uiCommentsPTxt } } for key, item in self.lineEditMaps.items(): self.mapper.addMapping(item["editor"], self.model.fieldIndex(key)) if "validator" in item: item["editor"].setValidator(item["validator"]) #item["editor"].textChanged.connect(partial(self.onLineEditChanged, item["editor"])) item["editor"].textChanged.connect(self.onLineEditChanged) #Text #self.mapper.addMapping(self.uiCommentsPTxt, self.model.fieldIndex("kommentar")) # Date and Times self.mapper.addMapping(self.uiFlightDate, self.model.fieldIndex("flugdatum")) #self.mapper.addMapping(self.uiFlightQgsDate, self.model.fieldIndex("flugdatum")) self.mapper.addMapping(self.uiInitalEntryDate, self.model.fieldIndex("datum_ersteintrag")) #self.mapper.addMapping(self.uiInitalEntryQgsDate, self.model.fieldIndex("datum_ersteintrag")) self.mapper.addMapping(self.uiLastChangesDate, self.model.fieldIndex("datum_aenderung")) #self.mapper.addMapping(self.uiLastChangesQgsDate, self.model.fieldIndex("datum_aenderung")) self.mapper.addMapping(self.uiDepartureTime, self.model.fieldIndex("abflug_zeit")) self.mapper.addMapping(self.uiArrivalTime, self.model.fieldIndex("ankunft_zeit")) self.uiDepartureTime.timeChanged.connect(self.onFlightTimeChanged) self.uiArrivalTime.timeChanged.connect(self.onFlightTimeChanged) # ComboBox without Model self.mapper.addMapping(self.uiFilmModeCombo, self.model.fieldIndex("weise")) self.uiFilmModeCombo.editTextChanged.connect(self.onLineEditChanged) completer = QCompleter(self.uiFilmModeCombo.model()) self.uiFilmModeCombo.setCompleter(completer) self.uiFilmModeCombo.lineEdit().setValidator(InListValidator([self.uiFilmModeCombo.itemText(i) for i in range(self.uiFilmModeCombo.count())], self.uiFilmModeCombo.lineEdit(), None, self)) # ComboBox with Model self.comboBoxMaps = { "archiv": { "editor": self.uiArchiveCombo, "table": "hersteller", "modelcolumn": 2, "depend": None }, "kamera": { "editor": self.uiCameraCombo, "table": "kamera", "modelcolumn": 0, "depend": [{"form1": self.uiFormat1Edit}, {"form2": self.uiFormat2Edit}] }, "filmfabrikat": { "editor": self.uiFilmMakeCombo, "table": "film_fabrikat", "modelcolumn": 0, "depend": [{"art": self.uiFilmMakeEdit}] }, "target": { "editor": self.uiTargetCombo, "table": "target", "modelcolumn": 0, "depend": None }, "copyright": { "editor": self.uiCopyrightCombo, "table": "copyright", "modelcolumn": 0, "depend": None } } for key, item in self.comboBoxMaps.items(): self.mapper.addMapping(item["editor"], self.model.fieldIndex(key)) self.setupComboBox(item["editor"], item["table"], item["modelcolumn"], item["depend"]) item["editor"].editTextChanged.connect(self.onLineEditChanged) self.mapper.addMapping(self.uiProjectList, self.model.fieldIndex("projekt")) def fixComboBoxDropDownListSizeAdjustemnt(self, cb): scroll = 0 if cb.count() <= cb.maxVisibleItems() else QApplication.style().pixelMetric(QStyle.PM_ScrollBarExtent) iconWidth = cb.iconSize().width() max = 0 for i in range(cb.count()): width = cb.view().fontMetrics().width(cb.itemText(i)) if max < width: max = width QMessageBox.information(self, "info", "scroll: {0}, max: {1}, icon: {2}".format(scroll, max, iconWidth)) #cb.view().setMinimumWidth(scroll + max) def setupComboBox(self, editor, table, modelColumn, depend): model = QSqlRelationalTableModel(self, self.dbm.db) model.setTable(table) model.removeColumn(0) model.select() tv = QTableView() editor.setView(tv) tv.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) tv.setSelectionMode(QAbstractItemView.SingleSelection) tv.setSelectionBehavior(QAbstractItemView.SelectRows) tv.setAutoScroll(False) editor.setModel(model) editor.setModelColumn(modelColumn) editor.setInsertPolicy(QComboBox.NoInsert) tv.resizeColumnsToContents() tv.resizeRowsToContents() tv.verticalHeader().setVisible(False) tv.horizontalHeader().setVisible(True) #tv.setMinimumWidth(tv.horizontalHeader().length()) tv.horizontalHeader().setStretchLastSection(True) #tv.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) tv.resizeColumnsToContents() scroll = 0 if editor.count() <= editor.maxVisibleItems() else QApplication.style().pixelMetric(QStyle.PM_ScrollBarExtent) tv.setMinimumWidth(tv.horizontalHeader().length() + scroll) #self.fixComboBoxDropDownListSizeAdjustemnt(editor) #editor.resize(tv.horizontalHeader().sizeHint()) completer = QCompleter(editor.model()) editor.setCompleter(completer) #editor.setAutoCompletion(True) editor.lineEdit().setValidator(InListValidator([editor.itemText(i) for i in range(editor.count())], editor.lineEdit(), depend, self)) if depend: editor.currentIndexChanged.connect(partial(self.updateDepends, editor, depend)) def updateDepends(self, editor, depend): for dep in depend: for key, value in dep.items(): idx = editor.model().createIndex(editor.currentIndex(), editor.model().fieldIndex(key)) value.setText(str(editor.model().data(idx))) #QMessageBox.warning(None, "Test", str(idx)) def setupNavigation(self): self.uiFirstFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.FIRST)) self.uiPreviousFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.PREV)) self.uiNextFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.NEXT)) self.uiLastFilmBtn.clicked.connect(partial(self.loadRecordByNavigation, APISFilm.LAST)) self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.intRecordValidator = QIntValidator(1, self.model.rowCount()) self.uiCurrentFilmCountEdit.setValidator(self.intRecordValidator) self.uiCurrentFilmCountEdit.setText(str(self.mapper.currentIndex() + 1)) self.uiCurrentFilmCountEdit.editingFinished.connect(lambda: self.loadRecordById(int(self.uiCurrentFilmCountEdit.text()) - 1)) # QMessageBox.warning(None, "Test", str(self.mapper.itemDelegate())) def enableItemsInLayout(self, layout, enable): for i in range(layout.count()): if layout.itemAt(i).widget(): layout.itemAt(i).widget().setEnabled(enable) def loadRecordByNavigation(self, mode): #self.mapper.submit() #self.submitChanges() self.initalLoad = True if mode == APISFilm.FIRST: self.mapper.toFirst() elif mode == APISFilm.PREV: self.mapper.toPrevious() elif mode == APISFilm.NEXT: self.mapper.toNext() elif mode == APISFilm.LAST: self.mapper.toLast() self.initalLoad = False def loadRecordById(self, id): #self.submitChanges self.initalLoad = True self.mapper.setCurrentIndex(id) self.initalLoad = False def loadRecordByKeyAttribute(self, attribute, value): #self.model.setFilter(attribute + " = '" + value + "'") #self.model.select() # self.mapper.toFirst() query = QSqlQuery(self.dbm.db) #qryStr = "select {0} from film where {0} = '{1}' limit 1".format(attribute, value) #qryStr = "SELECT rowid FROM film WHERE {0} = '{1}' limit 1".format(attribute, value) qryStr = "SELECT" \ " (SELECT COUNT(*)" \ " FROM film AS t2" \ " WHERE t2.rowid < t1.rowid" \ " ) + (" \ " SELECT COUNT(*)" \ " FROM film AS t3" \ " WHERE t3.rowid = t1.rowid AND t3.rowid < t1.rowid" \ " ) AS rowNum" \ " FROM film AS t1" \ " WHERE {0} = '{1}'" \ " ORDER BY t1.rowid ASC".format(attribute, value) query.exec_(qryStr) #QMessageBox.warning(None, "Test", str(query.size()) + ',' + str(query.numRowsAffected())) query.first() fn = query.value(0) if fn is not None: self.loadRecordById(fn) return True else: # Film does not exist QMessageBox.warning(None, "Film Nummer", str("Der Film mit der Nummer {0} existiert nicht!".format(value))) return False #self.model.setFilter('') #self.model.select() #while (self.model.canFetchMore()): #self.model.fetchMore() def submitChanges(self): self.mapper.submit() def onCurrentIndexChanged(self): self.uiCurrentFilmCountEdit.setText(str(self.mapper.currentIndex() + 1)) self.onFilmModeChanged() def onFlightTimeChanged(self): dTime = self.uiDepartureTime.time() aTime = self.uiArrivalTime.time() flightDuration = dTime.secsTo(aTime) self.uiFlightDurationEdit.setText(str(flightDuration / 60)) def disableIfOblique(self, isOblique): for editor in self.disableEditorsIfOblique: editor.setDisabled(isOblique) def onFilmModeChanged(self): if self.uiFilmModeCombo.currentText() == u'schräg': self.disableIfOblique(True) else: self.disableIfOblique(False) def onLineEditChanged(self): sender = self.sender() if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: sender.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(sender.metaObject().className())) self.editorsEdited.append(sender) def onComboBoxChanged(self, editor): pass def addProject(self): editor = self.uiProjectList value = self.uiProjectSelectionCombo.currentText() notInList = True for row in range(editor.count()): if value == editor.item(row).data(0): notInList = False break if notInList: editor.addItem(value) editor.sortItems() if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) def removeProject(self): editor = self.uiProjectList editor.takeItem(self.uiProjectList.currentRow()) if not self.editMode and not self.initalLoad: self.startEditMode() if not self.initalLoad: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) def onAccept(self): ''' Check DB Save options when pressing OK button Update Plugin Status ''' self.accept() def onReject(self): ''' Run some actions when the user closes the dialog ''' if self.editMode: res = self.cancelEdit() if res: self.close() else: self.show() else: self.close() def closeEvent(self, e): # Write window size and position to QSettings if self.editMode: self.onReject() else: SetWindowSizeAndPos("film", self.size(), self.pos()) e.accept() def extractGpsFromImages(self): key = self.uiCurrentFilmNumberEdit.text() e2p = Exif2Points(self.iface, key) layer = e2p.run() if layer: self.apisLayer.requestShapeFile(layer, groupName="Flugwege", addToCanvas=True) def exportDetailsPdf(self): if self.printingOptionsDlg is None: self.printingOptionsDlg = APISPrintingOptions(self) self.printingOptionsDlg.setWindowTitle("Druck Optionen: Film") self.printingOptionsDlg.configure(False, False, visPersonalDataChk=True) self.printingOptionsDlg.show() if self.printingOptionsDlg.exec_(): printPersonalData = self.printingOptionsDlg.printPersonalData() APISPrinterQueue([{'type': APISTemplatePrinter.FILM, 'idList': [self.uiCurrentFilmNumberEdit.text()], 'options': {'personalData': printPersonalData}}], OutputMode.MergeNone, openFile=self.printingOptionsDlg.uiOpenFilesChk.isChecked(), openFolder=self.printingOptionsDlg.uiOpenFolderChk.isChecked(), dbm=self.dbm, parent=self) def openSearchFilmDialog(self): """Run method that performs all the real work""" # show the dialog self.searchFilmDlg.show() #self.filmSelectionDlg.uiFilmNumberEdit.setFocus() # Run the dialog event loop and See if OK was pressed if self.searchFilmDlg.exec_(): # QMessageBox.warning(None, "FilmNumber", self.searchFilmDlg.generateSearchQuery()) model = QSqlRelationalTableModel(self, self.dbm.db) model.setTable("film") searchMode, searchFilter = self.searchFilmDlg.generateSearchFilter() # QMessageBox.information(self, "info", searchFilter) model.setFilter(searchFilter) model.select() rc = model.rowCount() while (model.canFetchMore()): model.fetchMore() rc = model.rowCount() query = QSqlQuery(self.dbm.db) searchQuery = "select filmnummer, substr(filmnummer, 3, 8) as 'ohne_hersteller', flugdatum, anzahl_bilder, weise, art_ausarbeitung, militaernummer, militaernummer_alt from film where {0}".format(searchFilter) query.exec_(searchQuery) querySize = 0 while(query.next()): querySize += 1 query.seek(-1) # if model.rowCount(): if querySize > 0: # open film selection list dialog searchListDlg = APISFilmSelectionList(self.iface, model, self.dbm, self.imageRegistry, parent=self) searchListDlg.uiFilmCountLbl.setText(str(rc)) searchListDlg.uiFilmCountDescriptionLbl.setText(u"Film gefunden" if model.rowCount() == 1 else u"Filme gefunden") searchListDlg.uiFilmSearchModeLbl.setText(searchMode) res = searchListDlg.loadFilmListBySqlQuery(query) if res and searchListDlg.exec_(): #QMessageBox.warning(None, "FilmNumber", unicode(searchListDlg.filmNumberToLoad)) self.loadRecordByKeyAttribute("filmnummer", searchListDlg.filmNumberToLoad) else: QMessageBox.warning(self, u"Film Suche", u"Keine Ergebnisse mit den angegebenen Suchkriterien.") self.openSearchFilmDialog() #QMessageBox.warning(None, "FilmNumber", u"{0}, rows: {1}".format(self.searchFilmDlg.generateSearchQuery(), model.rowCount())) # Get Search String/Query #if not self.loadRecordByKeyAttribute("filmnummer", self.filmSelectionDlg.filmNumber()): #self.openFilmSelectionDialog() def openFilmSelectionDialog(self): """Run method that performs all the real work""" self.filmSelectionDlg.show() self.filmSelectionDlg.uiFilmNumberEdit.setFocus() if self.filmSelectionDlg.exec_(): if not self.loadRecordByKeyAttribute("filmnummer", self.filmSelectionDlg.filmNumber()): self.openFilmSelectionDialog() def openNewFilmDialog(self): """Run method that performs all the real work""" self.newFilmDlg.show() if self.newFilmDlg.exec_(): self.addNewFilm(self.newFilmDlg.flightDate(), self.newFilmDlg.useLastEntry(), self.newFilmDlg.producer(), self.newFilmDlg.producerCode()) def openEditWeatherDialog(self): self.editWeatherDlg.setWeatherCode(self.uiWeatherCodeEdit.text()) self.editWeatherDlg.show() if self.editWeatherDlg.exec_(): self.uiWeatherCodeEdit.setText(self.editWeatherDlg.weatherCode()) #self.uiWeatherPTxt.setPlainText(self.editWeatherDlg.weatherDescription()) def generateWeatherCode(self): weatherDescription = self._generateWeatherCode(self.uiWeatherCodeEdit.text()) self.uiWeatherPTxt.setPlainText(weatherDescription) def _generateWeatherCode(self, weatherCode): categories = ["Low Cloud Amount", "Visibility Kilometres", "Low Cloud Height", "Weather", "Remarks Mission", "Remarks Weather"] query = QSqlQuery(self.dbm.db) pos = 0 help = 0 weatherDescription = "" for c in weatherCode: qryStr = "select description from wetter where category = '{0}' and code = '{1}' limit 1".format(categories[pos - help], c) query.exec_(qryStr) query.first() fn = query.value(0) if pos <= 5: weatherDescription += categories[pos] + ': ' + fn if pos < 5: weatherDescription += '\n' else: weatherDescription += '; ' + fn if pos >= 5: help += 1 pos += 1 return weatherDescription def openSystemTableEditorDialog(self, table, updateEditor): if self.dbm: self.systemTableEditorDlg = APISAdvancedInputDialog(self.dbm, table, False, parent=self) if self.systemTableEditorDlg.tableExists: if self.systemTableEditorDlg.exec_(): # See if OK was pressed # rec = self.systemTableEditorDlg.getRecord() # Update updateEditor # self.setupComboBox(self.uiProjectSelectionCombo, "projekt", 0, None) self.updateComboBox(updateEditor) else: QMessageBox.warning(self, "Tabelle nicht vorhanden", "Die Tabelle {0} ist in der APIS Datenbank nicht vorhanden".format(table)) else: QMessageBox.warning(self, "Warning Database", "Die APIS Datenbank konnte nicht gefunden werden.") def updateComboBox(self, updateEditor): updateEditor.model().select() tv = updateEditor.view() tv.resizeRowsToContents() tv.resizeColumnsToContents() scroll = 0 if updateEditor.count() <= updateEditor.maxVisibleItems() else QApplication.style().pixelMetric( QStyle.PM_ScrollBarExtent) tv.setMinimumWidth(tv.horizontalHeader().length() + scroll) updateEditor.setCurrentIndex(updateEditor.count() - 1) if updateEditor.validator(): updateEditor.lineEdit().setValidator( InListValidator([updateEditor.itemText(i) for i in range(updateEditor.count())], updateEditor.lineEdit(), None, self)) def openFlightPathDialog(self, filmList, toClose=None): self.flightPathDlg.viewFilms(filmList) # DEBUG self.flightPathDlg.show() if self.flightPathDlg.exec_(): #TODO load Data in TOC, Close Windows if toClose: toClose.close() self.close() def openSiteSelectionListDialog(self): if self.uiFilmModeCombo.currentIndex() == APISFilm.VERTICAL: fromTable = "luftbild_senk_fp" elif self.uiFilmModeCombo.currentIndex() == APISFilm.OBLIQUE: fromTable = "luftbild_schraeg_fp" query = QSqlQuery(self.dbm.db) query.prepare("SELECT fundortnummer, flurname, katastralgemeinde, fundgewinnung, sicherheit FROM fundort WHERE fundortnummer IN (SELECT DISTINCT fo.fundortnummer FROM fundort fo, {0} WHERE fo.geometry IS NOT NULL AND {0}.geometry IS NOT NULL AND {0}.filmnummer = '{1}' AND Intersects({0}.geometry, fo.geometry) AND fo.ROWID IN (SELECT ROWID FROM SpatialIndex WHERE f_table_name = 'fundort' AND search_frame = {0}.geometry)) ORDER BY katastralgemeindenummer, land, fundortnummer_nn".format(fromTable, self.uiCurrentFilmNumberEdit.text())) query.exec_() info = u"gefunden, die vom Film {0} abgedeckt/geschnitten werden.".format(self.uiCurrentFilmNumberEdit.text()) res = self.siteSelectionListDlg.loadSiteListBySpatialQuery(query, info) if res: self.siteSelectionListDlg.show() if self.siteSelectionListDlg.exec_(): pass def openImageSelectionListDialog(self): if self.uiFilmModeCombo.currentIndex() == APISFilm.VERTICAL: fromTable = 'luftbild_senk_cp' spatialIndicator = 'massstab' elif self.uiFilmModeCombo.currentIndex() == APISFilm.OBLIQUE: fromTable = 'luftbild_schraeg_cp' spatialIndicator = 'radius' query = QSqlQuery(self.dbm.db) query.prepare("SELECT cp.bildnummer AS bildnummer, cp.filmnummer AS filmnummer, cp.{2} AS mst_radius, f.weise AS weise, f.art_ausarbeitung AS art FROM {0} AS cp, film AS f WHERE cp.filmnummer = '{1}' AND f.filmnummer = '{1}'".format(fromTable, self.uiCurrentFilmNumberEdit.text(), spatialIndicator)) query.exec_() res = self.imageSelectionListDlg.loadImageListBySqlQuery(query) if res: self.imageSelectionListDlg.show() if self.imageSelectionListDlg.exec_(): pass def addNewFilm(self, flightDate, useLastEntry, producer, producerCode): self.initalLoad = True self.addMode = True self.startEditMode() row = self.model.rowCount() self.mapper.submit() while (self.model.canFetchMore()): self.model.fetchMore() self.model.insertRow(row) if useLastEntry: #copy last row for c in range(1, self.model.columnCount()): value = self.model.data(self.model.createIndex(row - 1, c)) self.model.setData(self.model.createIndex(row, c), value) editor = self.mapper.mappedWidgetAt(c) if editor and not (value == 'NULL' or value == ''): cName = editor.metaObject().className() if (cName == "QLineEdit" or cName == "QDateEdit") and editor.isReadOnly(): pass else: editor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(editor.metaObject().className())) self.editorsEdited.append(editor) self.mapper.setCurrentIndex(row) self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.uiFlightDate.setDate(flightDate) #self.uiFlightQgsDate.setDate(flightDate) self.uiProducerEdit.setText(producer) self.uiArchiveCombo.lineEdit().setText(producer) if not useLastEntry: self.uiWeatherCodeEdit.setText("9990X") #now = QDateTime.currentDateTime() now = QDate.currentDate() self.uiInitalEntryDate.setDate(now) #self.uiInitalEntryQgsDate.setDateTime(now) self.uiLastChangesDate.setDate(now) #self.uiLastChangesQgsDate.setDateTime(now) self.uiFilmModeCombo.setEnabled(True) #Filmnummer hh = producerCode yyyy = flightDate.toString("yyyy") mm = flightDate.toString("MM") query = QSqlQuery(self.dbm.db) qryStr = "select max(filmnummer_nn) from film where filmnummer_hh_jjjj_mm = '{0}{1}{2}' limit 1".format(hh, yyyy, mm) query.exec_(qryStr) query.first() fn = query.value(0) if isinstance(fn, int): nn = str(fn + 1).zfill(2) else: nn = "01" self.uiCurrentFilmNumberEdit.setText("{0}{1}{2}{3}".format(hh, yyyy, mm, nn)) self.initalLoad = False def removeNewFilm(self): self.initalLoad = True row = self.mapper.currentIndex() self.model.removeRow(row) self.model.submitAll() while (self.model.canFetchMore()): self.model.fetchMore() self.uiTotalFilmCountLbl.setText(str(self.model.rowCount())) self.mapper.toLast() self.initalLoad = False def saveEdits(self): # Check Mandatory fields flag = False for mEditor in self.mandatoryEditors: cName = mEditor.metaObject().className() if cName == 'QLineEdit': value = mEditor.text() elif cName == 'QComboBox': value = mEditor.lineEdit().text() if value.strip() == "": flag = True mEditor.setStyleSheet("{0} {{background-color: rgb(240, 160, 160);}}".format(cName)) if mEditor not in self.editorsEdited: self.editorsEdited.append(mEditor) else: if mEditor in self.editorsEdited: mEditor.setStyleSheet("{0} {{background-color: rgb(153, 204, 255);}}".format(cName)) #else: #mEditor.setStyleSheet("") if flag: QMessageBox.warning(self, self.tr(u"Benötigte Felder Ausfüllen"), self.tr(u"Füllen Sie bitte alle Felder aus, die mit * gekennzeichnet sind.")) return False #saveToModel currIdx = self.mapper.currentIndex() #now = QDateTime.currentDateTime() now = QDate.currentDate() self.uiLastChangesDate.setDate(now) #self.uiLastChangesQgsDate.setDateTime(now) res = self.mapper.submit() if not res: sqlError = self.mapper.model().lastError() QMessageBox.information(self, "Submit", u"Error: {0}, {1}".format(res, sqlError.text())) while (self.model.canFetchMore()): self.model.fetchMore() self.mapper.setCurrentIndex(currIdx) self.endEditMode() return True def cancelEdit(self): if self.editMode: result = QMessageBox.question(self, self.tr(u"Änderungen wurden vorgenommen!"), self.tr(u"Möchten Sie die Änerungen speichern?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) #save or not save if result == QMessageBox.Yes: res = self.saveEdits() if res: return True else: return False elif result == QMessageBox.No: if self.addMode: self.removeNewFilm() self.mapper.setCurrentIndex(self.mapper.currentIndex()) self.endEditMode() return True def startEditMode(self): self.editMode = True self.enableItemsInLayout(self.uiTopHorizontalLayout, False) self.enableItemsInLayout(self.uiBottomHorizontalLayout, False) self.uiOkBtn.setEnabled(False) self.uiSaveBtn.setEnabled(True) self.uiCancelBtn.setEnabled(True) self.editorsEdited = [] def endEditMode(self): self.editMode = False self.addMode = False self.enableItemsInLayout(self.uiTopHorizontalLayout, True) self.enableItemsInLayout(self.uiBottomHorizontalLayout, True) self.uiOkBtn.setEnabled(True) self.uiSaveBtn.setEnabled(False) self.uiCancelBtn.setEnabled(False) for editor in self.editorsEdited: cName = editor.metaObject().className() if (cName == "QLineEdit" or cName == "QDateEdit") and editor.isReadOnly(): editor.setStyleSheet("{0} {{background-color: rgb(218, 218, 218);}}".format(cName)) else: editor.setStyleSheet("") self.editorsEdited = [] self.uiFilmModeCombo.setEnabled(False) def showEvent(self, evnt): # QMessageBox.information(self, "info", "db requires update: {0}".format(self.dbm.dbRequiresUpdate)) if self.dbm.dbRequiresUpdate: self.initalLoad = True for editor in [self.uiArchiveCombo, self.uiCameraCombo, self.uiFilmMakeCombo, self.uiCopyrightCombo, self.uiProjectSelectionCombo, self.newFilmDlg.uiProducerCombo]: self.updateComboBox(editor) currIdx = self.mapper.currentIndex() self.model.select() while (self.model.canFetchMore()): self.model.fetchMore() self.mapper.setCurrentIndex(currIdx) self.dbm.dbRequiresUpdate = False self.initalLoad = False
class QmyMainWindow(QMainWindow): def __init__(self, parent=None, dbFilename='None'): super().__init__(parent) # Call the parent class. Create window. self.ui = Ui_MainWindow() # Create UI object self.ui.setupUi(self) # Create UI interface self.ui.tabWidget.setVisible(True) self.setCentralWidget(self.ui.tabWidget) # tableView setting self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(44) self.ui.tableView.horizontalHeader().setDefaultSectionSize(70) self.ui.tableView.setSortingEnabled(True) # Initialize chart self.__iniPieChart() # Initialize pie chart self.__iniStackedBar() # Stacked bar mpl.rcParams['font.sans-serif'] = ['Calibri'] mpl.rcParams['font.size'] = 8 # Choose the SQLITE database drive self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) # Set the name of database if self.DB.open(): # Open database self.__openTable() # Open tables else: QMessageBox.warning(self, "Warning", "Failed to open the database.") # ==============Self-defined Fuctions============ def __getFieldNames(self): # Get names of all fields. # Get empty records, only field name. emptyRec = self.tabModel.record() self.fldNum = {} # Dictionary of field name and index. for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i def __openTable(self): # Open the table of database self.tabModel = QSqlTableModel(self, self.DB) # Data model # Set the table of database TODO: Can user insert their own tables from # the database? self.tabModel.setTable("battery") # Data storage,OnManualSubmit , OnRowChange self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) # self.tabModel.setSort( # self.tabModel.fieldIndex("RANDOM"), # Qt.DescendingOrder) # Sorting if (self.tabModel.select() == False): # Failed to query the data QMessageBox.critical( self, "Wrong", "Something wrong. Failed to open the database\n" + self.tabModel.lastError().text()) return # self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.__getFieldNames() # Get the field name and index # Field name shown for i in self.fldNum: self.tabModel.setHeaderData(self.fldNum[i], Qt.Horizontal, i.capitalize()) # Create mappings between interface widget and the field name of data model self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) # Setting data model self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # The relations between interface widget and field name of tabModel self.mapper.addMapping(self.ui.dbEditValue, self.fldNum["Value"]) self.mapper.addMapping(self.ui.dbEditRunit, self.fldNum["Raw_unit"]) self.mapper.addMapping(self.ui.dbComboProperty, self.fldNum["Property"]) self.mapper.addMapping(self.ui.dbEditUnit, self.fldNum["Unit"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbEditEname, self.fldNum["Extracted_name"]) self.mapper.addMapping(self.ui.dbEditRvalue, self.fldNum["Raw_value"]) self.mapper.addMapping(self.ui.dbEditDOI_2, self.fldNum["DOI"]) self.mapper.addMapping(self.ui.dbEditDOI_4, self.fldNum["Date"]) self.mapper.addMapping(self.ui.dbEditDOI_3, self.fldNum["Title"]) self.mapper.addMapping(self.ui.dbEditDOI, self.fldNum["Journal"]) self.mapper.addMapping(self.ui.dbEditTag, self.fldNum["Tag"]) self.mapper.addMapping(self.ui.dbEditInfo, self.fldNum["Info"]) self.mapper.addMapping(self.ui.dbEditType, self.fldNum["Type"]) self.mapper.addMapping(self.ui.dbEditWarning, self.fldNum["Warning"]) self.mapper.addMapping(self.ui.dbEditSpecifier, self.fldNum["Specifier"]) self.mapper.toFirst() # Move to the first record self.selModel = QItemSelectionModel(self.tabModel) # Select model self.selModel.currentChanged.connect( self.do_currentChanged) # Trigger when the current changed self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # Trigger when the current row changed self.ui.tableView.setModel(self.tabModel) # Setting the data model self.ui.tableView.setSelectionModel( self.selModel) # Setting the selection model # self.ui.tableView.setColumnHidden(self.fldNum["TAG"], True) #Hide columns # self.ui.tableView.setColumnHidden(self.fldNum["INFO"], True) #Hide # columns self.ui.tableView.setColumnHidden(self.fldNum["Extracted_name"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Unit"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Num_records"], True) # Hide columns # self.ui.tableView.setColumnHidden( # self.fldNum["RAW_VALUE"], True) # Hide columns # Update the conditions of actions of interface widget self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.btnDrawPieChart.setEnabled(True) # Pie Chart self.ui.spinPieSize.setEnabled(True) self.ui.spinHoleSize.setEnabled(True) self.ui.chkBox_PieLegend.setEnabled(True) self.ui.generateButton_3.setEnabled(True) self.ui.searchButton_3.setEnabled(True) self.ui.clearButton_3.setEnabled(True) self.ui.frame_4.setEnabled(True) self.ui.frame_3.setEnabled(True) self.ui.searchInput.returnPressed.connect(self.ui.searchButton.click) self.ui.searchInput_3.returnPressed.connect( self.ui.searchButton_3.click) def __iniPieChart(self): # Initialize pie chart chart = QChart() # chart.setTitle("Piechart") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewPie.setChart(chart) # Setting chart for chart view self.ui.chartViewPie.setRenderHint(QPainter.Antialiasing) self.ui.chartViewPie.setCursor(Qt.CrossCursor) # Setting cross cursor def __iniStackedBar(self): # Initialize stacked bar chart chart = QChart() # chart.setTitle("Number of property records for each chemical") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewStackedBar.setChart(chart) # Set chart self.ui.chartViewStackedBar.setRenderHint(QPainter.Antialiasing) self.ui.chartViewStackedBar.setCursor(Qt.CrossCursor) # Set mouse # ==========Table tab slot function================== @pyqtSlot() # Save changes def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "Information", "Failed to store the changes. Wrong information. \n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Cancel changes def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Add records def on_actRecAppend_triggered(self): self.tabModel.insertRow( self.tabModel.rowCount(), QModelIndex()) # Add one record in the last row curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) # Create ModelIndex of the last row self.selModel.clearSelection() # Clear selections # Choosing the current row when selection. self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() # Get current row @pyqtSlot() # Insert records def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() # QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # Clear selections self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() # Delete records def on_actRecDelete_triggered(self): # Get the current index of current model curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row(), QModelIndex()) # Delete the current row self.tabModel.submit() @pyqtSlot() # Help message box def on_actHelp_triggered(self): msg = QMessageBox() msg.about( self, "Help", '<div>' '<h3>Table: Query the database according to data types and name or DOI.</h3>' '<ul>' '<li>Search the exact compound name in "<em>Exact Match</em>". Search the element or part of compound name in "<em>Generic Match</em>". </li>' '<li>Click the "<em>Home</em>" or "<em>All</em>" button to view the full database</li>' '<li>Refer to "<em>Correctness</em>" column for database evaluation details. </li>' '<li>You can add your own database entries using the "<em>Insert</em>" tab; click "<em>Save</em>" to save the changes.</li>' '</ul>' '<h3>Figure: Statistical analysis of the database.</h3>' '<ul>' '<li><em>Pie chart</em> shows the proportion of data records.</li>' '<li><em>Stacked bar chart</em> shows the data types for each compound. First input the compound name, click "<em>Add</em>" and then "<em>Generate</em>" data for overview. </li>' '<li><em>Histogram </em>shows the distribution of each data type.</li>' '<li><em>Venn diagram</em> shows the correlation of each data type.</li>' '</ul>' '</div>') @pyqtSlot() # Filtering def on_radioBtnVoltage_clicked(self): flag, sqlmerge = self.merged_or_not() print(sqlmerge) self.tabModel.setFilter("PROPERTY LIKE 'Voltage' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnCoulombic_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Coulombic Efficiency' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnConductivity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Conductivity' AND %s" % sqlmerge) @pyqtSlot() # Filetering def on_radioBtnCapacity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Capacity' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnEnergy_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Energy' AND %s" % sqlmerge) @pyqtSlot() # Cancel Filetering def on_radioBtnAll_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("%s" % sqlmerge) # print(self.tabModel.filter()) # self.tabModel.select() def get_elements(self): element_dic = { "Hydrogen": "H", "Helium": "He", "Lithium": "Li", "Beryllium": "Be", "Boron": "B", "Carbon": "C", "Nitrogen": "N", "Oxygen": "O", "Fluorine": "F", "Neon": "Ne", "Sodium": "Na", "Magnesium": "Mg", "Aluminum": "Al", "Silicon": "Si", "Phosphorus": "P", "Sulfur": "S", "Chlorine": "Cl", "Argon": "Ar", "Potassium": "K", "Calcium": "Ca", "Scandium": "Sc", "Titanium": "Ti", "Vanadium": "V", "Chromium": "Cr", "Manganese": "Mn", "Iron": "Fe", "Cobalt": "Co", "Nickel": "Ni", "Copper": "Cu", "Zinc": "Zn", "Gallium": "Ga", "Germanium": "Ge", "Arsenic": "As", "Selenium": "Se", "Bromine": "Br", "Krypton": "Kr", "Rubidium": "Rb", "Strontium": "Sr", "Yttrium": "Y", "Zirconium": "Zr", "Niobium": "Nb", "Molybdenum": "Mo", "Technetium": "Tc", "Ruthenium": "Ru", "Rhodium": "Rh", "Palladium": "Pd", "Silver": "Ag", "Cadmium": "Cd", "Indium": "In", "Tin": "Sn", "Antimony": "Sb", "Tellurium": "Te", "Iodine": "I", "Xenon": "Xe", "Cesium": "Cs", "Barium": "Ba", "Lanthanum": "La", "Cerium": "Ce", "Praseodymium": "Pr", "Neodymium": "Nd", "Promethium": "Pm", "Samarium": "Sm", "Europium": "Eu", "Gadolinium": "Gd", "Terbium": "Tb", "Dysprosium": "Dy", "Holmium": "Ho", "Erbium": "Er", "Thulium": "Tm", "Ytterbium": "Yb", "Lutetium": "Lu", "Hafnium": "Hf", "Tantalum": "Ta", "Tungsten": "W", "Rhenium": "Re", "Osmium": "Os", "Iridium": "Ir", "Platinum": "Pt", "Gold": "Au", "Mercury": "Hg", "Thallium": "Tl", "Lead": "Pb", "Bismuth": "Bi", "Polonium": "Po", "Astatine": "At", "Radon": "Rn", "Francium": "Fr", "Radium": "Ra", "Actinium": "Ac", "Thorium": "Th", "Protactinium": "Pa", "Uranium": "U", "Neptunium": "Np", "Plutonium": "Pu", "Americium": "Am", "Curium": "Cm", "Berkelium": "Bk", "Californium": "Cf", "Einsteinium": "Es", "Fermium": "Fm", "Mendelevium": "Md", "Nobelium": "No", "Lawrencium": "Lr", "Rutherfordium": "Rf", "Dubnium": "Db", "Seaborgium": "Sg", "Bohrium": "Bh", "Hassium": "Hs", "Meitnerium": "Mt", "Darmstadtium": "Ds", "Roentgenium": "Rg", "Copernicium": "Cn", "Nihonium": "Nh", "Flerovium": "Fl", "Moscovium": "Mc", "Livermorium": "Lv", "Tennessine": "Ts", "Oganesson": "Og" } return element_dic def merged_or_not(self): flag = self.ui.mergeBox.isChecked() if flag: sqlmerge = "NUM_RECORDS NOT LIKE 'NONE'" else: sqlmerge = "NUM_RECORDS LIKE 'NONE'" return flag, sqlmerge @pyqtSlot() def on_homeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.ui.mergeBox.setChecked(False) @pyqtSlot() def on_mergeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS NOT LIKE 'NONE'") self.ui.mergeBox.setChecked(True) @pyqtSlot() def on_searchButton_clicked(self): searchtext = self.ui.searchInput.text() searchclass = self.ui.searchClass.currentText() matchtype = self.ui.matchType.currentText() flag, sqlmerge = self.merged_or_not() if searchclass == "DOI": if matchtype == "Exact Match": self.tabModel.setFilter("DOI LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("DOI LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == "Warning": if matchtype == "Exact Match": self.tabModel.setFilter("WARNING LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("WARNING LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == 'Name': try: searchtext = self.get_elements()[searchtext.capitalize()] except BaseException: pass if matchtype == "Exact Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter("%s LIKE '%s' " % (searchclass, searchtext)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'VOLTAGE' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CAPACITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CONDUCTIVITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'COULOMBIC EFFICIENCY' AND %s " % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'ENERGY'AND %s " % (searchclass, searchtext, sqlmerge)) elif matchtype == "Generic Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter( "(EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s') AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such compounds in the database. Please search new compounds." ) # ============Picture Tab 1, Pie Chart===================== @pyqtSlot() # Draw the pie chart def on_btnDrawPieChart_clicked(self): self.draw_pieChart() @pyqtSlot(float) # Set holeSize def on_spinHoleSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setHoleSize(arg1) @pyqtSlot(float) # Set pieSize def on_spinPieSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setPieSize(arg1) @pyqtSlot(bool) # Set legend checkbox def on_chkBox_PieLegend_clicked(self, checked): self.ui.chartViewPie.chart().legend().setVisible(checked) def pie_data( self): # Return a list of property name and the number of property num_list = [] pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for i in range(len(pro_list)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT(NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro_list[i]) # Query database while query.next(): num_value = query.value(0) # Returned value for each query item = self.ui.treeWidget_2.topLevelItem(i) # The ith row # The 2nd column item.setText(1, str(num_value)) item.setTextAlignment(1, Qt.AlignHCenter) num_list.append(num_value) return pro_list, num_list def draw_pieChart(self): # Draw the pie chart chart = self.ui.chartViewPie.chart() chart.legend().setAlignment(Qt.AlignRight) # AlignRight,AlignBottom chart.removeAllSeries() # Delete all series seriesPie = QPieSeries() # Pie chart series seriesPie.setHoleSize(self.ui.spinHoleSize.value()) # Hole size seriesPie.setPieSize(self.ui.spinPieSize.value()) # Pie size sec_count = 5 # Number of properties seriesPie.setLabelsVisible(True) # Label pro, num = self.pie_data() for i in range(sec_count): seriesPie.append(pro[i], num[i]) seriesPie.setLabelsVisible(True) # Label # Pie hoverd when mouse selected seriesPie.hovered.connect(self.do_pieHovered) chart.addSeries(seriesPie) chart.setTitle("Proportion of data records for each property") font = QFont() font.setPointSize(12) font.setWeight(75) chart.setTitleFont(font) font = QFont() font.setPointSize(12) font.setBold(False) font.setWeight(35) legend = chart.legend() legend.setFont(font) # =========Picture tab 2. StackedBar========= @pyqtSlot() # Draw StackedBar def on_btnStackedBar_clicked(self): self.draw_stackedBar() @pyqtSlot() # Draw horizontal StackedBar def on_btnStackedBarH_clicked(self): self.draw_stackedBar(False) # Search button in the Stacked bar chart tab. Add name to the first column # of the table. @pyqtSlot() def on_searchButton_3_clicked(self): searchtext = self.ui.searchInput_3.text() current_index = self.ui.stackedWidget.topLevelItemCount() item_0 = QtWidgets.QTreeWidgetItem(self.ui.stackedWidget) item_0.setText(0, searchtext) @pyqtSlot() # Generate data using the input chemical names def on_generateButton_3_clicked(self): self.ui.zoominButton.setEnabled(True) self.ui.zoomoutButton.setEnabled(True) self.ui.originalButton.setEnabled(True) self.ui.btnStackedBar.setEnabled(True) # Stacked bar chart self.ui.btnStackedBarH.setEnabled(True) current_index = self.ui.stackedWidget.topLevelItemCount() chemical_list = [ self.ui.stackedWidget.topLevelItem(index).text(0) for index in range(current_index) ] # Get a list of the inputed chemical name pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for row, chemical in enumerate(chemical_list): for index, pro in enumerate(pro_list): query = QSqlQuery( db=self.DB, query= "SELECT SUM(NUM_RECORDS) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NAME LIKE '%s' " % (pro, chemical)) while query.next(): num_value = query.value(0) print(num_value) if num_value == "": num_value = 0 item = self.ui.stackedWidget.topLevelItem( row) # The row'th row item.setText(index + 1, str(num_value)) item.setTextAlignment(index + 1, Qt.AlignHCenter) @pyqtSlot() # Clear button def on_clearButton_3_clicked(self): self.ui.stackedWidget.clear() @pyqtSlot() # Zoom in def on_zoominButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(1.2) @pyqtSlot() # Zoom out def on_zoomoutButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(0.8) @pyqtSlot() # Reset original size def on_originalButton_clicked(self): self.ui.chartViewStackedBar.chart().zoomReset() def draw_stackedBar(self, isVertical=True): # Stacked bar chart chart = self.ui.chartViewStackedBar.chart() chart.removeAllSeries() # Remove all series chart.removeAxis(chart.axisX()) # remove axis chart.removeAxis(chart.axisY()) if isVertical: # Vertical chart.setTitle("Number of property records for each chemical ") chart.legend().setAlignment(Qt.AlignBottom) else: # Horizontal chart.setTitle("Number of property records for each chemical") chart.legend().setAlignment(Qt.AlignRight) # Create data sets setCapacity = QBarSet("Capacity") setConductivity = QBarSet("Conductivity") setCoulombic = QBarSet("Coulombic") setEnergy = QBarSet("Energy") setVoltage = QBarSet("Voltage") chemical_Count = self.ui.stackedWidget.topLevelItemCount() nameList = [] # Chemical lists for i in range(chemical_Count): item = self.ui.stackedWidget.topLevelItem(i) # print(item.text(1)) nameList.append(item.text(0)) setCapacity.append(float(item.text(1))) setConductivity.append(float(item.text(2))) setCoulombic.append(float(item.text(3))) setEnergy.append(float(item.text(4))) setVoltage.append(float(item.text(5))) # Create series if isVertical: seriesBar = QStackedBarSeries() else: seriesBar = QHorizontalStackedBarSeries() seriesBar.append(setCapacity) seriesBar.append(setConductivity) seriesBar.append(setCoulombic) seriesBar.append(setEnergy) seriesBar.append(setVoltage) seriesBar.setLabelsVisible(True) # Show labels for each bar seriesBar.setLabelsFormat("@value") seriesBar.setLabelsPosition(QAbstractBarSeries.LabelsCenter) seriesBar.setBarWidth(0.3) chart.addSeries(seriesBar) axisStud = QBarCategoryAxis() # Category axis axisStud.append(nameList) axisStud.setRange(nameList[0], nameList[chemical_Count - 1]) axisValue = QValueAxis() # Value axis # axisValue.setRange(0, 300) axisValue.setTitleText("Number of records") # axisValue.setTickCount(6) axisValue.applyNiceNumbers() if isVertical: chart.setAxisX(axisStud, seriesBar) chart.setAxisY(axisValue, seriesBar) else: chart.setAxisY(axisStud, seriesBar) chart.setAxisX(axisValue, seriesBar) for marker in chart.legend().markers(): # QLegendMarker lists marker.clicked.connect(self.do_LegendMarkerClicked) # =========Picture tab 3. Histogram========= @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_2_clicked(self, checked): self.ui.widgetHist_2.setToolbarVisible(checked) @pyqtSlot() # Draw def on_histButton_clicked(self): self.__drawHist() def hist_data(self, pro): query = QSqlQuery( db=self.DB, query= "SELECT VALUE FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro) data = [] while query.next(): num_value = query.value(0) data.append(num_value) return data def __drawHist(self): # Histogram pro = self.ui.propertycomboBox_2.currentText() data = self.hist_data(pro) self.ui.widgetHist_2.figure.clear() # Clear figure ax = self.ui.widgetHist_2.figure.add_subplot(1, 1, 1) if pro == 'Capacity': M, bins, patches = ax.hist( [5000 if float(i) > 4999 else float(i) for i in data], bins='auto', color='darkgreen', alpha=0.5, rwidth=1) ax.set_xlim(0, 5001) # ax.set_ylim(0,15000) ax.tick_params(labelsize=12) ax.set_xticklabels(['0', '1000', '2000', '3000', '4000', '5000+']) ax.set_xlabel('Capacity (mAh/g)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Capacity Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Voltage": count1 = [] for i in data: count1.append(float(i)) n, bins, patches = ax.hist(x=count1, bins='auto', range=(0, 8), color='r', alpha=0.5, rwidth=1) ax.set_xticklabels(['0', '1', '2', '3', '4', '5', '6', '7', '8+']) ax.set_xlim(0, 8) ax.tick_params(labelsize=12) ax.set_xlabel('Voltage (V)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Voltage Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Energy": n, bins, patches = ax.hist( [3000 if float(i) > 2999 else float(i) for i in data], bins='auto', color='y', alpha=0.5, rwidth=1) ax.set_xticklabels( ['0', '500', '1000', '1500', '2000', '2500', '3000+']) ax.set_xlim(0, 3001) ax.tick_params(labelsize=12) ax.set_xlabel('Energy (Wh/kg)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Energy Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Coulombic Efficiency": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins='auto', color='c', alpha=0.5, rwidth=1) ax.set_xlim(0, 100) ax.tick_params(labelsize=12) ax.set_xlabel('Coulombic Effciency (%)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Coulombic Effciency Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Conductivity": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins=np.logspace( np.log10(1e-15), np.log10(1)), color='k', alpha=0.5, rwidth=1) ax.set_xlim(1e-20, 10) ax.set_xscale('log') ax.tick_params(labelsize=12) ax.set_xlabel('log10 (Conductivity (S/cm))', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Conductivity Distrbution', fontsize=14) ax.figure.canvas.draw() # =========Picture tab 4. Venn diagram========= @pyqtSlot() # Draw def on_btnVenn_clicked(self): self.__drawVenn() @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_3_clicked(self, checked): self.ui.widgetVenn.setToolbarVisible(checked) def __drawVenn(self): self.ui.widgetVenn.figure.clear() # Clear figure pro_list = [ "Capacity", "Conductivity", "Coulombic Efficiency", "Energy", "Voltage" ] data_dic = {} color_dic = dict(zip(pro_list, ['C0', 'C2', 'C6', 'C8', 'C9'])) for i in pro_list: query = QSqlQuery( db=self.DB, query= "SELECT COUNT(DISTINCT NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % i) while query.next(): num_value = query.value(0) data_dic[i] = num_value for i, combo in enumerate(itertools.combinations(pro_list, 2)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT() FROM (SELECT DISTINCT NAME AS PRO1 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') INNER JOIN (SELECT DISTINCT NAME AS PRO2 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') ON PRO1 = PRO2" % combo) while query.next(): num = query.value(0) x3 = num x1 = data_dic[combo[0]] - x3 x2 = data_dic[combo[1]] - x3 hf = self.ui.widgetVenn.figure # hf.set_figheight(30) # hf.set_figwidth(30) # print(dir(hf)) hf.set_size_inches((10, 10)) ax1 = hf.add_subplot(5, 2, i + 1) v = venn2(subsets=(x1, x2, x3), set_labels=(combo[0], combo[1]), ax=ax1) v.get_patch_by_id('A').set_alpha(1) v.get_patch_by_id('A').set_color(color_dic[combo[0]]) v.get_patch_by_id('B').set_color(color_dic[combo[1]]) ax1.figure.canvas.draw() # =============Self-defined slot function=============================== # Update the conditions of actPost and actCancel def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled( self.tabModel.isDirty()) # Use when not saving changes self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): # Control during row changes self.ui.actRecDelete.setEnabled(current.isValid()) # Update current row index of mapping self.mapper.setCurrentIndex(current.row()) # Get current record,QSqlRecord curRec = self.tabModel.record(current.row()) def do_pieHovered(self, pieSlice, state): # Mouse move in and out in the pie chart pieSlice.setExploded(state) # Pop-up animation if state: # Show the tab of percentages self.__oldLabel = pieSlice.label() # Save original labels pieSlice.setLabel(self.__oldLabel + ": %.1f%%" % (pieSlice.percentage() * 100)) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) else: # show original labels pieSlice.setLabel(self.__oldLabel) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) def do_LegendMarkerClicked(self): # Click legend marker marker = self.sender() # QLegendMarker marker.series().setVisible(not marker.series().isVisible()) marker.setVisible(True) alpha = 1.0 if not marker.series().isVisible(): alpha = 0.5 brush = marker.labelBrush() # QBrush color = brush.color() # QColor color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) pen = marker.pen() # QPen color = pen.color() color.setAlphaF(alpha) pen.setColor(color) marker.setPen(pen)
class 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 StudentEditView(QWidget, Ui_FormEditDeleteStudent): def __init__(self, student_model, student_controller, main_window): super().__init__() self.setupUi(self) self.student_model: StudentModel = student_model self.student_controller: StudentController = student_controller self.main_window: MainWindowView = main_window self.mapper = QDataWidgetMapper() self.set_student_editor() def show_editor(self, s=None): i = self.is_selected() if i & i >= 0: self.mapper.setCurrentIndex(i) self.show() def clear_fields(self): self.lineEditStudentID.clear() self.lineEditStudentAge.clear() self.lineEditStudentName.clear() self.lineEditStudentGrade.clear() def is_selected(self, s=None) -> int: # print(self.selection_model.hasSelection()) # print(self.selection_model.isRowSelected(0)) index = self.main_window.tblStudents.selectionModel().currentIndex( ).row() return index def set_student_editor(self, index=0): self.btnEditStudentPrevious.clicked.connect(self.mapper.toPrevious) self.btnEditStudentNext.clicked.connect(self.mapper.toNext) self.btnEditStudentSave.clicked.connect(self.update_button_action) self.btnEditStudentDelete.clicked.connect(self.delete_button_action) self.mapper.setModel(self.main_window.student_sql_query_model) self.mapper.addMapping(self.lineEditStudentID, 0) self.mapper.addMapping(self.lineEditStudentName, 1) self.mapper.addMapping(self.lineEditStudentGrade, 2) self.mapper.addMapping(self.lineEditStudentAge, 3) # self.set_student_table(self.model) # if index > 0: # self.mapper.setCurrentIndex(index) self.mapper.toFirst() def update_button_action(self, saved): student_id = int(self.lineEditStudentID.text()) student_name = self.lineEditStudentName.text() student_grade = self.lineEditStudentGrade.text() student_age = self.lineEditStudentAge.text() student: Student = Student(student_name, student_age, student_grade) self.student_controller.update_student(student, student_id) def delete_button_action(self): dialog_alert_view: DialogAlertView = DialogAlertView() student_id = int(self.lineEditStudentID.text()) dialog_alert_view.show_dialog(True, 4) self.student_controller.delete_student(student_id)
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 QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.splitter) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败") 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.mapper = QDataWidgetMapper() self.mapper.setModel(self.qryModel) 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): index = self.mapper.currentIndex() curIndex = self.qryModel.index(index, 1) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) 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) @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()) empNo = curRec.value("EmpNo") query = QSqlQuery(self.DB) query.prepare( '''SELECT EmpNo, Memo, Photo FROM employee WHERE EmpNo = :ID''') query.bindValue(":ID", empNo) if not query.exec(): 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 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 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 MyMainWindow(VCPMainWindow): """Main window class for the VCP.""" def __init__(self, *args, **kwargs): super(MyMainWindow, self).__init__(*args, **kwargs) self.formNextBtn.clicked.connect(self.formNext) self.formPreviousBtn.clicked.connect(self.formPrevious) self.classNextBtn.clicked.connect(self.classNext) self.classPreviousBtn.clicked.connect(self.classPrevious) self.sizeNextBtn.clicked.connect(self.sizeNext) self.sizePreviousBtn.clicked.connect(self.sizePrevious) if not self.open_db(): print('Failed to Open Database') self.formModelInit() #self.sizeModelInit() def open_db(self): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName(current_path + 'sfc.db') db.open() return db def formModelInit(self): self.formMapper = QDataWidgetMapper(self) self.formModel = QSqlQueryModel(self) self.formModel.setQuery('SELECT DISTINCT form FROM threads') self.formMapper.setModel(self.formModel) self.formMapper.addMapping(self.formLbl, 0, b'text') self.formMapper.currentIndexChanged.connect(self.formChanged) self.formMapper.toLast() self.formsLast = self.formMapper.currentIndex() self.formMapper.toFirst() self.formIndexLbl.setText('{}'.format(self.formMapper.currentIndex())) self.classModelInit() def formNext(self): #print('before {}'.format(self.mapper.currentIndex())) if self.formMapper.currentIndex() != self.formsLast: self.formMapper.toNext() else: self.formMapper.toFirst() self.formIndexLbl.setText('{}'.format(self.formMapper.currentIndex())) #print('after {}'.format(self.mapper.currentIndex())) self.classModelInit() def formPrevious(self): if self.formMapper.currentIndex() != 0: self.formMapper.toPrevious() else: self.formMapper.toLast() self.formIndexLbl.setText('{}'.format(self.formMapper.currentIndex())) self.classModelInit() def formChanged(self): # is fired before the change is complete pass def classModelInit(self): print('class update') self.classMapper = QDataWidgetMapper(self) self.classModel = QSqlQueryModel(self) form = self.formLbl.text() print(form) classSelect = "SELECT DISTINCT class FROM threads WHERE form = '{}'".format( form) self.classModel.setQuery(classSelect) self.classMapper.setModel(self.classModel) self.classMapper.addMapping(self.classLbl, 0, b'text') #self.classMapper.currentIndexChanged.connect(self.formChanged) self.classMapper.toLast() self.classLast = self.classMapper.currentIndex() self.classMapper.toFirst() self.classIndexLbl.setText('{}'.format( self.classMapper.currentIndex())) self.sizeModelInit() def classNext(self): #print('before {}'.format(self.mapper.currentIndex())) if self.classMapper.currentIndex() != self.classLast: self.classMapper.toNext() else: self.classMapper.toFirst() self.classIndexLbl.setText('{}'.format( self.classMapper.currentIndex())) #print('after {}'.format(self.mapper.currentIndex())) self.sizeModelInit(self.sizeMapper.currentIndex()) def classPrevious(self): if self.classMapper.currentIndex() != 0: self.classMapper.toPrevious() else: self.classMapper.toLast() self.classIndexLbl.setText('{}'.format( self.classMapper.currentIndex())) self.sizeModelInit(self.sizeMapper.currentIndex()) def sizeModelInit(self, index=0): self.sizeMapper = QDataWidgetMapper(self) self.sizeModel = QSqlQueryModel(self) form = str(self.formLbl.text()) fit = self.classLbl.text() sizeSelect = "SELECT * FROM threads WHERE form = '{}' AND class = '{}'".format( form, fit) self.sizeModel.setQuery(sizeSelect) self.sizeMapper.setModel(self.sizeModel) self.sizeMapper.addMapping(self.threadLbl, 0, b'text') self.sizeMapper.addMapping(self.pitchLbl, 3, b'text') self.sizeMapper.addMapping(self.majorDiameterLbl, 4, b'text') self.sizeMapper.addMapping(self.maxMajorDiameterLbl, 5, b'text') self.sizeMapper.addMapping(self.minMajorDiameterLbl, 6, b'text') self.sizeMapper.addMapping(self.pitchDiameterLbl, 7, b'text') self.sizeMapper.addMapping(self.maxPitchDiameterLbl, 8, b'text') self.sizeMapper.addMapping(self.minPitchDiameterLbl, 9, b'text') self.sizeMapper.addMapping(self.minMinorDiameterLbl, 10, b'text') self.sizeMapper.currentIndexChanged.connect(self.formChanged) self.sizeMapper.toLast() self.sizeLast = self.sizeMapper.currentIndex() self.sizeMapper.setCurrentIndex(index) #self.sizeMapper.toFirst() self.sizeIndexLbl.setText('{}'.format(self.sizeMapper.currentIndex())) def sizeNext(self): #print('before {}'.format(self.mapper.currentIndex())) if self.sizeMapper.currentIndex() != self.sizeLast: self.sizeMapper.toNext() else: self.sizeMapper.toFirst() self.sizeIndexLbl.setText('{}'.format(self.sizeMapper.currentIndex())) #print('after {}'.format(self.mapper.currentIndex())) def sizePrevious(self): if self.sizeMapper.currentIndex() != 0: self.sizeMapper.toPrevious() else: self.sizeMapper.toLast() self.sizeIndexLbl.setText('{}'.format(self.sizeMapper.currentIndex()))