class PredictionsTab(QWidget): def __init__(self, patient): QWidget.__init__(self) self.patient = patient self.prediction_views = set() self.toolbar = QToolBar() self.toolbar.setMovable(False) add_prediction = QAction(QIcon("resources/icon/plus.png"), "Dodaj predykcję", self) add_prediction.triggered.connect(self.addPredictionForm) add_prediction.setStatusTip("Dodaj pomiary") self.toolbar.addAction(add_prediction) delete_prediction = QAction(QIcon("resources/icon/trash-bin.png"), "Usuń predykcję", self) delete_prediction.triggered.connect(self.deletePrediction) delete_prediction.setStatusTip("Usuń pomiar") self.toolbar.addAction(delete_prediction) draw_predictions = QAction(QIcon("resources/icon/graph.png"), "Rysuj predykcje", self) draw_predictions.triggered.connect(self.showPredictions) draw_predictions.setStatusTip("Rysuj predykcje") self.toolbar.addAction(draw_predictions) self.table = QTableWidget() self.table.setSelectionMode(QAbstractItemView.ExtendedSelection) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels(["Id", "Data", "Metoda"]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.verticalHeader().setVisible(False) self.fillTable() main_layout = QHBoxLayout(self) main_layout.setMenuBar(self.toolbar) main_layout.addWidget(self.table) def fillTable(self): for p in self.patient.predictions: self.addPrediction(p) def addPrediction(self, prediction: Prediction): row_count = self.table.rowCount() print(prediction) self.table.insertRow(row_count) self.table.setItem( row_count, 0, self.tableWidgetItem(str(prediction.db_id)), ) self.table.setItem( row_count, 1, self.tableWidgetItem( prediction.datetime_created.strftime(DATETIME_FORMAT)), ) self.table.setItem(row_count, 2, self.tableWidgetItem(prediction.method)) def tableWidgetItem(self, value): item = QTableWidgetItem(value) item.setFlags(item.flags() & ~Qt.ItemIsEditable) return item def addPredictionForm(self): self.predicion_form = PredictionForm(self, self.patient) self.predicion_form.exec() def deletePrediction(self): current_row = self.table.currentRow() if current_row == -1: QMessageBox.warning( self, "Usuwanie predykcji", "Brak danych w tabeli", ) return prediction_datetime = datetime.fromisoformat( self.table.item(current_row, 1).text()) delete_prediction_for_patient(self.patient, prediction_datetime) self.table.removeRow(current_row) def showPredictions(self): logging.debug( "All predictions %s", [(p.method, p.datetime_created) for p in self.patient.predictions], ) selected_rows = set( [ind.row() for ind in self.table.selectedIndexes()]) logging.debug("%s", selected_rows) chosen_predictions_ids: List[int] = [ int(self.table.item(ind, 0).text()) for ind in selected_rows ] logging.debug("Selected db_ids from table: %s", chosen_predictions_ids) chosen_predictions: Set[Prediction] = { prediction for prediction in self.patient.predictions if prediction.db_id in chosen_predictions_ids } logging.debug( "Selected predictions to graph: %s %d %s", chosen_predictions, len(chosen_predictions), type(chosen_predictions), ) predictions_view = PredictionsView( f"{self.patient.name} {self.patient.surname}", self.patient.measurements, chosen_predictions, ) predictions_view.show() self.prediction_views.add(predictions_view) logging.debug("Prediction views %s", self.prediction_views)
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 App(QMainWindow): # ------------------------------------------ # -- Initialize the Class # ------------------------------------------ def __init__(self): super(App, self).__init__() # -- Home Directory self.home_path = expanduser("~") # -- Base Path # self.app_path = os.path.abspath(__file__) self.app_path = sys.argv[0] self.base_path_split = self.app_path.split('/') self.base_path = str('/').join(self.base_path_split[0:-1]) # -- Files under config self.config_file = self.base_path + '/config/config.json' self.connect_file = self.base_path + '/config/connect.sh' self.table_file = self.base_path + '/config/table.csv' self.delimiter_table_row = ';' # -- Data of Config File (with Default Values) self.config_data_file = { 'Message': { 'FileReadSuccess': True, 'FileReadFailed': True, 'ColorRedefinition': True }, 'Window': { 'Left': 100, 'Top': 100, 'Width': 640, 'Height': 480 }, 'Margin': { 'Left': 11, 'Top': 11, 'Right': 11, 'Bottom': 11 }, 'Title': { 'Label': 'SLT - Simple Login Tool - ' + __version__ }, 'Button': { 'Connect': { 'Label': 'Connect' }, 'Message': { 'Label': 'X' } }, 'Table': { 'Header': [], 'Column': { 'Color': 1, # -- Number of column which used for text matching 'Connect': [ 9, 1 ] # -- These are those column numbers which data picked from the selected row and will be used as connection parameters }, 'Cell': { 'Color': {} } } } # -- Data of Table File self.table_data_file = [] # -- Dictionary of Colors with QColor Elements self.color_data = {} # -- Central Widget self.central_widget = None # -- Main Layout self.main_layout = None # -- Input Box self.input_box = None # -- Connect Button self.connect_button = None # -- Message Button self.message_button = None # -- Message Label self.message_label = None self.message_label_count = None # -- Message List self.message_list = [] # -- Main Table self.main_table = None # -- Status Bar self.status_bar = None # -- Messages self.messages = { 'FileReadSuccess': 'INFO: File is opened for read: ', 'FileReadFailed': 'ERROR: File could not be opened for read: ', 'ColorRedefinition': 'WARN: Color Redefinition: ' } # -- Initialize the UI self.initUI() # ------------------------------------------ # -- Create Main Window # ------------------------------------------ # def createMainWindow(self): # if (self.main_window is None): # self.main_window = QMainWindow(self) # ------------------------------------------ # -- Create Central Widget # ------------------------------------------ def createCentralWidget(self): if (self.central_widget is None): self.central_widget = QWidget() # -- Set Central Widget for QMainWindow self.setCentralWidget(self.central_widget) # ------------------------------------------ # -- Create Layout # ------------------------------------------ def createLayout(self): if (self.main_layout is None): self.main_layout = QGridLayout() # ------------------------------------------ # -- Create Input Box # ------------------------------------------ def createInputBox(self): if (self.input_box is None): self.input_box = QLineEdit() # -- Enable Clear Button self.input_box.setClearButtonEnabled(True) # -- Create Event if enter pressed # self.input_box.editingFinished.connect(self.eventSearchInTable) self.input_box.editingFinished.connect(self.eventConnectButtonClicked) # -- Create Event if text changed self.input_box.textChanged.connect(self.eventSearchInTable) # ------------------------------------------ # -- Search Event # ------------------------------------------ def eventSearchInTable(self): # print('Search Event. Look up text is: %s' %(self.input_box.text())) # -- Debug self.refreshTable(self.input_box.text().strip()) # ------------------------------------------ # -- Create Connect Button # ------------------------------------------ def createConnectButton(self, text): if (self.connect_button is None): self.connect_button = QPushButton(text) # -- Add Clicked Event self.connect_button.clicked.connect(self.eventConnectButtonClicked) # ------------------------------------------ # -- Event for Connect Button Clicked # ------------------------------------------ def eventConnectButtonClicked(self): connectParameters = '' row = 0 # print('Connect Button Pressed') # -- Debug if (self.main_table.rowCount() == 1): for column in self.config_data_file['Table']['Column']['Connect']: cellTable = self.main_table.item(row, column) connectParameters += ' ' + str(cellTable.text()) self.connectExecute(connectParameters) # else: # print('More than one item in table, therefore cannot decide which one to choose') # -- Debug # ------------------------------------------ # -- Create Status Bar # ------------------------------------------ def createStatusBar(self): if (self.status_bar is None): self.status_bar = QStatusBar() self.status_bar.addWidget(self.message_label_count, 0) self.status_bar.addWidget(VLine(), 0) self.status_bar.addWidget(self.message_label, 1) self.status_bar.addWidget(self.message_button, 0) # ------------------------------------------ # -- Update Status Bar # ------------------------------------------ def updateStatusBar(self): if (self.status_bar is not None): # -- Hide the Status Bar if the Message List is empty if (len(self.message_list) > 0): self.status_bar.show() else: self.status_bar.hide() # ------------------------------------------ # -- Get Latest Message # ------------------------------------------ def getLatestMessage(self): result = None length = len(self.message_list) if (length > 0): result = self.message_list[length - 1] return result # ------------------------------------------ # -- Create Message Label # ------------------------------------------ def createMessageLabel(self): if (self.message_label is None): self.message_label = QLabel(self) # -- Add a Text to Message Label # self.message_label.setText(self.getLatestMessage()) self.updateMessageLabel() # ------------------------------------------ # -- Update Message Label with Latest Entry # ------------------------------------------ def updateMessageLabel(self): message = '' length = len(self.message_list) if (self.message_label is not None): if (length > 0): message = self.message_list[-1] self.message_label.setText(message) # ------------------------------------------ # -- Create Message Label Count # ------------------------------------------ def createMessageLabelCount(self): if (self.message_label_count is None): self.message_label_count = QLabel(self) self.updateMessageLabelCount() # ------------------------------------------ # -- Update Message Label Count # ------------------------------------------ def updateMessageLabelCount(self): message = '' length = len(self.message_list) if (self.message_label_count is not None): if (length > 0): message = ' ' + str(length) self.message_label_count.setText(message) # ------------------------------------------ # -- Create Message Button # ------------------------------------------ def createMessageButton(self, text): if (self.message_button is None): self.message_button = QPushButton(text) # -- Add Clicked Event self.message_button.clicked.connect(self.eventMessageButtonClicked) # ------------------------------------------ # -- Event for Message Button Clicked # ------------------------------------------ def eventMessageButtonClicked(self): length = len(self.message_list) # -- Remove the latest item in Message List if (length > 0): del self.message_list[length - 1] # -- Update Message Labels self.updateMessageLabel() self.updateMessageLabelCount() # -- Update the Status Bar self.updateStatusBar() # ------------------------------------------ # -- Add a New Message to List # ------------------------------------------ def addNewMessage(self, message): self.message_list.append(message) self.updateMessageLabel() # -- Update Status Bar self.updateStatusBar() # ------------------------------------------ # -- Check Table Header # ------------------------------------------ def checkMainTableHeader(self, hostsList): # -- Length of the Header defined in the config file lengthHeaderconfig_file = len(self.config_data_file['Table']['Header']) # -- Length of the First Record if (len(hostsList) > 0): lengthFirstRecord = len(hostsList[0]) else: lengthFirstRecord = 0 # -- Append the Header if the list in config file is too short if (lengthHeaderconfig_file < lengthFirstRecord): for header in range(lengthHeaderconfig_file + 1, lengthFirstRecord + 1): self.config_data_file['Table']['Header'].append(str(header)) # ------------------------------------------ # -- Create Table # ------------------------------------------ def createMainTable(self, hostsList): numCell = 0 maxTableRow = len(hostsList) maxTableColumn = len(self.config_data_file['Table']['Header']) if (self.main_table is None): self.main_table = QTableWidget() # -- Set the Maximum Size of Table self.main_table.setColumnCount(maxTableColumn) self.main_table.setRowCount(maxTableRow) # -- Create the Horizontal Header of Table headerTableWidget = self.main_table.horizontalHeader() # -- Set the Table Header self.main_table.setHorizontalHeaderLabels( self.config_data_file['Table']['Header']) # -- Hide The Horizontal Table Header # self.main_table.horizontalHeader().setVisible(False) # -- Set the Cells to Read Only self.main_table.setEditTriggers(QTableWidget.NoEditTriggers) # -- Set Table Header Properties for numCell in range(0, len(self.config_data_file['Table']['Header'])): headerTableWidget.setSectionResizeMode( numCell, QHeaderView.ResizeToContents) # -- Set the First Column to Resizeable # headerTableWidget.setSectionResizeMode(0, QHeaderView.Stretch) # -- Set the Last Column to Resizeable headerTableWidget.setSectionResizeMode(maxTableColumn - 1, QHeaderView.Stretch) # -- Add Double Clicked Event on Table self.main_table.itemDoubleClicked.connect( self.eventMainTableDoubleClickedOnCell) # -- Insert Data into Table self.insertDataIntoTable(self.main_table, hostsList) # ------------------------------------------ # -- Double Clicked on Cell Event # ------------------------------------------ def eventMainTableDoubleClickedOnCell(self): connectParameters = '' row = self.main_table.currentRow() # print('Double Clicked on a Table Cell') # -- Debug for column in self.config_data_file['Table']['Column']['Connect']: cellTable = self.main_table.item(row, column) connectParameters += ' ' + str(cellTable.text()) self.connectExecute(connectParameters) # ------------------------------------------ # -- Insert Data into the Table # ------------------------------------------ def insertDataIntoTable(self, inputTable, inputRecords): maxHeaderColumn = len(self.config_data_file['Table']['Header']) maxTableColumn = len(self.config_data_file['Table']['Header']) maxTableRow = len(inputRecords) colorColumn = self.config_data_file['Table']['Column']['Color'] numRecord = 0 numCell = 0 # -- Set the Maximum size of Table inputTable.setColumnCount(maxTableColumn) inputTable.setRowCount(maxTableRow) for record in inputRecords: # print('Record : %s' %(str(record))) # -- Debug for cell in record: # print('Cell : %s' %(cell)) # -- Debug if (numCell < maxHeaderColumn): inputTable.setItem(numRecord, numCell, QTableWidgetItem(cell)) # -- Set the Background Color of Cells # if (record[colorColumn] in self.colorCell): # inputTable.item(numRecord, numCell).setBackground(self.colorCell[record[colorColumn]]) if (record[colorColumn] in self.config_data_file['Table'] ['Cell']['Color']): nameColor = self.config_data_file['Table']['Cell'][ 'Color'][record[colorColumn]] # print('Cell: %s, %s, %s' %(record[colorColumn], nameColor, self.color_data[nameColor])) # -- Debug inputTable.item(numRecord, numCell).setBackground( self.color_data[nameColor]) numCell += 1 numCell = 0 numRecord += 1 inputTable.move(0, 0) # ------------------------------------------ # -- Refresh Table # ------------------------------------------ def refreshTable(self, searchText): found = False filteredHostsList = [] # print('Refresh table data.') # -- Debug # -- Clean the Table for row in range(self.main_table.rowCount() - 1, -1, -1): # print('Remove row: %s' %(str(row))) # -- Debug self.main_table.removeRow(row) self.main_table.show() # -- Update the table_data_file with searchText for record in self.table_data_file: found = False for cell in record: if (searchText == '' or cell.lower().find(searchText.lower()) != -1): # print('Found: %s' %(str(cell))) # -- Debug found = True if (found): filteredHostsList.append(record) # -- Recreate Table Data with filtered Values self.insertDataIntoTable(self.main_table, filteredHostsList) # -- Refresh the QTableWidget (required due to screen artifact) self.main_table.hide() self.main_table.show() # ------------------------------------------ # -- Read Config File # ------------------------------------------ def readConfigFile(self, filename): fileHandle = None message = '' try: fileHandle = open(filename, 'r') except IOError: message = self.messages['FileReadFailed'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadFailed'] is True): self.addNewMessage(message) else: message = self.messages['FileReadSuccess'] + filename print(message) # -- Update the Default Data Values with the New Ones # self.config_data_file = json.load(fileHandle) self.config_data_file.update(json.load(fileHandle)) # -- Add Colors to the List for key, value in self.config_data_file['Color'].items(): # -- Check the Length of Value (must have 3 elements [R,G,B]) if (len(value) == 3): self.addColor(key, value[0], value[1], value[2]) # -- Message if (self.config_data_file['Message']['FileReadSuccess'] is True): self.addNewMessage(message) # print('JSON: %s' %(self.config_data_file)) # -- Debug finally: if (fileHandle): fileHandle.close() # ------------------------------------------ # -- Add Color to the Dictionary # ------------------------------------------ def addColor(self, name, red, green, blue): # print('Add Color: %s [%d,%d,%d]' %(name, red, green, blue)) # -- Debug # -- Check Red if (type(red) is int): if (red < 0 or red > 255): red = 255 else: red = 255 # -- Check Green if (type(green) is int): if (green < 0 or green > 255): green = 255 else: green = 255 # -- Check Blue if (type(blue) is int): if (blue < 0 or blue > 255): blue = 255 else: blue = 255 # print('Add Color: %s [%d,%d,%d]' %(name, red, green, blue)) # -- Debug # -- Add the Color to the Dictionary if (name not in self.color_data): self.color_data[name] = QColor(red, green, blue) else: # -- Message if (self.config_data_file['Message']['ColorRedefinition'] is True): self.addNewMessage(self.messages['ColorRedefinition'] + name) # ------------------------------------------ # -- Read CSV File # ------------------------------------------ def readCsvFile(self, filename): fileHandle = None result = [] message = '' try: fileHandle = open(filename, 'r') except IOError: message = self.messages['FileReadFailed'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadFailed'] is True): self.addNewMessage(message) else: message = self.messages['FileReadSuccess'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadSuccess'] is True): self.addNewMessage(message) fileContent = fileHandle.readlines() for line in fileContent: strippedLine = line.strip() if (strippedLine != ''): if (strippedLine[0] != '#'): # result.append(list(strippedLine.split(self.delimiter_table_row))) # -- List Items are not Stripped result.append( list(item.strip() for item in strippedLine.split( self.delimiter_table_row)) ) # -- List Items are Stripped # print(strippedLine) # -- Debug # -- Debug # for line in result: # for column in line: # print('[\'%s\']' %(str(column)), end='') # print('') finally: if (fileHandle): fileHandle.close() return result # ------------------------------------------ # -- Execute the Connect Command in Shell # ------------------------------------------ def connectExecute(self, parameters): # print('Run: %s %s' %(self.connect_file, parameters)) # -- Debug os.system(self.connect_file + ' ' + parameters) # ------------------------------------------ # -- UI Initialization # ------------------------------------------ def initUI(self): # -- Read the Config File self.readConfigFile(self.config_file) # -- Read the Table CSV File self.table_data_file = self.readCsvFile(self.table_file) # -- Create GUI Elements self.createCentralWidget() self.createLayout() self.createInputBox() self.createConnectButton( self.config_data_file['Button']['Connect']['Label']) self.checkMainTableHeader(self.table_data_file) self.createMainTable(self.table_data_file) self.createMessageLabel() self.createMessageLabelCount() self.createMessageButton( self.config_data_file['Button']['Message']['Label']) self.createStatusBar() self.updateStatusBar() # -- Set Window Title self.setWindowTitle(self.config_data_file['Title']['Label']) # -- Set Window Geometry self.setGeometry(self.config_data_file['Window']['Left'], self.config_data_file['Window']['Top'], self.config_data_file['Window']['Width'], self.config_data_file['Window']['Height']) # -- Set Layout Margins self.main_layout.setContentsMargins( self.config_data_file['Margin']['Left'], self.config_data_file['Margin']['Top'], self.config_data_file['Margin']['Right'], self.config_data_file['Margin']['Bottom']) # -- Set Layout self.central_widget.setLayout(self.main_layout) # -- Add Widgets to Layout self.main_layout.addWidget(self.input_box, 0, 0, 1, 1) self.main_layout.addWidget(self.connect_button, 0, 1, 1, 1) self.main_layout.addWidget(self.main_table, 1, 0, 1, 2) # -- Set Status Bar for QMainWindow self.setStatusBar(self.status_bar)
class PatientListView(QWidget): columns = ["Identyfikator", "Imię", "Nazwisko"] def __init__(self): QWidget.__init__(self) self.toolbar = QToolBar() self.toolbar.setMovable(False) btn_ac_adduser = QAction(QIcon("resources/icon/add-user.png"), "Add Patient", self) btn_ac_adduser.triggered.connect(self.addPatientForm) btn_ac_adduser.setStatusTip("Add Student") self.toolbar.addAction(btn_ac_adduser) btn_ac_delete = QAction(QIcon("resources/icon/remove-user.png"), "Delete Patient", self) btn_ac_delete.triggered.connect(self.deleteCurrentPatient) btn_ac_delete.setStatusTip("Delete User") self.toolbar.addAction(btn_ac_delete) self.table = QTableWidget() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setColumnCount(len(self.columns)) self.table.verticalHeader().setVisible(False) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.setHorizontalHeaderLabels(self.columns) self.reload() layout = QHBoxLayout(self) layout.setMenuBar(self.toolbar) layout.addWidget(self.table) def fillTable(self): for patient in self.data: self.addPatient(patient) def addPatient(self, patient): ind = self.table.rowCount() self.table.insertRow(ind) self.table.setItem(ind, 0, self.tableWidgetItem(str(patient.db_id))) self.table.setItem(ind, 1, self.tableWidgetItem(patient.name)) self.table.setItem(ind, 2, self.tableWidgetItem(patient.surname)) def tableWidgetItem(self, value): item = QTableWidgetItem(value) item.setFlags(item.flags() & ~Qt.ItemIsEditable) return item def addPatientForm(self): logging.debug("Entering PatientForm") self.patient_form = PatientForm() answer = self.patient_form.exec() if answer == QDialog.Accepted: self.reload() logging.debug(f"Patient {self.patient_form.patient} added") def deleteCurrentPatient(self): if self.table.rowCount() == 0: QMessageBox.warning( self, "Usuwanie pacjenta", "Brak danych w tabeli", ) return current_row = self.table.currentRow() if current_row == -1: QMessageBox.warning( self, "Usuwanie pacjenta", "Brak wskazania na pacjenta", ) return patient = self.data[current_row] answer = QMessageBox.question( self, "Usuwanie pacjenta", f"Czy na pewno chcesz usunąć pacjenta {patient}?", ) if answer == QMessageBox.Yes: delete_patient(patient) self.reload() logging.debug("Deleting PatientForm") def reload(self): self.table.setRowCount(0) self.data = get_all_patients() self.fillTable()
class TreatmentsTab(QWidget): def __init__(self, patient): QWidget.__init__(self) self.patient = patient self.toolbar = QToolBar() self.toolbar.setMovable(False) add_treatment = QAction( QIcon("resources/icon/plus.png"), "Podaj lekarstwo", self ) add_treatment.triggered.connect(self.addTreatmentForm) add_treatment.setStatusTip("Dodaj pomiary") self.toolbar.addAction(add_treatment) delete_treatment = QAction( QIcon("resources/icon/trash-bin.png"), "Usuń podanie lekarstwa", self ) delete_treatment.triggered.connect(self.deleteCurrentTreatment) delete_treatment.setStatusTip("Usuń pomiar") self.toolbar.addAction(delete_treatment) self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["Data", "Zawartość lekarstwa"]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.fillTable(patient.treatments) main_layout = QHBoxLayout(self) main_layout.setMenuBar(self.toolbar) main_layout.addWidget(self.table) def addTreatmentForm(self): logging.debug("Entering TreatmentForm of patient %s", self.patient) self.treatment_form = TreatmentForm(self, self.patient) self.treatment_form.show() def fillTable(self, treatments): for tr in treatments: self.addTreatment(tr) def addTreatment(self, treatment): row_count = self.table.rowCount() self.table.insertRow(row_count) self.table.setItem( row_count, 0, self.tableWidgetItem(treatment.date.isoformat()) ) self.table.setItem(row_count, 1, self.tableWidgetItem(str(treatment.amount))) def tableWidgetItem(self, value): item = QTableWidgetItem(value) item.setFlags(item.flags() & ~Qt.ItemIsEditable) return item def deleteCurrentTreatment(self): if self.table.rowCount() == 0: QMessageBox.warning( self, "Usuwanie podania lekarstwa", "Brak danych w tabeli", ) return current_row = self.table.currentRow() if current_row == -1: QMessageBox.warning( self, "Usuwanie lekarstwa", "Brak wskazania na żaden pomiar", ) return treatment = self.patient.treatments[current_row] answer = QMessageBox.question( self, "Usuwanie pomiaru", f"Czy na pewno chcesz usunąć podanie lekarstwa {treatment}?", ) if answer == QMessageBox.Yes: delete_treatment_for_patient(self.patient, treatment) self.table.removeRow(current_row) logging.debug(f"Deleting {treatment}")
class TableWidget(QWidget): def __init__(self, table, headers, bold=True, mono=True, tooltips=None, align=False, search=True, parent=None): super(TableWidget, self).__init__(parent) self.table_widget = QTableWidget(len(table), len(table[0])) for i, row in enumerate(table): for j, item in enumerate(row): if item is not None: self.table_widget.setItem(i, j, QTableWidgetItem(str(item))) if tooltips is not None: self.table_widget.setToolTip(tooltips[i][j]) modify_font(self.table_widget.item(i, j), bold=bold and j == 0, mono=mono) if align: self.table_widget.item(i, j).setTextAlignment( Qt.AlignRight) self.table_headers = headers self.table_widget.setHorizontalHeaderLabels(self.table_headers) self.table_widget.setSelectionMode(QAbstractItemView.SingleSelection) self.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_widget.resizeColumnsToContents() self.table_widget.setAlternatingRowColors(True) self.table_widget.itemDoubleClicked.connect(self.copy) search_layout = QHBoxLayout() search_layout.addWidget(QLabel(self.tr('Search:'))) self.search_edit = QLineEdit() self.search_edit.textChanged.connect(self.start) self.search_edit.returnPressed.connect(self.next) search_layout.addWidget(self.search_edit) clear_button = QToolButton() clear_button.setIcon(QIcon('icons/clear.svg')) clear_button.setShortcut(QKeySequence.DeleteCompleteLine) clear_button.setToolTip(self.tr('Clear pattern')) clear_button.clicked.connect(self.search_edit.clear) search_layout.addWidget(clear_button) self.case_button = QToolButton() self.case_button.setText(self.tr('Aa')) self.case_button.setCheckable(True) self.case_button.toggled.connect(self.start) self.case_button.setToolTip(self.tr('Case sensitive')) search_layout.addWidget(self.case_button) self.word_button = QToolButton() self.word_button.setText(self.tr('W')) self.word_button.setCheckable(True) self.word_button.toggled.connect(self.start) self.word_button.setToolTip(self.tr('Whole words')) search_layout.addWidget(self.word_button) self.regex_button = QToolButton() self.regex_button.setText(self.tr('.*')) self.regex_button.setCheckable(True) self.regex_button.toggled.connect(self.start) self.regex_button.setToolTip(self.tr('Regular expression')) search_layout.addWidget(self.regex_button) prev_button = QToolButton() prev_button.setIcon(QIcon('icons/up.svg')) prev_button.setShortcut(QKeySequence.FindPrevious) prev_button.clicked.connect(self.previous) prev_button.setToolTip(self.tr('Previous occurence')) search_layout.addWidget(prev_button) next_button = QToolButton() next_button.setIcon(QIcon('icons/down.svg')) next_button.setShortcut(QKeySequence.FindNext) next_button.clicked.connect(self.next) next_button.setToolTip(self.tr('Next occurence')) search_layout.addWidget(next_button) self.matches_label = QLabel() search_layout.addWidget(self.matches_label) search_layout.addStretch() export_button = QToolButton() export_button.setText(self.tr('Export...')) export_button.clicked.connect(self.export) search_layout.addWidget(export_button) main_layout = QVBoxLayout() main_layout.addWidget(self.table_widget) if search: main_layout.addLayout(search_layout) self.setLayout(main_layout) def start(self): self.search(self.search_edit.text(), -1, -1, 1) def next(self): row = self.table_widget.currentRow() col = self.table_widget.currentColumn() + 1 if col == self.table_widget.columnCount(): row += 1 col = 0 self.search(self.search_edit.text(), row, col, +1) def previous(self): row = self.table_widget.currentRow() col = self.table_widget.currentColumn() - 1 if col == -1: row -= 1 col = self.table_widget.columnCount() - 1 self.search(self.search_edit.text(), row, col, -1) def search(self, pattern, row, col, direction): nocase = not self.case_button.isChecked() word = self.word_button.isChecked() regex = self.regex_button.isChecked() matches = 0 index = 0 if direction > 0: row_range = range(self.table_widget.rowCount() - 1, -1, -1) col_range = range(self.table_widget.columnCount() - 1, -1, -1) else: row_range = range(self.table_widget.rowCount()) col_range = range(self.table_widget.columnCount()) for i in row_range: for j in col_range: item = self.table_widget.item(i, j) if item is not None: text = item.text() if regex: match = QRegularExpression(pattern).match( text).hasMatch() else: if nocase: text = text.lower() pattern = pattern.lower() if word: match = text == pattern else: match = pattern in text if match and pattern: self.table_widget.item(i, j).setBackground(Qt.yellow) if (direction > 0 and (i > row or i == row and j > col)) or \ (direction < 0 and (i < row or i == row and j < col)): self.table_widget.setCurrentCell(i, j) index = matches matches += 1 else: self.table_widget.item(i, j).setBackground(Qt.transparent) if pattern: self.matches_label.setVisible(True) if matches > 0: match = matches - index if direction > 0 else index + 1 self.matches_label.setText( self.tr('match #{}/{}'.format(match, matches))) self.matches_label.setStyleSheet('color: #000000') modify_font(self.matches_label, bold=True) else: self.matches_label.setText(self.tr('not found!')) self.matches_label.setStyleSheet('color: #FF0000') modify_font(self.matches_label, italic=True) else: self.matches_label.setText('') def export(self): settings = QSettings() filename = QFileDialog.getSaveFileName(self, self.tr('Export metadata'), settings.value('save_folder'), self.tr('CSV files (*.csv)'))[0] if not filename: return if not filename.endswith('.csv'): filename += '.csv' settings.setValue('save_folder', QFileInfo(filename).absolutePath()) rows = self.table_widget.rowCount() cols = self.table_widget.columnCount() table = [[None for _ in range(cols)] for __ in range(rows)] for i in range(rows): for j in range(cols): item = self.table_widget.item(i, j) if item is not None: table[i][j] = item.text() with open(filename, 'w') as file: writer = csv.writer(file) writer.writerow(self.table_headers) writer.writerows(table) self.info_message.emit(self.tr('Table contents exported to disk')) def copy(self, item): QApplication.clipboard().setText(item.text()) QToolTip.showText(QCursor.pos(), self.tr('Cell contents copied to clipboard'), self, QRect(), 3000)
class GeneratedCurvePage(DetailsPageBase): def __init__(self, parent=None): super().__init__(parent, 'synthetic curve') self.gen_curve: Optional[WdGeneratedValues] = None self.segments_edit_semaphore = Lock() layout = QVBoxLayout() group_segments = QGroupBox('Segments') layout_segments = QVBoxLayout() layout_segments_hdr = QHBoxLayout() layout_segments_hdr_labels = QVBoxLayout() layout_segments_hdr_labels.addWidget( QLabel('Segments are calculated in parallel')) layout_segments_hdr_labels.addWidget( QLabel('and may have different density (PHIN)')) layout_segments_hdr.addLayout(layout_segments_hdr_labels) layout_segments_hdr.addStretch() self.add_segment_button = QToolButton() self.add_segment_button.setText('+') self.del_segment_button = QToolButton() self.del_segment_button.setText('-') layout_segments_hdr.addWidget(self.add_segment_button) layout_segments_hdr.addWidget(self.del_segment_button) layout_segments.addLayout(layout_segments_hdr) self.table_segments = QTableWidget(1, 3) self.table_segments.setHorizontalHeaderLabels( ['phase from', 'phase to', 'PHIN phase incr']) layout_segments.addWidget(self.table_segments) group_segments.setLayout(layout_segments) layout.addWidget(group_segments) self.setLayout(layout) self.table_segments.itemActivated.connect(self._on_segment_activated) self.table_segments.itemChanged.connect(self._on_segment_item_changed) self.add_segment_button.clicked.connect(self._on_segment_add_clicked) self.del_segment_button.clicked.connect(self._on_segment_del_clicked) def is_active_for_item( self, item: Optional[Container]) -> (bool, Optional[Container]): """Returns pair: should_be_active and item (may be e.g. parent of provided item)""" try: if isinstance(item, CurveGeneratedContainer): return True, item elif isinstance(item.parent(), CurveGeneratedContainer): return True, item.parent() else: return False, None except AttributeError: return False, None @Slot(QTableWidgetItem) def _on_segment_activated(self, item: QTableWidgetItem): self.del_segment_button.setEnabled(self.table_segments.rowCount() > 1) self.add_segment_button.setEnabled( self.table_segments.rowCount() <= 20) @Slot(QTableWidgetItem) def _on_segment_item_changed(self, item: QTableWidgetItem): """when user enters new value to segments table""" if self.segments_edit_semaphore.locked( ): # item is being updated from code rather than by user return val = float(item.text()) logger().info(f'Segment value changed: {val}') if item.column() == 0: self.gen_curve.segment_set_range(segment=item.row(), from_value=val) elif item.column() == 1: self.gen_curve.segment_set_range(segment=item.row(), to_value=val) elif item.column() == 2: self.gen_curve.segment_update_data(segment=item.row(), data={'PHIN': val}) self.populate_segments() @Slot(bool) def _on_segment_add_clicked(self, selected: bool): """on delete segment button""" row = self.table_segments.currentRow() if row is None: return self.gen_curve.segment_split(row) self.populate_segments() @Slot(bool) def _on_segment_del_clicked(self, selected: bool): """on add new segment button""" row = self.table_segments.currentRow() if self.table_segments.rowCount() < 2 or row is None: return self.gen_curve.segment_delete(row) self.populate_segments() def item_changed(self, previous_item: Container): """when curved item for which details are displayed hve been changed""" super().item_changed(previous_item) if self.enabled: try: if isinstance(self.item.content, WdGeneratedValues): self.gen_curve = self.item.content else: self.gen_curve = None except AttributeError: self.gen_curve = None self.populate_segments() # try: # df = self.item.get_df() # except AttributeError: # df = None # self.pandas.setDataFrame(df) def populate_segments(self): if self.gen_curve is None: self.table_segments.setRowCount(0) return self.table_segments.setRowCount(self.gen_curve.segments_count()) for n in range(self.gen_curve.segments_count()): start, stop = self.gen_curve.segment_range(n) phin = self.gen_curve.segment_get_data(n, 'PHIN') self.set_segment_text(n, 0, start) self.set_segment_text(n, 1, stop) self.set_segment_text(n, 2, phin) self.table_segments.resizeRowsToContents() def set_segment_text(self, row, col, txt): with self.segments_edit_semaphore: item = self.table_segments.item(row, col) if item is None: item = QTableWidgetItem() self.table_segments.setItem(row, col, item) item.setText(str(txt))
class AddRecipeWidget(QWidget): def __init__(self, database, parent): QWidget.__init__(self) self.recipe_ingredients = 0 self.add_recipe_win = QWidget() self.add_recipe_win.setFixedWidth(400) self.add_recipe_win.setWindowTitle("Add Recipe") add_rec_main_layout = QVBoxLayout() self.meal_planner_db = database self.setParent(parent) self.rec_name_input = QLineEdit() self.rec_desc_input = QLineEdit() self.rec_source_input = QLineEdit() rec_form_layout = QFormLayout() rec_form_layout.addRow(QLabel("Recipe Name"), self.rec_name_input) rec_form_layout.addRow(QLabel("Recipe Desc"), self.rec_desc_input) rec_form_layout.addRow(QLabel("Recipe Source"), self.rec_source_input) add_rec_main_layout.addLayout(rec_form_layout) rec_info_layout = QHBoxLayout() difficulty_layout = QVBoxLayout() self.difficulty_spinbox = QSpinBox() self.difficulty_spinbox.setRange(1, 5) self.difficulty_spinbox.setValue(3) difficulty_layout.addWidget(QLabel("Difficulty")) difficulty_layout.addWidget(self.difficulty_spinbox) prep_time_layout = QVBoxLayout() self.prep_time_spinbox = QSpinBox() self.prep_time_spinbox.setRange(0, 600) self.prep_time_spinbox.setValue(30) prep_time_layout.addWidget(QLabel("Prep. Time (min.)")) prep_time_layout.addWidget(self.prep_time_spinbox) rating_layout = QVBoxLayout() self.rating_spinbox = QSpinBox() self.rating_spinbox.setRange(1, 5) self.rating_spinbox.setValue(3) rating_layout.addWidget(QLabel("Rating")) rating_layout.addWidget(self.rating_spinbox) rec_info_layout.addLayout(difficulty_layout) rec_info_layout.addLayout(prep_time_layout) rec_info_layout.addLayout(rating_layout) add_rec_main_layout.addLayout(rec_info_layout) rec_tags_layout = QHBoxLayout() rec_cuisine_layout = QVBoxLayout() cuisines = [ "Mexican", "Swedish", "Austrian", "Italian", "Spanish", "American", "British", "Thai", "Greek", "Vietnamese", "Caribbean", "Japanese", "Chinese", "Indian", "French", "Swiss", "Portuguese", "Korean", "Turkish", "Moroccan", "Russian", "Malaysian", "Philippines", "Ethiopian", "Lebanese", "Arab", "Peruvian", "Brazilian", "Asian", "Middle Eastern", "South American", "African", "-" ] cuisines.sort() self.tag_cuisine = QComboBox() self.tag_cuisine.addItems(cuisines) rec_cuisine_layout.addWidget(QLabel("Cuisine")) rec_cuisine_layout.addWidget(self.tag_cuisine) rec_tags_layout.addLayout(rec_cuisine_layout) rec_category_layout = QVBoxLayout() categories = [ "Beef & Calf", "Chicken & Poultry", "Lamb", "Pork", "Preservation", "Salad", "Sandwich", "Soup", "Stew", "Pasta", "Rice", "Grain & Beans", "Fish & Seafood", "Vegetables", "Eggs & Cheese", "BBQ", "Fruits", "Cake & Pie (Sweet)", "Pie", "Bread", "Beverage", "Cookies & Sweets", "Sauce", "-" ] categories.sort() self.tag_category = QComboBox() self.tag_category.addItems(categories) rec_category_layout.addWidget(QLabel("Category")) rec_category_layout.addWidget(self.tag_category) rec_tags_layout.addLayout(rec_category_layout) rec_meal_type_layout = QVBoxLayout() meal_types = [ "Breakfast", "Brunch", "Lunch", "Dinner", "Dessert", "Starter", "Side", "Buffet", "Snack", "-" ] meal_types.sort() self.tag_meal_types = QComboBox() self.tag_meal_types.addItems(meal_types) rec_meal_type_layout.addWidget(QLabel("Meal Type")) rec_meal_type_layout.addWidget(self.tag_meal_types) rec_tags_layout.addLayout(rec_meal_type_layout) add_rec_main_layout.addLayout(rec_tags_layout) self.ingredient_name_input = QLineEdit() self.ingredient_qty_input = QDoubleSpinBox() self.ingredient_qty_input.setValue(1.0) self.ingredient_qty_input.setMaximum(1000) self.ingredient_qty_input.setDecimals(2) self.ingredient_unit_input = QComboBox() self.ingredient_unit_input.addItems( ["g", "ml", "dl", "l", "msk", "tsk", "st", "-"]) add_ingredient_btn = QPushButton("Create ingredient") add_ingredient_btn.clicked.connect(self.create_ingredient) add_ingredient_layout = QHBoxLayout() add_rec_main_layout.addWidget(QLabel("Ingredient")) add_ingredient_layout.addWidget(self.ingredient_name_input) add_ingredient_layout.addWidget(self.ingredient_qty_input) add_ingredient_layout.addWidget(self.ingredient_unit_input) add_ingredient_layout.addWidget(add_ingredient_btn) add_rec_main_layout.addLayout(add_ingredient_layout) btn_layout = QHBoxLayout() add_ingredient_to_recipe_btn = QPushButton("Add ingredient") add_ingredient_to_recipe_btn.clicked.connect( self.add_ingredient_to_recipe) del_ingredient_from_recipe_btn = QPushButton("Remove ingredient") del_ingredient_from_recipe_btn.clicked.connect( self.del_ingredient_from_recipe) btn_layout.addWidget(add_ingredient_to_recipe_btn) btn_layout.addWidget(del_ingredient_from_recipe_btn) add_rec_main_layout.addLayout(btn_layout) self.rec_ingredient_table = QTableWidget() self.rec_ingredient_table.setColumnCount(3) self.rec_ingredient_table.setHorizontalHeaderLabels( ["Amount", "Unit", "Ingredient"]) header = self.rec_ingredient_table.horizontalHeader() header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) add_rec_main_layout.addWidget(self.rec_ingredient_table) self.step_count = 0 self.add_recipe_step_btn = QPushButton("Add recipe instruction") add_rec_main_layout.addWidget(self.add_recipe_step_btn) self.add_recipe_step_btn.clicked.connect(self.add_recipe_step_win) self.rec_step_table = QTableWidget() self.rec_step_table.setColumnCount(1) self.rec_step_table.setHorizontalHeaderLabels(["Instructions"]) self.rec_step_table.setWordWrap(True) header = self.rec_step_table.horizontalHeader() header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) add_rec_main_layout.addWidget(self.rec_step_table) bottom_btn_layout = QHBoxLayout() self.add_rec_btn = QPushButton("Add recipe") self.add_rec_btn.clicked.connect(self.add_recipe_to_db) self.back_btn = QPushButton("Cancel") self.back_btn.clicked.connect(self.add_recipe_win.close) bottom_btn_layout.addWidget(self.add_rec_btn) bottom_btn_layout.addWidget(self.back_btn) add_rec_main_layout.addLayout(bottom_btn_layout) self.add_recipe_win.setLayout(add_rec_main_layout) self.add_recipe_win.show() @Slot() def add_recipe_step_win(self): self.rec_step_table.insertRow(self.step_count) self.step_count += 1 @Slot() def create_ingredient(self): self.add_ingredient_widget = AddIngredientWidget( self.meal_planner_db, self, self.ingredient_name_input.text()) @Slot() def add_ingredient_to_recipe(self): if self.meal_planner_db.ingredient_exists( self.ingredient_name_input.text()): self.rec_ingredient_table.insertRow(self.recipe_ingredients) self.rec_ingredient_table.setItem( self.recipe_ingredients, 0, QTableWidgetItem(str(self.ingredient_qty_input.value()))) self.rec_ingredient_table.setItem( self.recipe_ingredients, 1, QTableWidgetItem(self.ingredient_unit_input.currentText())) self.rec_ingredient_table.setItem( self.recipe_ingredients, 2, QTableWidgetItem(self.ingredient_name_input.text())) self.recipe_ingredients += 1 self.ingredient_name_input.clear() self.ingredient_qty_input.setValue(1.0) self.ingredient_unit_input.setCurrentIndex(0) else: print("Ingredient does not exist in database, please add it first") @Slot() def del_ingredient_from_recipe(self): recipe = self.rec_name_input.text() ingredient = self.rec_ingredient_table.currentItem().text() self.rec_ingredient_table.removeRow( self.rec_ingredient_table.currentRow()) self.meal_planner_db.del_recipe_ingredient(recipe, ingredient) @Slot() def add_recipe_to_db(self): rec_id = self.meal_planner_db.get_table_len("recipes") self.meal_planner_db.add_recipe(rec_id, self.rec_name_input.text(), self.rec_desc_input.text(), self.rec_source_input.text(), self.difficulty_spinbox.value(), self.prep_time_spinbox.value(), self.rating_spinbox.value(), "2000-01-01", 0) for row in range(self.rec_ingredient_table.rowCount()): qty_id = self.meal_planner_db.get_table_len("measurement_qty") qty_id = self.meal_planner_db.add_qty( qty_id, self.rec_ingredient_table.item(row, 0).text()) unit_id = self.meal_planner_db.get_table_len("measurement_units") unit_id = self.meal_planner_db.add_measurement( unit_id, self.rec_ingredient_table.item(row, 1).text()) ing_id = self.meal_planner_db.get_ingredient_id( self.rec_ingredient_table.item(row, 2).text()) if ing_id == -1: print("INGREDIENT DOES NOT EXIST! WE F****D UP!") break self.meal_planner_db.add_recipe_ingredient(rec_id, ing_id, unit_id, qty_id) for row in range(self.step_count): print(row, self.rec_step_table.item(row, 0).text()) self.meal_planner_db.add_step( rec_id, row, self.rec_step_table.item(row, 0).text()) # Cuisine tag tag_id = self.meal_planner_db.get_table_len("tags") tag_id = self.meal_planner_db.add_tag(tag_id, self.tag_cuisine.currentText()) self.meal_planner_db.add_recipe_tag(tag_id, rec_id) # Category tag tag_id = self.meal_planner_db.get_table_len("tags") tag_id = self.meal_planner_db.add_tag(tag_id, self.tag_category.currentText()) self.meal_planner_db.add_recipe_tag(tag_id, rec_id) # Meal type tag tag_id = self.meal_planner_db.get_table_len("tags") tag_id = self.meal_planner_db.add_tag( tag_id, self.tag_meal_types.currentText()) self.meal_planner_db.add_recipe_tag(tag_id, rec_id) self.rec_name_input.clear() self.rec_desc_input.clear() self.rec_ingredient_table.setRowCount(0) self.rec_step_table.setRowCount(0) self.step_count = 0 self.parent().update_recipe_table()
class GameControlPanel(QWidget): def __init__(self, first: str, second: str, parent=None): super(GameControlPanel, self).__init__(parent) # buttons on the top self.flipButton = QPushButton("*") self.toStartFenButton = QPushButton("<<") self.previousMoveButton = QPushButton("<") self.nextMoveButton = QPushButton(">") self.toCurrentFenButton = QPushButton(">>") self.flipButton.setFixedWidth(45) self.toStartFenButton.setFixedWidth(45) self.previousMoveButton.setFixedWidth(45) self.nextMoveButton.setFixedWidth(45) self.toStartFenButton.setFixedWidth(45) self.toStartFenButton.setDisabled(True) self.previousMoveButton.setDisabled(True) self.nextMoveButton.setDisabled(True) self.toCurrentFenButton.setDisabled(True) self.toolButtonsLayout = QHBoxLayout() self.toolButtonsLayout.setContentsMargins(0, 0, 0, 0) self.toolButtonsLayout.addWidget(self.flipButton) self.toolButtonsLayout.addWidget(self.toStartFenButton) self.toolButtonsLayout.addWidget(self.previousMoveButton) self.toolButtonsLayout.addWidget(self.nextMoveButton) self.toolButtonsLayout.addWidget(self.toCurrentFenButton) self.toolButtonsLayout.addStretch() self.toolButtonsLayout.setSpacing(14) # the column that contains the first empty cell. self.nextColumn = 0 self.firstMaterial = QLabel() self.firstName = QLabel(first) self.secondName = QLabel(second) self.secondMaterial = QLabel() self.moveTable = QTableWidget() self.moveTable.setEditTriggers(QTableWidget.NoEditTriggers) self.moveTable.setFocusPolicy(Qt.NoFocus) self.moveTable.setSelectionMode(QTableWidget.SingleSelection) self.moveTable.currentCellChanged.connect(self.onCurrentChanged) self.moveTable.setFixedWidth(320) self.moveTable.setColumnCount(2) self.moveTable.horizontalHeader().hide() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addStretch() layout.addWidget(self.firstMaterial) layout.addLayout(self.toolButtonsLayout) layout.addWidget(self.firstName) layout.addWidget(self.moveTable) layout.addWidget(self.secondName) layout.addWidget(self.secondMaterial) layout.addStretch() layout.setSpacing(0) self.setLayout(layout) def isLive(self) -> bool: if not self.moveTable.rowCount(): return True return (self.moveTable.currentRow() == self.moveTable.rowCount() - 1 and self.moveTable.currentColumn() != self.nextColumn) def reset(self): self.moveTable.setRowCount(0) self.nextColumn = 0 self.toStartFenButton.setDisabled(True) self.previousMoveButton.setDisabled(True) self.nextMoveButton.setDisabled(True) self.toCurrentFenButton.setDisabled(True) @Slot(int, int) def onCurrentChanged(self, row, column): self.toStartFenButton.setDisabled(self.moveTable.currentItem() is None) self.previousMoveButton.setDisabled(self.moveTable.currentItem() is None) def toPreviousCell(self): current = self.moveTable.currentItem() if current is not None: row = current.row() column = current.column() if row == column == 0: self.moveTable.setCurrentCell(-1, -1) else: prevCoord = row * 2 + column - 1 if prevCoord % 2: row -= 1 self.moveTable.setCurrentCell(row, not column) def toNextCell(self): current = self.moveTable.currentItem() if self.moveTable.rowCount(): if current is None: self.moveTable.setCurrentCell(0, 0) else: row = current.row() column = current.column() nextCoord = row * 2 + column + 1 if nextCoord % 2 == 0: row += 1 self.moveTable.setCurrentCell(row, not column) def addMove(self, move: str) -> QTableWidgetItem: if self.isLive(): if not self.nextColumn: self.moveTable.setRowCount(self.moveTable.rowCount() + 1) item = QTableWidgetItem(move) self.moveTable.setItem(self.moveTable.rowCount()-1, self.nextColumn, item) self.nextColumn = not self.nextColumn return item def popMove(self): if self.moveTable.rowCount(): if self.isLive(): self.toPreviousCell() self.nextColumn = not self.nextColumn self.moveTable.takeItem(self.moveTable.rowCount() - 1, self.nextColumn) if not self.nextColumn: self.moveTable.setRowCount(self.moveTable.rowCount()-1) def swapNames(self): tempFirstMaterial = self.firstMaterial.text() tempFirstName = self.firstName.text() self.firstMaterial.setText(self.secondMaterial.text()) self.secondMaterial.setText(tempFirstMaterial) self.firstName.setText(self.secondName.text()) self.secondName.setText(tempFirstName)
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)