class InvoiceForm(QWidget): submitted = Signal(dict) def __init__(self, parent=None): # + parent=None super().__init__(parent) # + parent self.setLayout(QFormLayout()) self.inputs = dict() self.inputs['Customer Name'] = QLineEdit() self.inputs['Customer Address'] = QPlainTextEdit() self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True) self.inputs['Days until Due'] = QSpinBox() for label, widget in self.inputs.items(): self.layout().addRow(label, widget) self.line_items = QTableWidget(rowCount=10, columnCount=3) self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours']) self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.layout().addRow(self.line_items) for row in range(self.line_items.rowCount()): for col in range(self.line_items.columnCount()): if col > 0: w = QSpinBox() self.line_items.setCellWidget(row, col, w) submit = QPushButton('Create Invoice', clicked=self.on_submit) # + vvvvvv vvvvvvvvvvvvv _print = QPushButton('Print Invoice', clicked=self.window().printpreviewDialog) # + _print, + self.window() self.layout().addRow(submit, _print) # + _print def on_submit(self): data = {'c_name': self.inputs['Customer Name'].text(), 'c_addr': self.inputs['Customer Address'].toPlainText(), 'i_date': self.inputs['Invoice Date'].date().toString(), 'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(), 'i_terms': '{} days'.format(self.inputs['Days until Due'].value()), 'line_items': list()} for row in range(self.line_items.rowCount()): if not self.line_items.item(row, 0): continue job = self.line_items.item(row, 0).text() rate = self.line_items.cellWidget(row, 1).value() hours = self.line_items.cellWidget(row, 2).value() total = rate * hours row_data = [job, rate, hours, total] if any(row_data): data['line_items'].append(row_data) data['total_due'] = sum(x[3] for x in data['line_items']) self.submitted.emit(data) # remove everything else in this function below this point # + return data # +++
class PeopleTab(QWidget): def __init__(self, parent): QWidget.__init__(self) self.Parent = parent self.PeoplTitle = 'PEOPLE' self.UI() @property def Title(self): return self.PeoplTitle def UI(self): self.widgets() self.layouts() self.funcDisplayPeople() def widgets(self): # People widgets ########################################################### # Top layout (search people) widgets self.searchPeopleText = QLabel("Search: ") self.searchPeopleEntry = QLineEdit() self.searchPeopleEntry.setPlaceholderText("Search people..") self.searchPeopleBtn = QPushButton("Search") self.searchPeopleBtn.clicked.connect(self.searchPeople) self.refreshPeopleBtn = QPushButton("Refresh") self.refreshPeopleBtn.clicked.connect(self.funcDisplayPeople) # Middle layout (list people) widgets with radio buttons self.allPeopleRadioBtn = QRadioButton("All people") self.employeesPeopleRadioBtn = QRadioButton("Employees") self.contractorsPeopleRadioBtn = QRadioButton("Contractors") self.subcontractorsPeopleRadioBtn = QRadioButton("Subcontractors") self.listPeopleBtn = QPushButton("List") self.listPeopleBtn.clicked.connect(self.funcListPeople) # Bottom layout widget, a table showing people self.peopleTable = QTableWidget() self.peopleTable.verticalHeader().hide() self.peopleTable.setSortingEnabled(True) self.peopleTable.setShowGrid(False) self.peopleTable.verticalHeader().setDefaultSectionSize(90) self.peopleTable.setColumnCount(10) # self.peopleTable.setColumnHidden(0, True) self.peopleTable.setHorizontalHeaderItem(0, QTableWidgetItem("")) self.peopleTable.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.peopleTable.setHorizontalHeaderItem(1, QTableWidgetItem("Photo")) self.peopleTable.horizontalHeader().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.peopleTable.setHorizontalHeaderItem(2, QTableWidgetItem("ID")) self.peopleTable.setHorizontalHeaderItem( 3, QTableWidgetItem("First name")) self.peopleTable.horizontalHeader().setSectionResizeMode( 3, QHeaderView.Stretch) self.peopleTable.setHorizontalHeaderItem(4, QTableWidgetItem("Last name")) self.peopleTable.horizontalHeader().setSectionResizeMode( 4, QHeaderView.Stretch) self.peopleTable.setHorizontalHeaderItem(5, QTableWidgetItem("Title")) self.peopleTable.setHorizontalHeaderItem(6, QTableWidgetItem("Phone")) self.peopleTable.horizontalHeader().setSectionResizeMode( 6, QHeaderView.Stretch) self.peopleTable.setHorizontalHeaderItem(7, QTableWidgetItem("Email")) self.peopleTable.horizontalHeader().setSectionResizeMode( 7, QHeaderView.Stretch) self.peopleTable.setHorizontalHeaderItem(8, QTableWidgetItem("Location")) self.peopleTable.setHorizontalHeaderItem( 9, QTableWidgetItem("Employment type")) self.peopleTable.horizontalHeader().setSectionResizeMode( 9, QHeaderView.ResizeToContents) # Double clicking a row opens a window with person details self.peopleTable.doubleClicked.connect(self.selectedPerson) # Buttons for actions on selected people self.addPerson = QPushButton("Add") self.addPerson.clicked.connect(self.funcAddPerson) self.viewPerson = QPushButton("View/Edit") self.viewPerson.clicked.connect(self.selectedPerson) self.deletePerson = QPushButton("Delete") self.deletePerson.clicked.connect(self.funcDeletePerson) self.exportPeopleCSVBtn = QPushButton("Export CSV") self.exportPeopleCSVBtn.setEnabled(False) self.exportPeopleCSVBtn.clicked.connect(self.funcPeopleToCSV) self.exportPeopleXLSXBtn = QPushButton("Export XLSX") self.exportPeopleXLSXBtn.setEnabled(False) self.exportPeopleXLSXBtn.clicked.connect(self.funcPeopleToXLSX) self.exportPeoplePDFBtn = QPushButton("Export PDF") self.exportPeoplePDFBtn.setEnabled(False) self.exportPeoplePDFBtn.clicked.connect(self.funcPeopleToPdf) def layouts(self): # People layouts ########################################################### self.peopleMainLayout = QVBoxLayout() self.peopleMainTopLayout = QHBoxLayout() self.peopleTopLeftLayout = QHBoxLayout() self.peopleTopRightLayout = QHBoxLayout() # self.peopleMainMiddleLayout = QHBoxLayout() self.peopleMainBottomLayout = QHBoxLayout() self.peopleBottomRightLayout = QVBoxLayout() self.peopleBottomLeftLayout = QHBoxLayout() # Groupboxes allows customization using CSS-like syntax # self.peopleTopGroupBox = QGroupBox() # self.peopleTopGroupBoxRightFiller = QGroupBox() # self.peopleMiddleGroupBox = QGroupBox() # self.peopleMiddleGroupBoxRightFiller = QGroupBox() self.peopleTopLeftGroupBox = QGroupBox() self.peopleTopRightGroupBox = QGroupBox() self.peopleTopGroupBox = QGroupBox() self.peopleBottomGroupBox = QGroupBox() self.peopleBottomLeftGroupBox = QGroupBox() self.peopleBottomRightGroupBox = QGroupBox() self.peopleBottomRightGroupBox.setStyleSheet( 'QGroupBox {margin-top: 0px;}') self.peopleBottomRightGroupBoxFiller = QGroupBox() self.peopleBottomRightGroupBoxFiller.setStyleSheet( styles.groupBoxFillerStyle()) # Top layout (search box) widgets self.peopleTopLeftLayout.addWidget(self.searchPeopleText, 10) self.peopleTopLeftLayout.addWidget(self.searchPeopleEntry, 30) self.peopleTopLeftLayout.addWidget(self.searchPeopleBtn, 10) self.peopleTopLeftLayout.addItem( QSpacerItem(70, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.peopleTopLeftLayout.addWidget(self.refreshPeopleBtn, 10) self.peopleTopLeftGroupBox.setLayout(self.peopleTopLeftLayout) # Middle layout (list box) widgets self.peopleTopRightLayout.addWidget(self.allPeopleRadioBtn) self.peopleTopRightLayout.addWidget(self.employeesPeopleRadioBtn) self.peopleTopRightLayout.addWidget(self.contractorsPeopleRadioBtn) self.peopleTopRightLayout.addWidget(self.subcontractorsPeopleRadioBtn) self.peopleTopRightLayout.addWidget(self.listPeopleBtn) self.peopleTopRightGroupBox.setLayout(self.peopleTopRightLayout) self.peopleMainTopLayout.addWidget(self.peopleTopLeftGroupBox, 60) self.peopleMainTopLayout.addWidget(self.peopleTopRightGroupBox, 40) # Bottom layout (table with issues) widgets # Bottom left layout with table self.peopleBottomLeftLayout.addWidget(self.peopleTable) self.peopleBottomLeftGroupBox.setLayout(self.peopleBottomLeftLayout) # Bottom right layout with buttons self.peopleBottomRightLayout.addWidget(self.addPerson, 5) self.peopleBottomRightLayout.addWidget(self.viewPerson, 5) self.peopleBottomRightLayout.addWidget(self.deletePerson, 5) self.peopleBottomRightLayout.addWidget( self.peopleBottomRightGroupBoxFiller, 70) self.peopleBottomRightLayout.addWidget(self.exportPeopleCSVBtn, 5) self.peopleBottomRightLayout.addWidget(self.exportPeopleXLSXBtn, 5) self.peopleBottomRightLayout.addWidget(self.exportPeoplePDFBtn, 5) self.peopleBottomRightGroupBox.setLayout(self.peopleBottomRightLayout) self.peopleMainBottomLayout.addWidget(self.peopleTable, 90) self.peopleMainBottomLayout.addWidget(self.peopleBottomRightGroupBox, 10) # self.peopleMainLayout.addWidget(self.peopleTopGroupBox, 10) # self.peopleMainLayout.addWidget(self.peopleMiddleGroupBox, 10) # self.peopleMainLayout.addLayout(self.peopleMainBottomLayout, 80) self.peopleMainLayout.addLayout(self.peopleMainTopLayout, 10) self.peopleMainLayout.addLayout(self.peopleMainBottomLayout, 90) self.setLayout(self.peopleMainLayout) @Slot() def funcDisplayPeople(self): for i in reversed(range(self.peopleTable.rowCount())): self.peopleTable.removeRow(i) cur = db.cur people = cur.execute("SELECT * FROM people") for row_data in people: row_number = self.peopleTable.rowCount() self.peopleTable.insertRow(row_number) # Add checkboxes to the table widget = QWidget() checkBox = QCheckBox() checkBox.setCheckState(Qt.Unchecked) checkBox.stateChanged.connect(self.funcActivateBtnsWithCheckbox) hBoxLayout = QHBoxLayout(widget) hBoxLayout.addWidget(checkBox) hBoxLayout.setAlignment(Qt.AlignCenter) self.peopleTable.setCellWidget(row_number, 0, widget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add photo photos_thumbnails to the table thumbWidget = QWidget() pic = QPixmap(str(row_data[10])) thumbLabel = QLabel() thumbLabel.setPixmap(pic) thumbLayout = QHBoxLayout(thumbWidget) thumbLayout.addWidget(thumbLabel) self.peopleTable.setCellWidget(row_number, 1, thumbWidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Fill the rest of the data for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem("PRN#" + str(data))) else: self.peopleTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) self.peopleTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.peopleTable.setSelectionBehavior(QTableView.SelectRows) @Slot() def funcActivateBtnsWithCheckbox(self): indices = self.funcPeopleCheckBox() if self.sender().isChecked() or indices: self.exportPeopleCSVBtn.setEnabled(True) self.exportPeopleXLSXBtn.setEnabled(True) self.exportPeoplePDFBtn.setEnabled(True) else: self.exportPeopleCSVBtn.setEnabled(False) self.exportPeopleXLSXBtn.setEnabled(False) self.exportPeoplePDFBtn.setEnabled(False) @Slot() def funcAddPerson(self): self.newPerson = AddPerson(self) self.newPerson.setObjectName("add_person_popup") self.newPerson.setStyleSheet(styles.addPopups()) @Slot() def funcPeopleCheckBox(self): checked_list = [] for i in range(self.peopleTable.rowCount()): if self.peopleTable.cellWidget(i, 0).findChild(type( QCheckBox())).isChecked(): item = self.peopleTable.item(i, 2).text() checked_list.append(item.lstrip("PRN#")) return checked_list @Slot() def selectedPerson(self): self.displayPerson = DisplayPerson(self) self.displayPerson.show() @Slot() def funcDeletePerson(self): indices = self.funcPeopleCheckBox() mbox = QMessageBox.question( self, "Warning", "Are you sure you want to delete this person?", QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel) if (mbox == QMessageBox.Yes): if indices: try: for index in range(len(indices)): query = "DELETE FROM people WHERE person_id = ?" db.cur.execute(query, (indices[index], )) db.conn.commit() QMessageBox.information(self, "Info", "Selected people were deleted") self.funcDisplayPeople() except: QMessageBox.information(self, "Info", "No changes made") else: row = self.peopleTable.currentRow() personId = self.peopleTable.item(row, 0).text() personId = personId.lstrip("PRN#") try: query = "DELETE FROM people WHERE person_id = ?" db.cur.execute(query, (personId, )) db.conn.commit() QMessageBox.information(self, "Info", "Person was deleted") self.funcDisplayPeople() except: QMessageBox.information(self, "Info", "No changes made") self.displayPerson.close() @Slot() def funcListPeople(self): if self.allPeopleRadioBtn.isChecked(): self.funcDisplayPeople() elif self.employeesPeopleRadioBtn.isChecked(): try: query = "SELECT * FROM people WHERE person_empl_type = 'Employee'" people = db.cur.execute(query).fetchall() for i in reversed(range(self.peopleTable.rowCount())): self.peopleTable.removeRow(i) for row_data in people: row_number = self.peopleTable.rowCount() self.peopleTable.insertRow(row_number) # Add checkboxes to the table widget = QWidget() checkBox = QCheckBox() checkBox.setCheckState(Qt.Unchecked) hBoxLayout = QHBoxLayout(widget) hBoxLayout.addWidget(checkBox) hBoxLayout.setAlignment(Qt.AlignCenter) self.peopleTable.setCellWidget(row_number, 0, widget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add photo photos_thumbnails to the table thumbWidget = QWidget() pic = QPixmap( "assets/media/people-media/photos_thumbnails/01Aug2020_18h01mtrtgzteuzuspxrp_thumbnail.png" ) thumbLabel = QLabel() thumbLabel.setPixmap(pic) thumbLayout = QHBoxLayout(thumbWidget) thumbLayout.addWidget(thumbLabel) self.peopleTable.setCellWidget(row_number, 1, thumbWidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Fill the rest of the data for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem("PRN#" + str(data))) else: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") elif self.contractorsPeopleRadioBtn.isChecked(): try: query = "SELECT * FROM people WHERE person_empl_type = 'Contractor'" people = db.cur.execute(query).fetchall() for i in reversed(range(self.peopleTable.rowCount())): self.peopleTable.removeRow(i) for row_data in people: row_number = self.peopleTable.rowCount() self.peopleTable.insertRow(row_number) # Add checkboxes to the table widget = QWidget() checkBox = QCheckBox() checkBox.setCheckState(Qt.Unchecked) hBoxLayout = QHBoxLayout(widget) hBoxLayout.addWidget(checkBox) hBoxLayout.setAlignment(Qt.AlignCenter) self.peopleTable.setCellWidget(row_number, 0, widget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add photo photos_thumbnails to the table thumbWidget = QWidget() pic = QPixmap( "assets/media/people-media/photos_thumbnails/01Aug2020_18h01mtrtgzteuzuspxrp_thumbnail.png" ) thumbLabel = QLabel() thumbLabel.setPixmap(pic) thumbLayout = QHBoxLayout(thumbWidget) thumbLayout.addWidget(thumbLabel) self.peopleTable.setCellWidget(row_number, 1, thumbWidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Fill the rest of the data for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem("PRN#" + str(data))) else: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") elif self.subcontractorsPeopleRadioBtn.isChecked(): try: query = "SELECT * FROM people WHERE person_empl_type = 'Subcontractor'" people = db.cur.execute(query).fetchall() for i in reversed(range(self.peopleTable.rowCount())): self.peopleTable.removeRow(i) for row_data in people: row_number = self.peopleTable.rowCount() self.peopleTable.insertRow(row_number) # Add checkboxes to the table widget = QWidget() checkBox = QCheckBox() checkBox.setCheckState(Qt.Unchecked) hBoxLayout = QHBoxLayout(widget) hBoxLayout.addWidget(checkBox) hBoxLayout.setAlignment(Qt.AlignCenter) self.peopleTable.setCellWidget(row_number, 0, widget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add photo photos_thumbnails to the table thumbWidget = QWidget() pic = QPixmap( "assets/media/people-media/photos_thumbnails/01Aug2020_18h01mtrtgzteuzuspxrp_thumbnail.png" ) thumbLabel = QLabel() thumbLabel.setPixmap(pic) thumbLayout = QHBoxLayout(thumbWidget) thumbLayout.addWidget(thumbLabel) self.peopleTable.setCellWidget(row_number, 1, thumbWidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Fill the rest of the data for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem("PRN#" + str(data))) else: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") @Slot() def searchPeople(self): value = self.searchPeopleEntry.text() if value == "": QMessageBox.information(self, "Warning", "Search string cannot be empty") self.funcDisplayPeople() else: # Erase search entry self.searchPeopleEntry.setText("") try: query = "SELECT * FROM people WHERE " \ "person_id LIKE ? " \ "OR person_first_name LIKE ?" \ "OR person_last_name LIKE ?" \ "OR person_title LIKE ?" \ "OR person_phone LIKE ?" \ "OR person_email LIKE ?" \ "OR person_location LIKE ?" \ "OR person_empl_type LIKE ?" results = db.cur.execute(query, ( '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', )).fetchall() if results == []: QMessageBox.information(self, "Info", "Nothing was found") self.displayPeople() else: for i in reversed(range(self.peopleTable.rowCount())): self.peopleTable.removeRow(i) for row_data in results: row_number = self.peopleTable.rowCount() self.peopleTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.peopleTable.setCellWidget(row_number, 0, qwidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add photo photos_thumbnails to the table thumbWidget = QWidget() pic = QPixmap( "assets/media/people-media/photos_thumbnails/01Aug2020_18h01mtrtgzteuzuspxrp_thumbnail.png" ) thumbLabel = QLabel() thumbLabel.setPixmap(pic) thumbLayout = QHBoxLayout(thumbWidget) thumbLayout.addWidget(thumbLabel) self.peopleTable.setCellWidget(row_number, 1, thumbWidget) self.peopleTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem("PRN#" + str(data))) else: self.peopleTable.setItem( row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") @Slot() def funcPeopleToCSV(self): indices = self.funcPeopleCheckBox() if indices: CSV(self, "people", indices) else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" ) @Slot() def funcPeopleToXLSX(self): indices = self.funcPeopleCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp to when it was created to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/PeopleXLSX" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".xlsx", "Excel files (*.xlsx)") if fileName: db.cur.execute("SELECT * FROM people") workbook = xlsxwriter.Workbook(fileName) worksheet = workbook.add_worksheet("People") worksheet.set_column('A:C', 12) worksheet.set_row(0, 30) merge_format = workbook.add_format({ 'bold': 1, 'align': 'center', 'valign': 'vcenter' }) worksheet.merge_range('A1:B1', '', merge_format) worksheet.insert_image( 'A1', './assets/logo/logo-full-main.png', { 'x_scale': 0.4, 'y_scale': 0.4, 'x_offset': 15, 'y_offset': 10 }) # Create header row stop = 8 col = 0 for i, value in enumerate(db.cur.description[:stop]): worksheet.write(1, col, value[0]) col += 1 # Write date to xlsx file row_number = 2 for index in range(len(indices)): query = "SELECT * FROM people WHERE person_id=?" person_record = db.cur.execute( query, (indices[index], )).fetchone() for i, value in enumerate(person_record[:stop]): if person_record[9]: worksheet.set_row(row_number, 185) worksheet.set_column(8, 8, 35) worksheet.insert_image(row_number, 8, person_record[9], { 'x_scale': 0.3, 'y_scale': 0.3 }) worksheet.write(row_number, i, value) row_number += 1 workbook.close() QMessageBox.information( self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" ) @Slot() def funcPeopleToPdf(self): indices = self.funcPeopleCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp to when it was created to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/PeoplePDF" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".pdf", "PDF files (*.pdf)") if fileName: pdf = PDF() pdf.add_page() pdf.set_font('Arial', 'B', 13) for index in range(len(indices)): query = "SELECT * FROM people WHERE person_id=?" person_record = db.cur.execute( query, (indices[index], )).fetchone() # This string allows for text formatting in the pdf, easy to implement and test stringPerson = "\nPerson id: " + str(person_record[0]) + \ "\nFirst name: " + str(person_record[1]) + \ "\nLast name: " + str(person_record[2]) + \ "\nTitle: " + str(person_record[3]) + \ "\nPhone: " + str(person_record[4]) + \ "\nEmail: " + str(person_record[5]) + \ "\nLocation: " + str(person_record[6]) + \ "\nEmployment type: " + str(person_record[7]) effectivePageWidth = pdf.w - 2 * pdf.l_margin ybefore = pdf.get_y() pdf.multi_cell(effectivePageWidth / 2, 10, stringPerson) if person_record[9]: pdf.set_xy(effectivePageWidth / 2 + pdf.l_margin, ybefore) pdf.image(person_record[9], effectivePageWidth / 2 + 20, 40, w=70) pdf.ln(0.5) if index != (len(indices) - 1): pdf.add_page() # pdf.multi_cell(200, 10, stringPerson) pdf.output(fileName, 'F') QMessageBox.information( self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" )
class TableWidget(QWidget): @property def columns(self): return self._columns @columns.setter def columns(self, columns): self._columns = columns self.table.setHorizontalHeaderLabels(columns) self.table.resizeColumnsToContents() def __init__(self, columns, parent=None, callback=None): super().__init__(parent) self.callback = callback self.table = QTableWidget(0, len(columns), self) self.columns = columns self.table.setMinimumSize(400, 300) self.table.setShowGrid(True) self.hh = self.table.horizontalHeader() self.hh.setStretchLastSection(False) self.vh = self.table.verticalHeader() layout = QBoxLayout( QBoxLayout.Direction(QBoxLayout.LeftToRight | QBoxLayout.TopToBottom), self) layout.addWidget(self.table) self.setLayout(layout) # self.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum, QSizePolicy.DefaultType)) self.callback = callback if self.callback: #self.table.cellEntered.connect(self.enterProcessor) #self.vh.activated.connect(self.enterProcessor) #self.vh.selected.connect(self.enterProcessor) #self.table.clicked.connect(self.enterProcessor) self.table.itemSelectionChanged.connect(self.enterProcessor) def removeRow(self, rowNo): for i in range(len(self.systemColumnsDefs)): try: self.table.cellWidget(rowNo, i).disconnect() except Exception as ex: print("DAMN!", ex) self.table.removeCellWidget(rowNo, i) self.table.removeRow(rowNo) def appendRow(self, i, row, isN=False): newRowNo = i + 1 self.table.insertRow(newRowNo) for j, cell in enumerate(row): w = QLabel() w.setText(str(cell)) self.table.setCellWidget(newRowNo, j, w) def clear(self): self.table.clearSelection() # self.table.disconnect() self.table.clearContents() self.table.setRowCount(0) def enterProcessor(self): #rowNo = idx.row() rowNo = self.table.currentIndex().row() self.callback(self, rowNo)
class EntityViewer(QWidget): def __init__(self, parent, entity): self.parent = parent QWidget.__init__(self) self.entity = entity self.initUI() def initUI(self): self.layout = QGridLayout(self) self.setLayout(self.layout) self.back = QPushButton("Back") self.back.clicked.connect(self.chooseotherentity) self.layout.addWidget(self.back, 0, 0) self.pullb = QPushButton("Reload from Shotgun") self.pullb.clicked.connect(self.pull) self.layout.addWidget(self.pullb, 2, 0) self.savePage = QPushButton("Save Page") self.savePage.clicked.connect(self.setPageSettings) self.layout.addWidget(self.savePage, 3, 0) self.grid = QTableWidget(self) self.layout.addWidget(self.grid, 1, 0) self.grid.cellDoubleClicked.connect(self.edit) self.grid.cellClicked.connect(self.unedit) self.activeCell = None self.initData() self.grid.horizontalHeader().setSectionsMovable(True) self.grid.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.grid.horizontalHeader().customContextMenuRequested.connect( self.hideF) def initData(self): self.entdata = self.parent.parent.db.get(self.entity) self.fieldsToShow = self.sanitizeFields(self.entdata) i = 0 indexMap = {} for field in self.fieldsToShow: self.grid.insertColumn(self.grid.columnCount()) self.grid.setHorizontalHeaderItem(i, QTableWidgetItem(field)) indexMap[field] = i i += 1 for ent in self.entdata.data: self.grid.insertRow(self.grid.rowCount()) for field in ent: if field in indexMap: cell = cellmanager.barfCell(ent[field], self.parent) self.grid.setCellWidget(self.grid.rowCount() - 1, indexMap[field], cell) def edit(self, row, column): newCell = cellmanager.edit(self.grid.cellWidget(row, column)) if newCell: self.grid.setCellWidget(row, column, newCell) self.activeCell = (row, column) def unedit(self, row, column): #pylint:disable=unused-argument if self.activeCell: self.grid.setCellWidget( self.activeCell[0], self.activeCell[1], cellmanager.save( self.grid.cellWidget(self.activeCell[0], self.activeCell[1]))) self.activeCell = None def chooseotherentity(self): self.parent.parent.changeState(self.parent) def pull(self): self.parent.parent.db.reloadTable(self.entity) self.parent.parent.changeState(EntityViewer(self.parent, self.entity)) def hideF(self, position): menu = QMenu() hideField = menu.addAction("Hide") ac = menu.exec_(self.grid.mapToGlobal(position)) if ac == hideField: index = self.grid.horizontalHeader().logicalIndexAt(position) self.grid.removeColumn(index) def setPageSettings(self): rules = [] for i in range(0, self.grid.columnCount()): rules.append( self.grid.horizontalHeaderItem( self.grid.horizontalHeader().visualIndex(i)).text()) json_reader.write(rules, "data/pagesettings/%s" % self.entity) def sanitizeFields(self, allData): try: rules = json_reader.read("data/pagesettings/%s" % self.entity) except IOError: print("No page settings saved for %ss." % self.entity) return allData.data[0] return rules
class FileDownloaderWidget(QWidget): def __init__(self): super().__init__() self.id = 1 self.TASKID = 0 self.FILENAME = 1 self.PROGRESSBAR = 2 self.SPEED = 3 self.STATUS = 4 self.URL = 5 self.FILEPATH = 6 self.CANCEL = 7 self.RETRY = 8 self.DELETE = 9 self.signals = FileDownloaderSignals() self.tableWidget = QTableWidget() self.startDownloadButton = QPushButton('Start Download') self.clearListButton = QPushButton('Clear Download List') self.hideFinishedCheckBox = QCheckBox('Hide Finished') self.startDownloadButton.clicked.connect(self.startDownload) self.clearListButton.clicked.connect(self.clearDownloadList) self.hideFinishedCheckBox.clicked.connect(self.hideFinished) self.createTable() layout = QGridLayout() layout.addWidget(self.tableWidget, 0, 0, 1, 2) layout.addWidget(self.hideFinishedCheckBox, 1, 0, 1, 1) layout.addWidget(self.startDownloadButton, 2, 0, 1, 1) layout.addWidget(self.clearListButton, 2, 1, 1, 1) layout.setMargin(0) self.setLayout(layout) self.resize(1920, 1080) def createTable(self): # taskId | fileName | progressBar | speed | status | url | filePath | cancel | retry | delete tableItemList = ['Task ID', 'File Name', 'Progress Bar', 'Download Speed', 'Download Status', 'Url', 'File Path', 'Cancel', 'Retry', 'Delete'] self.tableWidget.setColumnCount(len(tableItemList)) self.tableWidget.setRowCount(0) for i in range(len(tableItemList)): self.tableWidget.setHorizontalHeaderItem(i, QTableWidgetItem(tableItemList[i])) def addDownloadItem(self, taskId: str, fileName: str, filePath: str, url: str): row = self.tableWidget.rowCount() self.tableWidget.setRowCount(row + 1) taskIdItem = QTableWidgetItem(taskId) fileNameItem = QTableWidgetItem(fileName) progressBarItem = QTableWidgetItem() downloadSpeedItem = QTableWidgetItem("0 MB/s") downloadStatusItem = QTableWidgetItem("Pending") downloadUrlItem = QTableWidgetItem(url) filePathItem = QTableWidgetItem(filePath) cancelItem = QTableWidgetItem() retryItem = QTableWidgetItem() deleteItem = QTableWidgetItem() taskIdItem.setFlags(Qt.ItemIsEnabled) fileNameItem.setFlags(Qt.ItemIsEnabled) progressBarItem.setFlags(Qt.ItemIsEnabled) downloadSpeedItem.setFlags(Qt.ItemIsEnabled) downloadStatusItem.setFlags(Qt.ItemIsEnabled) downloadUrlItem.setFlags(Qt.ItemIsEnabled) filePathItem.setFlags(Qt.ItemIsEnabled) cancelItem.setFlags(Qt.ItemIsEnabled) retryItem.setFlags(Qt.ItemIsEnabled) deleteItem.setFlags(Qt.ItemIsEnabled) progressBar = QProgressBar() progressBar.setMinimum(0) progressBar.setMaximum(100) progressBar.setValue(0) progressBar.setAlignment(Qt.AlignCenter) progressBar.setFormat(str(progressBar.value()) + ' %') cancelButton = QPushButton('Cancel') cancelButton.clicked.connect(self.cancelTask) retryButton = QPushButton('Retry') retryButton.clicked.connect(self.retryTask) deleteButton = QPushButton('Delete') deleteButton.clicked.connect(self.deleteTask) self.tableWidget.setItem(row, 0, taskIdItem) self.tableWidget.setItem(row, 1, fileNameItem) self.tableWidget.setItem(row, 2, progressBarItem) self.tableWidget.setCellWidget(row, 2, progressBar) self.tableWidget.setItem(row, 3, downloadSpeedItem) self.tableWidget.setItem(row, 4, downloadStatusItem) self.tableWidget.setItem(row, 5, downloadUrlItem) self.tableWidget.setItem(row, 6, filePathItem) self.tableWidget.setItem(row, 7, cancelItem) self.tableWidget.setCellWidget(row, 7, cancelButton) self.tableWidget.setItem(row, 8, retryItem) self.tableWidget.setCellWidget(row, 8, retryButton) self.tableWidget.setItem(row, 9, deleteItem) self.tableWidget.setCellWidget(row, 9, deleteButton) def hideFinished(self): """ hideFinished() is triggered when the hideFinishedCheckbox is clicked It will hide all rows with Cancelled or Finished status and leave Pending or Downloading items visible. """ rowListCancel = self.rowOfValue('Cancelled', self.STATUS) rowListFinished = self.rowOfValue('Finished', self.STATUS) rowList = rowListCancel + rowListFinished logging.debug("rowList: {}".format(rowList)) for row in rowList: if self.hideFinishedCheckBox.isChecked() is True: self.tableWidget.hideRow(row) else: self.tableWidget.showRow(row) def addDownloadTask(self, fileName: str, filePath: str, url: str): """ addDownloadTask(fileName: str, filePath: str, url: str) is an interface to be called externally to add a download task to the widget. """ self.addDownloadItem(str(self.id), fileName, filePath, url) self.id += 1 def startDownload(self): """ startTask() will find the first task in the table with Pending status and start it. """ row = self.rowOfValue('Pending', self.STATUS) if (len(row) > 0): row = row[0] self.startTask(int(self.tableWidget.item(row, self.TASKID).text()), True) def startTask(self, taskId: int, downloadNext: bool): """ startTask(taskId: int) starts a DownloadWorker and starts the task with taskId. It will not check if the task has started before or has cancelled. """ row = self.rowOfValue(taskId, self.TASKID) if (len(row) > 0): row = row[0] filePath = self.tableWidget.item(row, self.FILEPATH).text() url = self.tableWidget.item(row, self.URL).text() worker = DownloadWorker(taskId, filePath, url, downloadNext, self) worker.start() def cancelTask(self): row = self.rowOfWidget(self.sender(), self.CANCEL) taskId = int(self.tableWidget.item(row, self.TASKID).text()) self.tableWidget.item(row, 4).setText('Cancelled') self.signals.cancelDownload.emit(taskId) def retryTask(self): row = self.rowOfWidget(self.sender(), self.RETRY) taskId = int(self.tableWidget.item(row, self.TASKID).text()) self.signals.cancelDownload.emit(taskId) self.tableWidget.item(row, self.STATUS).setText('Pending') self.startTask(taskId, False) def deleteTask(self): row = self.rowOfWidget(self.sender(), self.DELETE) taskId = int(self.tableWidget.item(row, self.TASKID).text()) self.signals.cancelDownload.emit(taskId) self.tableWidget.removeRow(row) def clearDownloadList(self): """ clearDownloadList() emits a signal to cancell all downloading items and directly recreate the table. """ self.signals.cancelDownload.emit(-1) self.createTable() def rowOfWidget(self, item: QWidget, itemType: int): """ rowOfWidget(item, itemType) is used to locate the embedded widget in self.tableWidget. It will compare item with those in the itemType column and return the first row number where it finds the matched item. """ for row in range(self.tableWidget.rowCount()): if self.tableWidget.cellWidget(row, itemType) == item: return row def rowOfValue(self, value, itemType: int): """ rowOfValue(value, itemType: str) takes two parameters. It will look into self.tableWidget for the itemType(column) to find the value. It returns a list of row number where the values are. """ rowList = [] for row in range(self.tableWidget.rowCount()): if self.tableWidget.item(row, itemType).text() == str(value): rowList.append(row) return rowList def updateProgressBar(self, taskId: int, value: int): row = self.rowOfValue(taskId, self.TASKID) if len(row) > 0: row = row[0] logging.debug("updateProgressBar of row {} with value {}".format(row, value)) self.tableWidget.cellWidget(row, self.PROGRESSBAR).setValue(value) self.tableWidget.cellWidget(row, self.PROGRESSBAR).setFormat(str(value) + " %") def updateDownloadSpeed(self, taskId: int, value: str): row = self.rowOfValue(taskId, self.TASKID) if len(row) > 0: row = row[0] logging.debug("updateDownloadSpeed of row {} with value {}".format(row, value)) self.tableWidget.item(row, self.SPEED).setText(value) def updateDownloadStatus(self, taskId: int, value: str): row = self.rowOfValue(taskId, self.TASKID) if len(row) > 0: row = row[0] logging.debug("updateDownloadStatus of row {} with value {}".format(row, value)) self.tableWidget.item(row, self.STATUS).setText(value)
class IssuesTab(QWidget): def __init__(self, parent): QWidget.__init__(self) self.Parent = parent self.IssueTitle = 'ISSUES' self.UI() @property def Title(self): return self.IssueTitle def UI(self): self.widgets() self.layouts() self.funcDisplayIssues() def widgets(self): # Issues widgets ########################################################### # Top layout (search issues) widgets self.searchIssuesText = QLabel("Search: ") self.searchIssuesEntry = QLineEdit() self.searchIssuesEntry.setPlaceholderText("Search issues..") self.searchIssuesBtn = QPushButton("Search") self.searchIssuesBtn.setObjectName("btn_searchIssues") self.searchIssuesBtn.clicked.connect(self.funcSearchIssues) self.refreshIssuesBtn = QPushButton("Refresh") self.refreshIssuesBtn.clicked.connect(self.funcDisplayIssues) # Middle layout (list issues) widgets with radio buttons self.allIssuesRadioBtn = QRadioButton("All issues") self.ongoingIssuesRadioBtn = QRadioButton("Pending issues") self.lateIssuesRadioBtn = QRadioButton("Late issues") self.closedIssuesRadioBtn = QRadioButton("Closed issues") self.listIssuesBtn = QPushButton("List") self.listIssuesBtn.setObjectName("btn_listIssues") self.listIssuesBtn.clicked.connect(self.funcListIssues) # Bottom layout widget # Table showing issues self.issuesTable = QTableWidget() self.issuesTable.verticalHeader().hide() self.issuesTable.setSortingEnabled(True) self.issuesTable.setShowGrid(False) self.issuesTable.verticalHeader().setDefaultSectionSize(90) self.issuesTable.setColumnCount(13) self.issuesTable.setHorizontalHeaderItem(0, QTableWidgetItem("")) self.issuesTable.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.issuesTable.setHorizontalHeaderItem(1, QTableWidgetItem("Status")) self.issuesTable.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.issuesTable.setHorizontalHeaderItem(2, QTableWidgetItem("ID")) self.issuesTable.setHorizontalHeaderItem(3, QTableWidgetItem("Date")) self.issuesTable.setHorizontalHeaderItem(4, QTableWidgetItem("Priority")) self.issuesTable.setHorizontalHeaderItem(5, QTableWidgetItem("Observer")) self.issuesTable.horizontalHeader().setSectionResizeMode(5, QHeaderView.Stretch) self.issuesTable.setHorizontalHeaderItem(6, QTableWidgetItem("Inspection name")) self.issuesTable.horizontalHeader().setSectionResizeMode(6, QHeaderView.Stretch) self.issuesTable.setHorizontalHeaderItem(7, QTableWidgetItem("Theme")) self.issuesTable.horizontalHeader().setSectionResizeMode(7, QHeaderView.Stretch) self.issuesTable.setHorizontalHeaderItem(8, QTableWidgetItem("Facility")) self.issuesTable.setHorizontalHeaderItem(9, QTableWidgetItem("Insp. Dept")) self.issuesTable.setHorizontalHeaderItem(10, QTableWidgetItem("Deadline")) self.issuesTable.setHorizontalHeaderItem(11, QTableWidgetItem("Status")) self.issuesTable.setHorizontalHeaderItem(12, QTableWidgetItem("Created on")) # Double clicking a row opens a window with issue details self.issuesTable.doubleClicked.connect(self.funcSelectedIssue) # Buttons for actions on selected issues self.addIssue = QPushButton("Add") self.addIssue.setObjectName("btn_addIssue") self.addIssue.clicked.connect(self.funcAddIssue) self.viewIssue = QPushButton("View/Edit") self.viewIssue.setObjectName("btn_viewIssue") self.viewIssue.clicked.connect(self.funcSelectedIssue) self.closeIssueBtn = QPushButton("Close") self.closeIssueBtn.setObjectName("btn_closeIssue") self.closeIssueBtn.clicked.connect(self.funcCloseIssue) self.deleteIssue = QPushButton("Delete") self.deleteIssue.setObjectName("btn_deleteIssue") self.deleteIssue.clicked.connect(self.funcDeleteIssue) self.exportIssuesCSVBtn = QPushButton("Export CSV") self.exportIssuesCSVBtn.setEnabled(False) self.exportIssuesCSVBtn.setObjectName("btn_exportIssuesCSV") self.exportIssuesCSVBtn.clicked.connect(self.funcIssuesToCSV) self.exportIssuesXLSXBtn = QPushButton("Export XLSX") self.exportIssuesXLSXBtn.setEnabled(False) self.exportIssuesXLSXBtn.setObjectName("btn_exportIssuesXLSX") self.exportIssuesXLSXBtn.clicked.connect(self.funcIssuestoXLSX) self.exportIssuesPDFBtn = QPushButton("Export PDF") self.exportIssuesPDFBtn.setEnabled(False) self.exportIssuesPDFBtn.setObjectName("btn_exportIssuesPDF") self.exportIssuesPDFBtn.clicked.connect(self.funcIssuesToPdf) def layouts(self): # Issues layouts ########################################################### self.issuesMainLayout = QVBoxLayout() self.issuesMainTopLayout = QHBoxLayout() self.issuesTopLeftLayout = QHBoxLayout() self.issuesTopRightLayout = QHBoxLayout() # self.issuesMainMiddleLayout = QHBoxLayout() self.issuesMainBottomLayout = QHBoxLayout() self.issuesBottomRightLayout = QVBoxLayout() self.issuesBottomLeftLayout = QHBoxLayout() # Groupboxes allow customization using CSS-like syntax # self.issuesTopGroupBox = QGroupBox() # self.issuesTopGroupBoxRightFiller = QGroupBox() # self.issuesTopGroupBoxRightFiller.setStyleSheet(styles.groupBoxFillerStyle()) # # self.issuesMiddleGroupBox = QGroupBox() # self.issuesMiddleGroupBoxRightFiller = QGroupBox() # self.issuesMiddleGroupBoxRightFiller.setStyleSheet(styles.groupBoxFillerStyle()) self.issuesTopLeftGroupBox = QGroupBox() self.issuesTopRightGroupBox = QGroupBox() self.issuesTopGroupBox = QGroupBox() self.issuesBottomGroupBox = QGroupBox() self.issuesBottomLeftGroupBox = QGroupBox() self.issuesBottomRightGroupBox = QGroupBox() self.issuesBottomRightGroupBox.setStyleSheet('QGroupBox {margin-top: 0px;}') self.issuesBottomRightGroupBoxFiller = QGroupBox() self.issuesBottomRightGroupBoxFiller.setStyleSheet(styles.groupBoxFillerStyle()) # Add widgets # Top layout (search box) widgets self.issuesTopLeftLayout.addWidget(self.searchIssuesText, 10) self.issuesTopLeftLayout.addWidget(self.searchIssuesEntry, 30) self.issuesTopLeftLayout.addWidget(self.searchIssuesBtn, 10) self.issuesTopLeftLayout.addItem(QSpacerItem(70, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.issuesTopLeftLayout.addWidget(self.refreshIssuesBtn, 10) self.issuesTopLeftGroupBox.setLayout(self.issuesTopLeftLayout) # layout (list box) widgets self.issuesTopRightLayout.addWidget(self.allIssuesRadioBtn) self.issuesTopRightLayout.addWidget(self.ongoingIssuesRadioBtn) self.issuesTopRightLayout.addWidget(self.lateIssuesRadioBtn) self.issuesTopRightLayout.addWidget(self.closedIssuesRadioBtn) self.issuesTopRightLayout.addWidget(self.listIssuesBtn) self.issuesTopRightGroupBox.setLayout(self.issuesTopRightLayout) self.issuesMainTopLayout.addWidget(self.issuesTopLeftGroupBox, 60) self.issuesMainTopLayout.addWidget(self.issuesTopRightGroupBox, 40) # Bottom layout (table with facilities) widgets # Bottom left layout with table self.issuesBottomLeftLayout.addWidget(self.issuesTable) self.issuesBottomLeftGroupBox.setLayout(self.issuesBottomLeftLayout) # Bottom right layout with buttons self.issuesBottomRightLayout.addWidget(self.addIssue, 5) self.issuesBottomRightLayout.addWidget(self.viewIssue, 5) self.issuesBottomRightLayout.addWidget(self.closeIssueBtn, 5) self.issuesBottomRightLayout.addWidget(self.deleteIssue, 5) self.issuesBottomRightLayout.addWidget(self.issuesBottomRightGroupBoxFiller, 65) self.issuesBottomRightLayout.addWidget(self.exportIssuesCSVBtn, 5) self.issuesBottomRightLayout.addWidget(self.exportIssuesXLSXBtn, 5) self.issuesBottomRightLayout.addWidget(self.exportIssuesPDFBtn, 5) self.issuesBottomRightGroupBox.setLayout(self.issuesBottomRightLayout) self.issuesMainBottomLayout.addWidget(self.issuesTable, 90) self.issuesMainBottomLayout.addWidget(self.issuesBottomRightGroupBox, 10) self.issuesMainLayout.addLayout(self.issuesMainTopLayout, 10) self.issuesMainLayout.addLayout(self.issuesMainBottomLayout, 90) self.setLayout(self.issuesMainLayout) # Populating the table @Slot() def funcDisplayIssues(self): for i in reversed(range(self.issuesTable.rowCount())): self.issuesTable.removeRow(i) issues = db.cur.execute("SELECT " "issue_id, " "issue_date, " "issue_priority, " "issue_observer," "issue_inspection, " "issue_theme, " "issue_facility, " "issue_insp_dept," "issue_deadline, " "status, " "created_on " "FROM issues") for row_data in issues: row_number = self.issuesTable.rowCount() self.issuesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) checkbox.stateChanged.connect(self.funcActivateBtnsWithCheckbox) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.issuesTable.setCellWidget(row_number, 0, qwidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) # Add status photos_thumbnails to the table thumbStatusWidget = QWidget() # Assign proper status thumbnails based on deadline if row_data[9] == "Closed": pic = QPixmap('assets/icons/issue_icons/issue_closed_icon.png') elif row_data[8] > str(datetime.datetime.now()): pic = QPixmap('assets/icons/issue_icons/issue_pending_icon.png') elif row_data[8] < str(datetime.datetime.now()): pic = QPixmap('assets/icons/issue_icons/issue_late_icon.png') thumbStatusLabel = QLabel() thumbStatusLabel.setPixmap(pic) thumbStatusLayout = QHBoxLayout(thumbStatusWidget) thumbStatusLayout.addWidget(thumbStatusLabel) self.issuesTable.setCellWidget(row_number, 1, thumbStatusWidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) thumbPriorityWidget = QWidget() # Assign priority thumbnails if row_data[2] == "Low": pic = QPixmap('assets/icons/issue_priority_icons/low_priority.png') elif row_data[2] == "Medium": pic = QPixmap('assets/icons/issue_priority_icons/medium_priority.png') elif row_data[2] == "High": pic = QPixmap('assets/icons/issue_priority_icons/high_priority.png') thumbPriorityLabel = QLabel() thumbPriorityLabel.setAlignment(Qt.AlignCenter) thumbPriorityLabel.setPixmap(pic) thumbPriorityLayout = QHBoxLayout(thumbPriorityWidget) thumbPriorityLayout.addWidget(thumbPriorityLabel) self.issuesTable.setCellWidget(row_number, 4, thumbPriorityWidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=2): if column_number == 2: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("ISS#" + str(data))) elif column_number == 4: # Do not print priority in the table self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("")) else: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) self.issuesTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.issuesTable.setSelectionBehavior(QTableView.SelectRows) @Slot() def funcActivateBtnsWithCheckbox(self): indices = self.funcIssuesCheckBox() if self.sender().isChecked() or indices: self.exportIssuesCSVBtn.setEnabled(True) self.exportIssuesXLSXBtn.setEnabled(True) self.exportIssuesPDFBtn.setEnabled(True) else: self.exportIssuesCSVBtn.setEnabled(False) self.exportIssuesXLSXBtn.setEnabled(False) self.exportIssuesPDFBtn.setEnabled(False) @Slot() def funcAddIssue(self): self.newIssue = AddIssue(self) self.newIssue.setObjectName("add_issue_popup") self.newIssue.setStyleSheet(styles.addPopups()) @Slot() def funcIssuesCheckBox(self): checked_list = [] for i in range(self.issuesTable.rowCount()): if self.issuesTable.cellWidget(i, 0).findChild(type(QCheckBox())).isChecked(): item = self.issuesTable.item(i, 2).text() checked_list.append(item.lstrip("ISS#")) return checked_list @Slot() def funcSelectedIssue(self): self.displayIssue = DisplayIssue(self) @Slot() def funcSearchIssues(self): value = self.searchIssuesEntry.text() if value == "": QMessageBox.information(self, "Warning", "Search string cannot be empty") self.funcDisplayIssues() else: # Erase search entry self.searchIssuesEntry.setText("") try: query = "SELECT * FROM issues WHERE " \ "issue_id LIKE ? " \ "OR issue_date LIKE ?" \ "OR issue_priority LIKE ?" \ "OR issue_observer LIKE ?" \ "OR issue_team LIKE ?" \ "OR issue_inspection LIKE ?" \ "OR issue_theme LIKE ?" \ "OR issue_facility LIKE ?" \ "OR issue_fac_supervisor LIKE ?" \ "OR issue_spec_loc LIKE ?" \ "OR issue_insp_dept LIKE ?" \ "OR issue_insp_contr LIKE ?" \ "OR issue_insp_subcontr LIKE ?" \ "OR issue_deadline LIKE ?" results = db.cur.execute(query, ('%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%',)).fetchall() if results == []: QMessageBox.information(self, "Info", "Nothing was found") self.funcDisplayIssues() else: for i in reversed(range(self.issuesTable.rowCount())): self.issuesTable.removeRow(i) for row_data in results: row_number = self.issuesTable.rowCount() self.issuesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.issuesTable.setCellWidget(row_number, 0, qwidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("ISS#" + str(data))) else: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") @Slot() def funcListIssues(self): try: if self.allIssuesRadioBtn.isChecked(): self.funcDisplayIssues() elif self.ongoingIssuesRadioBtn.isChecked(): query = "SELECT " \ "issue_id, " \ "issue_date, " \ "issue_priority, " \ "issue_observer," \ "issue_inspection, " \ "issue_theme, " \ "issue_facility, " \ "issue_insp_dept," \ "issue_deadline, " \ "status, " \ "created_on " \ "FROM issues WHERE status='Open' " \ "AND issue_deadline > DATETIME('now')" issues = db.cur.execute(query).fetchall() for i in reversed(range(self.issuesTable.rowCount())): self.issuesTable.removeRow(i) for row_data in issues: row_number = self.issuesTable.rowCount() self.issuesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.issuesTable.setCellWidget(row_number, 0, qwidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("ISS#" + str(data))) else: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) elif self.lateIssuesRadioBtn.isChecked(): query = "SELECT issue_id, " \ "issue_date, " \ "issue_priority, " \ "issue_observer," \ "issue_inspection, " \ "issue_theme, " \ "issue_facility, " \ "issue_insp_dept," \ "issue_deadline, " \ "status, " \ "created_on " \ "FROM issues " \ "WHERE status='Open' AND issue_deadline < DATETIME('now')" issues = db.cur.execute(query).fetchall() for i in reversed(range(self.issuesTable.rowCount())): self.issuesTable.removeRow(i) for row_data in issues: row_number = self.issuesTable.rowCount() self.issuesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.issuesTable.setCellWidget(row_number, 0, qwidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("ISS#" + str(data))) else: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) elif self.closedIssuesRadioBtn.isChecked(): query = "SELECT " \ "issue_id, " \ "issue_date, " \ "issue_priority, " \ "issue_observer," \ "issue_inspection, " \ "issue_theme, " \ "issue_facility, " \ "issue_insp_dept," \ "issue_deadline, " \ "status, " \ "created_on " \ "FROM issues WHERE status='Closed'" issues = db.cur.execute(query).fetchall() for i in reversed(range(self.issuesTable.rowCount())): self.issuesTable.removeRow(i) for row_data in issues: row_number = self.issuesTable.rowCount() self.issuesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.issuesTable.setCellWidget(row_number, 0, qwidget) self.issuesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem("ISS#" + str(data))) else: self.issuesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") @Slot() def funcCloseIssue(self): indices = self.funcIssuesCheckBox() # Close issues with selected checkboxes if indices: try: for index in range(len(indices)): statusQuery = "SELECT status FROM issues WHERE issue_id=?" currentStatus = db.cur.execute(statusQuery, (indices[index],)).fetchone() if currentStatus[0] == "Open": query = "UPDATE issues SET status='Closed' WHERE issue_id=?" db.cur.execute(query, (indices[index],)) db.conn.commit() else: QMessageBox.information(self, "Info", "Issue(s) is already closed") QMessageBox.information(self, "Info", "Operation completed successfully") self.funcDisplayIssues() except: QMessageBox.information(self, "Info", "Something went wrong") else: row = self.issuesTable.currentRow() issueId = self.issuesTable.item(row, 2).text() issueId = issueId.lstrip("ISS#") try: statusQuery = "SELECT status FROM issues WHERE issue_id=?" currentStatus = db.cur.execute(statusQuery, (issueId,)).fetchone() if currentStatus[0] == "Open": query = "UPDATE issues SET status='Closed' WHERE issue_id=?" db.cur.execute(query, (issueId,)) db.conn.commit() QMessageBox.information(self, "Info", "Issue closed successfully") self.funcDisplayIssues() else: QMessageBox.information(self, "Info", "Issue is already closed") except: QMessageBox.information(self, "Info", "Something went wrong") @Slot() def funcDeleteIssue(self): indices = self.funcIssuesCheckBox() mbox = QMessageBox.question(self, "Warning", "Are you sure you want to delete selected issue(s)?", QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel) if (mbox == QMessageBox.Yes): if indices: try: for index in range(len(indices)): query = "DELETE FROM issues WHERE issue_id = ?" db.cur.execute(query, (indices[index],)) db.conn.commit() QMessageBox.information(self, "Info", "Issues were deleted") self.funcDisplayIssues() except: QMessageBox.information(self, "Info", "No changes made") else: row = self.issuesTable.currentRow() issueId = self.issuesTable.item(row, 2).text() issueId = issueId.lstrip("ISS#") try: query = "DELETE FROM issues WHERE issue_id = ?" db.cur.execute(query, (issueId,)) db.conn.commit() QMessageBox.information(self, "Info", "Issue was deleted") self.funcDisplayIssues() except: QMessageBox.information(self, "Info", "No changes made") self.displayIssue.close() @Slot() def funcIssuesToCSV(self): indices = self.funcIssuesCheckBox() if indices: CSV(self, "issues", indices) else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export") @Slot() def funcIssuestoXLSX(self): indices = self.funcIssuesCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp to when it was created to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/IssuesXLSX" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".xlsx", "Excel files (*.xlsx)") if fileName: db.cur.execute("SELECT * FROM issues") workbook = xlsxwriter.Workbook(fileName) worksheet = workbook.add_worksheet("Issues") worksheet.set_column('A:C', 12) worksheet.set_row(0, 30) merge_format = workbook.add_format({ 'bold': 1, 'align': 'center', 'valign': 'vcenter'}) worksheet.merge_range('A1:B1', '', merge_format) worksheet.insert_image('A1', './assets/logo/logo-full-main.png', {'x_scale': 0.4, 'y_scale': 0.4, 'x_offset': 15, 'y_offset': 10}) # Create header row stop = 17 col = 0 for i, value in enumerate(db.cur.description[:stop]): worksheet.write(1, col, value[0]) col += 1 # Write data to xlsx file row_number = 2 for index in range(len(indices)): query = "SELECT * FROM issues WHERE issue_id=?" issue_record = db.cur.execute(query, (indices[index],)).fetchone() for i, value in enumerate(issue_record[:stop]): if issue_record[18]: worksheet.set_row(row_number, 185) worksheet.set_column(17, 17, 35) worksheet.insert_image( row_number, 17, issue_record[18], {'x_scale': 0.3, 'y_scale': 0.3}) worksheet.write(row_number, i, value) row_number += 1 workbook.close() QMessageBox.information(self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export") @Slot() def funcIssuesToPdf(self): indices = self.funcIssuesCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp it was created on to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/IssuesPDF" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".pdf", "PDF files (*.pdf)") if fileName: pdf = PDF() pdf.add_page() pdf.set_font('Arial', 'B', 13) for index in range(len(indices)): query = "SELECT * FROM issues WHERE issue_id=?" issue_record = db.cur.execute(query, (indices[index],)).fetchone() # This string allows for text formatting in the pdf, easy to implement and test stringIssue = "\nIssue id: " + str(issue_record[0]) + \ "\nIssue date: " + str(issue_record[1]) + \ "\nPriority: " + str(issue_record[2]) + \ "\nObserver: " + str(issue_record[3]) + \ "\nRevision team: " + str(issue_record[4]) + \ "\nInspection name: " + str(issue_record[5]) + \ "\nHSE theme: " + str(issue_record[6]) + \ "\nFacility: " + str(issue_record[7]) + \ "\nFacility supervisor: " + str(issue_record[8]) + \ "\nSpecific location: " + str(issue_record[9]) + \ "\nInspected department: " + str(issue_record[10]) + \ "\nInspected contractor: " + str(issue_record[11]) + \ "\nInspected subcontractor: " + str(issue_record[12]) + \ "\nDeadline: " + str(issue_record[13]) + \ "\nStatus: " + str(issue_record[14]) + \ "\nCreated on: " + str(issue_record[15]) + \ "\nClosed on: " + str(issue_record[16]) effectivePageWidth = pdf.w - 2 * pdf.l_margin ybefore = pdf.get_y() pdf.multi_cell(effectivePageWidth / 2, 10, stringIssue) if issue_record[18]: pdf.set_xy(effectivePageWidth / 2 + pdf.l_margin, ybefore) pdf.image(issue_record[18], effectivePageWidth / 2 + 20, 40, w=70) pdf.ln(0.5) # Page break is achieved by adding a new page # after all items except for the last one if index != (len(indices) - 1): pdf.add_page() pdf.output(fileName, 'F') QMessageBox.information(self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export")
class FacilityTab(QWidget): def __init__(self, parent): QWidget.__init__(self) self.Parent = parent self.FcltyTitle = 'FACILITIES' self.UI() @property def Title(self): return self.FcltyTitle def UI(self): self.widgets() self.layouts() self.funcDisplayFacilities() def widgets(self): # Facilities widgets ########################################################### # Top layout (search facilities) widgets self.searchFacilitiesText = QLabel("Search: ") self.searchFacilitesEntry = QLineEdit() self.searchFacilitesEntry.setPlaceholderText("Search facilities..") self.searchFacilitiesBtn = QPushButton("Search") self.searchFacilitiesBtn.clicked.connect(self.funcSearchFacilities) self.refreshFacilitiesBtn = QPushButton("Refresh") self.refreshFacilitiesBtn.clicked.connect(self.funcDisplayFacilities) # Middle layout (list people) widgets with radio buttons self.allFacilitiesRadioBtn = QRadioButton("All facilities") self.withOngoingIssuesFacilitiesRadioBtn = QRadioButton( "With pending issues") self.withLateIssuesRadioBtn = QRadioButton("With late issues") self.listFacilitiesBtn = QPushButton("List") # Bottom layout widget, a table showing people self.facilitiesTable = QTableWidget() self.facilitiesTable.verticalHeader().hide() self.facilitiesTable.setSortingEnabled(True) self.facilitiesTable.setShowGrid(False) self.facilitiesTable.verticalHeader().setDefaultSectionSize(40) self.facilitiesTable.setColumnCount(11) # self.peopleTable.setColumnHidden(0, True) self.facilitiesTable.setHorizontalHeaderItem(0, QTableWidgetItem("")) self.facilitiesTable.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.facilitiesTable.setHorizontalHeaderItem(1, QTableWidgetItem("ID")) self.facilitiesTable.setHorizontalHeaderItem(2, QTableWidgetItem("Name")) self.facilitiesTable.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.facilitiesTable.setHorizontalHeaderItem( 3, QTableWidgetItem("Location")) self.facilitiesTable.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.facilitiesTable.setHorizontalHeaderItem(4, QTableWidgetItem("Phone")) self.facilitiesTable.horizontalHeader().setSectionResizeMode( 3, QHeaderView.Stretch) self.facilitiesTable.setHorizontalHeaderItem(5, QTableWidgetItem("Email")) self.facilitiesTable.horizontalHeader().setSectionResizeMode( 4, QHeaderView.Stretch) self.facilitiesTable.setHorizontalHeaderItem( 6, QTableWidgetItem("Supervisor")) self.facilitiesTable.setHorizontalHeaderItem( 7, QTableWidgetItem("Ongoing issues")) self.facilitiesTable.setHorizontalHeaderItem( 8, QTableWidgetItem("Late issues")) self.facilitiesTable.setHorizontalHeaderItem( 9, QTableWidgetItem("Total issues")) self.facilitiesTable.setHorizontalHeaderItem( 10, QTableWidgetItem("Total inspections")) # Double clicking a row opens a window with person details self.facilitiesTable.doubleClicked.connect(self.funcSelectedFacility) # Buttons for actions on selected facilities self.addFacility = QPushButton("Add") self.addFacility.clicked.connect(self.funcAddFacility) self.viewFacility = QPushButton("View/Edit") self.viewFacility.clicked.connect(self.funcSelectedFacility) self.deleteFacility = QPushButton("Delete") self.deleteFacility.clicked.connect(self.funcDeleteFacility) self.exportFacilitiesCSVBtn = QPushButton("Export CSV") self.exportFacilitiesCSVBtn.setEnabled(False) self.exportFacilitiesCSVBtn.clicked.connect(self.funcFacilitiesToCSV) self.exportFacilitiesXSLXBtn = QPushButton("Export XLSX") self.exportFacilitiesXSLXBtn.setEnabled(False) self.exportFacilitiesXSLXBtn.clicked.connect(self.funcFacilitiesToXLSX) self.exportFacilitiesPDFBtn = QPushButton("Export PDF") self.exportFacilitiesPDFBtn.setEnabled(False) self.exportFacilitiesPDFBtn.clicked.connect(self.funcFacilitiesToPdf) def layouts(self): # Facilities layouts ########################################################### self.facilitiesMainLayout = QVBoxLayout() self.facilitiesMainTopLayout = QHBoxLayout() self.facilitiesTopLeftLayout = QHBoxLayout() self.facilitiesTopRightLayout = QHBoxLayout() self.facilitiesMainBottomLayout = QHBoxLayout() self.facilitiesBottomRightLayout = QVBoxLayout() self.facilitiesBottomLeftLayout = QHBoxLayout() # Groupboxes allows customization using CSS-like syntax self.facilitiesTopLeftGroupBox = QGroupBox() self.facilitiesTopRightGroupBox = QGroupBox() self.facilitiesTopGroupBox = QGroupBox() self.facilitiesBottomGroupBox = QGroupBox() self.facilitiesBottomLeftGroupBox = QGroupBox() self.facilitiesBottomRightGroupBox = QGroupBox() self.facilitiesBottomRightGroupBox.setStyleSheet( 'QGroupBox {margin-top: 0px;}') self.facilitiesBottomRightGroupBoxFiller = QGroupBox() self.facilitiesBottomRightGroupBoxFiller.setStyleSheet( styles.groupBoxFillerStyle()) # Top layout (search box) widgets self.facilitiesTopLeftLayout.addWidget(self.searchFacilitiesText, 10) self.facilitiesTopLeftLayout.addWidget(self.searchFacilitesEntry, 30) self.facilitiesTopLeftLayout.addWidget(self.searchFacilitiesBtn, 10) self.facilitiesTopLeftLayout.addItem( QSpacerItem(70, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.facilitiesTopLeftLayout.addWidget(self.refreshFacilitiesBtn, 10) self.facilitiesTopLeftGroupBox.setLayout(self.facilitiesTopLeftLayout) # layout (list box) widgets self.facilitiesTopRightLayout.addWidget(self.allFacilitiesRadioBtn) self.facilitiesTopRightLayout.addWidget( self.withOngoingIssuesFacilitiesRadioBtn) self.facilitiesTopRightLayout.addWidget(self.withLateIssuesRadioBtn) self.facilitiesTopRightLayout.addWidget(self.listFacilitiesBtn) self.facilitiesTopRightGroupBox.setLayout( self.facilitiesTopRightLayout) self.facilitiesMainTopLayout.addWidget(self.facilitiesTopLeftGroupBox, 60) self.facilitiesMainTopLayout.addWidget(self.facilitiesTopRightGroupBox, 40) # Bottom layout (table with facilities) widgets # Bottom left layout with table self.facilitiesBottomLeftLayout.addWidget(self.facilitiesTable) self.facilitiesBottomLeftGroupBox.setLayout( self.facilitiesBottomLeftLayout) # Bottom right layout with buttons self.facilitiesBottomRightLayout.addWidget(self.addFacility, 5) self.facilitiesBottomRightLayout.addWidget(self.viewFacility, 5) self.facilitiesBottomRightLayout.addWidget(self.deleteFacility, 5) self.facilitiesBottomRightLayout.addWidget( self.facilitiesBottomRightGroupBoxFiller, 70) self.facilitiesBottomRightLayout.addWidget(self.exportFacilitiesCSVBtn, 5) self.facilitiesBottomRightLayout.addWidget( self.exportFacilitiesXSLXBtn, 5) self.facilitiesBottomRightLayout.addWidget(self.exportFacilitiesPDFBtn, 5) self.facilitiesBottomRightGroupBox.setLayout( self.facilitiesBottomRightLayout) self.facilitiesMainBottomLayout.addWidget(self.facilitiesTable, 90) self.facilitiesMainBottomLayout.addWidget( self.facilitiesBottomRightGroupBox, 10) self.facilitiesMainLayout.addLayout(self.facilitiesMainTopLayout, 10) self.facilitiesMainLayout.addLayout(self.facilitiesMainBottomLayout, 90) self.setLayout(self.facilitiesMainLayout) @Slot() def funcAddFacility(self): self.newFacility = AddFacility(self) self.newFacility.setObjectName("add_facility_popup") self.newFacility.setStyleSheet(styles.addPopups()) @Slot() def funcDisplayFacilities(self): for i in reversed(range(self.facilitiesTable.rowCount())): self.facilitiesTable.removeRow(i) cur = db.cur facilities = cur.execute("SELECT * FROM facilities") for row_data in facilities: row_number = self.facilitiesTable.rowCount() self.facilitiesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) checkbox.stateChanged.connect(self.funcActivateBtnsWithCheckbox) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.facilitiesTable.setCellWidget(row_number, 0, qwidget) self.facilitiesTable.setItem(row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.facilitiesTable.setItem( row_number, column_number, QTableWidgetItem("FCL#" + str(data))) else: self.facilitiesTable.setItem(row_number, column_number, QTableWidgetItem(str(data))) self.facilitiesTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.facilitiesTable.setSelectionBehavior(QTableView.SelectRows) @Slot() def funcActivateBtnsWithCheckbox(self): indices = self.funcFacilitiesCheckBox() if self.sender().isChecked() or indices: self.exportFacilitiesCSVBtn.setEnabled(True) self.exportFacilitiesXSLXBtn.setEnabled(True) self.exportFacilitiesPDFBtn.setEnabled(True) else: self.exportFacilitiesCSVBtn.setEnabled(False) self.exportFacilitiesXSLXBtn.setEnabled(False) self.exportFacilitiesPDFBtn.setEnabled(False) @Slot() def funcFacilitiesCheckBox(self): checked_list = [] for i in range(self.facilitiesTable.rowCount()): if self.facilitiesTable.cellWidget(i, 0).findChild( type(QCheckBox())).isChecked(): item = self.facilitiesTable.item(i, 1).text() checked_list.append(item.lstrip("FCL#")) return checked_list @Slot() def funcSelectedFacility(self): self.displayFacility = DisplayFacility(self) self.displayFacility.show() @Slot() def funcDeleteFacility(self): indices = self.funcFacilitiesCheckBox() mbox = QMessageBox.question( self, "Warning", "Are you sure you want to delete this facility?", QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel) if (mbox == QMessageBox.Yes): if indices: try: for index in range(len(indices)): query = "DELETE FROM facilities WHERE facility_id = ?" db.cur.execute(query, (indices[index], )) db.conn.commit() QMessageBox.information(self, "Info", "Facilites were deleted") self.funcDisplayFacilities() except: QMessageBox.information(self, "Info", "No changes made") else: row = self.facilitiesTable.currentRow() facilityId = self.facilitiesTable.item(row, 0).text() facilityId = facilityId.lstrip("FCL#") try: query = "DELETE FROM facilities WHERE facility_id = ?" db.cur.execute(query, (facilityId, )) db.conn.commit() QMessageBox.information(self, "Info", "Facility was deleted") self.funcDisplayFacilities() except: QMessageBox.information(self, "Info", "No changes made") self.displayFacility.close() @Slot() def funcSearchFacilities(self): value = self.searchFacilitesEntry.text() if value == "": QMessageBox.information(self, "Warning", "Search string cannot be empty") self.displayFacilities() else: # Erase search entry self.searchFacilitesEntry.setText("") try: query = "SELECT * FROM facilities WHERE " \ "facility_id LIKE ? " \ "OR facility_name LIKE ?" \ "OR facility_location LIKE ?" \ "OR facility_phone LIKE ?" \ "OR facility_email LIKE ?" \ "OR facility_supervisor LIKE ?" results = db.cur.execute( query, ('%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%', '%' + value + '%')).fetchall() if results == []: QMessageBox.information(self, "Info", "Nothing was found") self.funcDisplayFacilities() else: for i in reversed(range(self.facilitiesTable.rowCount())): self.facilitiesTable.removeRow(i) for row_data in results: row_number = self.facilitiesTable.rowCount() self.facilitiesTable.insertRow(row_number) # Add checkboxes to the table qwidget = QWidget() checkbox = QCheckBox() checkbox.setCheckState(Qt.Unchecked) qhboxlayout = QHBoxLayout(qwidget) qhboxlayout.addWidget(checkbox) qhboxlayout.setAlignment(Qt.AlignCenter) self.facilitiesTable.setCellWidget( row_number, 0, qwidget) self.facilitiesTable.setItem( row_number, 0, QTableWidgetItem(row_number)) for column_number, data in enumerate(row_data, start=1): if column_number == 1: self.facilitiesTable.setItem( row_number, column_number, QTableWidgetItem("FCL#" + str(data))) else: self.facilitiesTable.setItem( row_number, column_number, QTableWidgetItem(str(data))) except: QMessageBox.information(self, "Info", "Cannot access database") @Slot() def funcFacilitiesToCSV(self): indices = self.funcFacilitiesCheckBox() if indices: CSV(self, "facilities", indices) else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" ) @Slot() def funcFacilitiesToXLSX(self): indices = self.funcFacilitiesCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp to when it was created to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/FacilitiesXLSX" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".xlsx", "Excel files (*.xlsx)") if fileName: db.cur.execute("SELECT * FROM facilities") workbook = xlsxwriter.Workbook(fileName) worksheet = workbook.add_worksheet("Facilities") worksheet.set_column('A:C', 12) worksheet.set_row(0, 30) merge_format = workbook.add_format({ 'bold': 1, 'align': 'center', 'valign': 'vcenter' }) worksheet.merge_range('A1:B1', '', merge_format) worksheet.insert_image( 'A1', './assets/logo/logo-full-main.png', { 'x_scale': 0.4, 'y_scale': 0.4, 'x_offset': 15, 'y_offset': 10 }) # Create header row col = 0 for value in db.cur.description: worksheet.write(1, col, value[0]) col += 1 # Write date to xlsx file row_number = 2 for index in range(len(indices)): query = "SELECT * FROM facilities WHERE facility_id=?" facility_record = db.cur.execute( query, (indices[index], )).fetchone() for i, value in enumerate(facility_record): worksheet.write(row_number, i, value) row_number += 1 workbook.close() QMessageBox.information( self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" ) @Slot() def funcFacilitiesToPdf(self): indices = self.funcFacilitiesCheckBox() if indices: try: date = datetime.datetime.now() # Get file location and add timestamp to when it was created to the filename fileName, _ = QFileDialog.getSaveFileName( self, "Save as...", "~/FacilitiesPDF" + "{:%d%b%Y_%Hh%Mm}".format(date) + ".pdf", "PDF files (*.pdf)") if fileName: pdf = PDF() pdf.add_page() pdf.set_font('Arial', 'B', 13) for index in range(len(indices)): query = "SELECT * FROM facilities WHERE facility_id=?" facility_record = db.cur.execute( query, (indices[index], )).fetchone() # This string allows for text formatting in the pdf, easy to implement and test stringFacility = "\nFacility id: " + str(facility_record[0]) + \ "\nFacility name: " + str(facility_record[1]) + \ "\nLocation: " + str(facility_record[2]) + \ "\nPhone: " + str(facility_record[3]) + \ "\nEmail: " + str(facility_record[4]) + \ "\nSupervisor: " + str(facility_record[5]) pdf.multi_cell(200, 10, stringFacility) pdf.output(fileName, 'F') QMessageBox.information( self, "Info", "Data exported successfully into {}".format(fileName)) except: QMessageBox.information(self, "Info", "Export failed") else: QMessageBox.information( self, "Info", "Nothing selected for export\nUse checkboxes to select issues to export" )
class defaultRepos(confStack): def __init_stack__(self): self.dbg = False self._debug("confDefault Load") self.menu_description = (_("Choose the default repositories")) self.description = (_("Default repositories")) self.icon = ('go-home') self.tooltip = (_( "From here you can activate/deactivate the default repositories")) self.index = 1 self.enabled = True self.defaultRepos = {} self.changed = [] self.level = 'user' #def __init__ def _load_screen(self): box = QVBoxLayout() lbl_txt = QLabel(_("Enable or disable default repositories")) lbl_txt.setAlignment(Qt.AlignTop) box.addWidget(lbl_txt, 0) self.table = QTableWidget(1, 2) Hheader = self.table.horizontalHeader() Vheader = self.table.verticalHeader() Hheader.setSectionResizeMode(0, QHeaderView.Stretch) Vheader.setSectionResizeMode(QHeaderView.ResizeToContents) self.table.setShowGrid(False) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setSelectionMode(QTableWidget.NoSelection) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.horizontalHeader().hide() self.table.verticalHeader().hide() box.addWidget(self.table) self.setLayout(box) self.updateScreen() #def _load_screen def updateScreen(self): self.table.clearContents() self.changed = [] while self.table.rowCount(): self.table.removeRow(0) config = self.getConfig() try: n4dclass = "RepoManager" n4dmethod = "list_default_repos" repos = self.n4dQuery(n4dclass, n4dmethod) if isinstance(repos, str): #It's a string, something went wrong. Perhaps a llx16 server? if (repos == "METHOD NOT ALLOWED FOR YOUR GROUPS"): #Server is a llx16 so switch to localhost self._debug("LLX16 server detected. Switch to localhost") self.errServer = True repos = self.n4dQuery(n4dclass, n4dmethod) if repos.get('status', 0) != -1: self.defaultRepos = repos.copy() except Exception as e: self._debug(self.n4dQuery(n4dclass, n4dmethod)) states = {} row = 0 for repo, data in self.defaultRepos.items(): self.table.insertRow(row) state = data.get('enabled', 'false') if state == 'true': state = True else: state = False description = data.get('desc', '') lbl = QLabelDescription(repo, _(description)) self.table.setCellWidget(row, 0, lbl) chk = QCheckBox() chk.setTristate(False) chk.setStyleSheet("margin-left:50%;margin-right:50%") chk.stateChanged.connect(lambda x: self.setChanged(True)) chk.stateChanged.connect(self.changeState) self.table.setCellWidget(row, 1, chk) chk.setChecked(state) row += 1 #def _udpate_screen def changeState(self): row = self.table.currentRow() repoWidget = self.table.cellWidget(row, 0) stateWidget = self.table.cellWidget(row, 1) if repoWidget == None: self._debug("Item not found at %s,%s" % (row, 0)) return repo = repoWidget.text()[0] #Check mirror if repo.lower() == "lliurex mirror": #Mirror must be checked against server ret = self.appConfig.n4dQuery("MirrorManager", "is_mirror_available") if isinstance(ret, dict): if ret.get('status') == -1: self._debug("Mirror not available") self.showMsg(_("Mirror not available"), 'RepoMan') self.defaultRepos[repo]['enabled'] = "False" self.updateScreen() return if (type(ret) == type("")): if ret != "Mirror available": self._debug("Mirror not available") self.showMsg(_("Mirror not available"), 'RepoMan') self.defaultRepos[repo]['enabled'] = "False" self.updateScreen() return elif not (ret.get('status', False)): self._debug("Mirror not available") self.showMsg(_("Mirror not available"), 'RepoMan') self.defaultRepos[repo]['enabled'] = "False" self.updateScreen() return state = str(stateWidget.isChecked()).lower() self.defaultRepos[repo]['enabled'] = "%s" % state if repo not in self.changed: self.changed.append(repo) #def changeState def writeConfig(self): ret = True for repo in self.changed: self._debug("Updating %s" % repo) ret = self.n4dQuery("RepoManager", "write_repo_json", {repo: self.defaultRepos[repo]}) if ret: ret = self.n4dQuery("RepoManager", "write_repo", {repo: self.defaultRepos[repo]}) if ret == False: self.showMsg( _("Couldn't write repo") + " {}".format(repo), 'error') else: self.showMsg( _("Couldn't write info for") + " {}".format(repo), 'error') if ret == True: self._updateRepos() self.updateScreen() #def writeConfig def _updateRepos(self): cursor = QtGui.QCursor(Qt.WaitCursor) self.setCursor(cursor) self._debug("Updating repos") ret = self.appConfig.n4dQuery("RepoManager", "update_repos") errList = [] for line in ret.split("\n"): if line.startswith("E: ") or line.startswith("W:"): for name, data in self.defaultRepos.items(): for repoLine in data.get('repos', []): repoItems = repoLine.split(" ") if repoItems: if repoItems[0] in line: if "NODATA" in line: continue err = " *{}".format(name) if err not in errList: errList.append(err) ret = ("\n").join(errList) if ret: #self.showMsg(_("Repositories updated succesfully")) self._debug("Error updating: %s" % ret) ret = _("Failed to update: ") + "\n" + "{}".format(ret) self.showMsg("{}".format(ret), 'RepoMan') self.refresh = True self.changes = False else: self.showMsg(_("Repositories updated succesfully")) cursor = QtGui.QCursor(Qt.PointingHandCursor) self.setCursor(cursor)
class customRepos(confStack): def __init_stack__(self): self.dbg=False self._debug("confDefault Load") self.menu_description=(_("Manage custom repositories")) self.description=(_("Custom repositories")) self.icon=('menu_new') self.tooltip=(_("From here you can manage your custom repositories")) self.index=2 self.enabled=True self.defaultRepos={} self.level='user' self.changed=[] #def __init__ def _load_screen(self): box=QVBoxLayout() info=QWidget() infoBox=QHBoxLayout() lbl_txt=QLabel(_("Manage extra repositories")) lbl_txt.setAlignment(Qt.AlignTop) infoBox.addWidget(lbl_txt,1) icn_add=QtGui.QIcon().fromTheme('document-new') btn_add=QPushButton() btn_add.clicked.connect(self._addRepo) btn_add.setIcon(icn_add) btn_add.setToolTip(_("Add new repository")) infoBox.addWidget(btn_add,0) info.setLayout(infoBox) box.addWidget(info,0) self.table=QTableWidget(1,2) Hheader=self.table.horizontalHeader() Vheader=self.table.verticalHeader() Hheader.setSectionResizeMode(0,QHeaderView.Stretch) Vheader.setSectionResizeMode(QHeaderView.ResizeToContents) self.table.setShowGrid(False) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setSelectionMode(QTableWidget.NoSelection) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.horizontalHeader().hide() self.table.verticalHeader().hide() box.addWidget(self.table) self.setLayout(box) self.updateScreen() #def _load_screen def updateScreen(self): self.table.clearContents() self.changed=[] while self.table.rowCount(): self.table.removeRow(0) config=self.getConfig() repos=self.appConfig.n4dQuery("RepoManager","list_sources") if isinstance(repos,str): #It's a string, something went wrong. Perhaps a llx16 server? #Server is a llx16 so switch to localhost self._debug("LLX16 server detected. Switch to localhost") self.appConfig.n4d.server='localhost' self.appConfig.n4d.n4dClient=None repos=self.appConfig.n4dQuery("RepoManager","list_sources") elif isinstance(repos,dict): status=repos.get('status',None) if status!=0 and status!=None: self.showMsg(_("N4d is down. Check the state of N4d server")) return self.defaultRepos=repos.copy() states={} row=0 orderedKeys=sorted(self.defaultRepos,key=str.casefold) for repo in orderedKeys: data=self.defaultRepos[repo] self.table.insertRow(row) state=data.get('enabled','false').lower() if state=='true': state=True else: state=False description=data.get('desc','') lbl=QLabelDescription(repo,description) locked=data.get('protected','false') locked=str(locked).lower() lbl.click_on.connect(self.editRepo) if locked=='false': lbl.showEdit() if not state: lbl.stateEdit(False) self.table.setCellWidget(row,0,lbl) chk=QCheckBox() chk.setStyleSheet("margin-left:50%;margin-right:50%") chk.stateChanged.connect(lambda x:self.setChanged(True)) chk.stateChanged.connect(self.changeState) self.table.setCellWidget(row,1,chk) chk.setChecked(state) row+=1 #def _update_screen def editRepo(self,repo,*args): sfile=repo.replace(' ','_') self._debug("Editing {}.list".format(sfile)) fpath="{}.list".format(os.path.join(APT_SRC_DIR,sfile)) if os.path.isfile(fpath) or os.path.isfile(fpath.lower()): if os.path.isfile(fpath.lower()): fpath=fpath.lower() edit=True try: display=os.environ['DISPLAY'] subprocess.run(["xhost","+"]) subprocess.run(["pkexec","scite",fpath],check=True) subprocess.run(["xhost","-"]) except Exception as e: self._debug("_edit_source_file error: %s"%e) edit=False if edit: newrepos=[] wrkfile=os.path.join(APT_SRC_DIR,"{}.list".format(sfile)) if not os.path.isfile(wrkfile): wrkfile=wrkfile.lower() try: with open(wrkfile,'r') as f: for line in f: newrepos.append(line.strip()) except Exception as e: self._debug("_edit_source_file failed: {}".format(e)) if sorted(self.defaultRepos[repo]['repos'])!=sorted(newrepos): self.defaultRepos[repo]['repos']=newrepos self.appConfig.n4dQuery("RepoManager","write_repo_json",{repo:self.defaultRepos[repo]}) self._updateRepos() else: self._debug("File {} not found".format(fpath)) #def _edit_source_file def changeState(self): row=self.table.currentRow() repoWidget=self.table.cellWidget(row,0) stateWidget=self.table.cellWidget(row,1) if repoWidget==None: self._debug("Item not found at {},0".format(row)) return repo=repoWidget.text()[0] state=str(stateWidget.isChecked()).lower() self.defaultRepos[repo]['enabled']="%s"%state if repo not in self.changed: self.changed.append(repo) #def changeState(self) def _addRepo(self): self.stack.gotoStack(idx=4,parms="") #def _addRepo def writeConfig(self): ret=True for repo in self.changed: self._debug("Updating {}".format(repo)) self._debug("Updating %s"%self.defaultRepos[repo]) ret=self.appConfig.n4dQuery("RepoManager","write_repo_json",{repo:self.defaultRepos[repo]}) if ret: ret=self.appConfig.n4dQuery("RepoManager","write_repo",{repo:self.defaultRepos[repo]}) if ret==False: self.showMsg(_("Couldn't write repo")+" {}".format(repo),'error') else: self.showMsg(_("Couldn't write info for")+" {}".format(repo),'error') if ret==True: self._updateRepos() self.updateScreen() #def writeConfig def _updateRepos(self): cursor=QtGui.QCursor(Qt.WaitCursor) self.setCursor(cursor) self._debug("Updating repos") ret=self.appConfig.n4dQuery("RepoManager","update_repos") errList=[] for line in ret.split("\n"): if line.startswith("E: ") or line.startswith("W:"): for name,data in self.defaultRepos.items(): for repoLine in data.get('repos',[]): repoItems=repoLine.split(" ") if repoItems: if repoItems[0] in line: err=" *{}".format(name) if err not in errList: errList.append(err) ret=("\n").join(errList) if ret: #self.showMsg(_("Repositories updated succesfully")) self._debug("Error updating: {}".format(ret)) ret=_("Failed to update: ")+"\n"+"{}".format(ret) self.showMsg("{}".format(ret),'RepoMan') self.refresh=True self.changes=False else: self.showMsg(_("Repositories updated succesfully")) cursor=QtGui.QCursor(Qt.PointingHandCursor) self.setCursor(cursor)