class MainTable(QWidget): def __init__(self, change_set_a, change_set_b): """ Initialize the MainTable class """ super().__init__() self.rows: list = [] grid = QGridLayout() self.setLayout(grid) self.table = QTableWidget() self.table.setSelectionMode( QtWidgets.QAbstractItemView.SingleSelection) self.file_dropped = "" self.table.setRowCount(0) # Set the initial row count to 0 self.table.setColumnCount(5) # Set the column count to 5 self.setAcceptDrops(True) self.undo_ctrlr: undo_redo.UndoRedo = undo_redo.UndoRedo.get_instance() # List containing indices of all diff rows. This is used for jump to diff functions self.diff_indices: list = [] self.diff_index_block_end: list = [] # Contains the index of the current diff that has been jumped to self.curr_diff_idx: int = -1 self.selected_block: list = [0, 0] self.block_undo_size: list = [] self.block_redo_size: list = [] self.table.cellClicked.connect(self.cellClickedEvent) self.change_set_a = change_set_a self.change_set_b = change_set_b self.left_file: str = "" self.right_file: str = "" self.table.verticalHeader().setVisible( False) # Disable the automatic line numbers. self.table.setVerticalScrollMode(0) # Set the head text self.table.setHorizontalHeaderItem(0, QTableWidgetItem("Line")) self.table.setHorizontalHeaderItem(1, QTableWidgetItem("")) self.table.setHorizontalHeaderItem(2, QTableWidgetItem("Merge\nRight")) self.table.setHorizontalHeaderItem(3, QTableWidgetItem("Merge\nLeft ")) self.table.setHorizontalHeaderItem(4, QTableWidgetItem("")) # Set the header font self.table.horizontalHeader().setFont( gui_cfg.FONTS["TBL_HEADER_DEFAULT"]) # Set the header text alignment self.table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignCenter) self.table.horizontalHeaderItem(1).setTextAlignment(Qt.AlignCenter) self.table.horizontalHeaderItem(2).setTextAlignment(Qt.AlignCenter) self.table.horizontalHeaderItem(3).setTextAlignment(Qt.AlignCenter) self.table.horizontalHeaderItem(4).setTextAlignment(Qt.AlignCenter) # Set column resize modes self.table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.table.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.table.horizontalHeader().setSectionResizeMode( 3, QHeaderView.ResizeToContents) self.table.horizontalHeader().setSectionResizeMode( 4, QHeaderView.Stretch) self.set_tbl_fonts_and_colors() # Set the color and fonts self.table.setGridStyle(Qt.PenStyle(Qt.DotLine)) # Make the table read only for the user self.table.setEditTriggers(QTableWidget.NoEditTriggers) # Convert icon paths from gui_config.py to QIcon objects # gui_cgf.converted ensures test software can run correctly if not gui_cfg.converted: gui_cfg.convert_icon_dict() grid.addWidget(self.table) def set_tbl_fonts_and_colors(self): """ Contains all the function calls required to setup the color and text formatting for the table. :return: """ # Set the header background colors self.table.horizontalHeaderItem(0).setBackground( gui_cfg.COLORS["TBL_HDR_DEFAULT_BG"]) self.table.horizontalHeaderItem(1).setBackground( gui_cfg.COLORS["TBL_HDR_DEFAULT_BG"]) self.table.horizontalHeaderItem(2).setBackground( gui_cfg.COLORS["TBL_HDR_DEFAULT_BG"]) self.table.horizontalHeaderItem(3).setBackground( gui_cfg.COLORS["TBL_HDR_DEFAULT_BG"]) self.table.horizontalHeaderItem(4).setBackground( gui_cfg.COLORS["TBL_HDR_DEFAULT_BG"]) # Setup the text and background colors for active and inactive rows (selections) table_palette = QtGui.QPalette(self.table.palette()) table_palette.setBrush( QtGui.QPalette.Active, QtGui.QPalette.HighlightedText, QtGui.QBrush(gui_cfg.COLORS["ROW_ACTV_TXT"]), ) table_palette.setBrush( QtGui.QPalette.Inactive, QtGui.QPalette.HighlightedText, QtGui.QBrush(gui_cfg.COLORS["ROW_INACTV_TXT"]), ) table_palette.setBrush( QtGui.QPalette.Active, QtGui.QPalette.Highlight, QtGui.QBrush(gui_cfg.COLORS["ROW_ACTV_BG"]), ) table_palette.setBrush( QtGui.QPalette.Inactive, QtGui.QPalette.Highlight, QtGui.QBrush(gui_cfg.COLORS["ROW_INACTV_BG"]), ) self.table.setPalette(table_palette) def dragEnterEvent(self, event): """ Override the dragEnterEvent method from PyQt :param event: :return: """ event.accept() if event.mimeData().hasUrls else event.ignore() def dragMoveEvent(self, event): """ Override the dragMoveEvent method from PyQt :param event: :return: """ if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() else: event.ignore() def dropEvent(self, event): """ Override the dropEvent method :param event: :return: """ if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() for url in event.mimeData().urls(): path = url.toLocalFile() if self.file_dropped == "": self.file_dropped = path else: self.clear_table() fileB = path if not utilities.file_writable(self.file_dropped): QMessageBox.about( self, "Warning ", os.path.basename(self.file_dropped) + " is read only") if not utilities.file_writable(fileB): QMessageBox.about( self, "Warning ", os.path.basename(fileB) + " is read only") # I use a local instance of fileIO to generate changesets, since # main_table can't access the main window instance of fileIO self.file_io = file_io.FileIO() result = self.file_io.diff_files(self.file_dropped, fileB) if result == pymerge_enums.RESULT.GOOD: result = result = self.file_io.get_change_sets( self.file_io.changes_a, self.file_io.changes_b) # I point the local changeSets to a local changeSet # but I must point the local changeSets back at the mainWindow changeSets # so I user could open different file via file open after they do a click and drag change_set_rereference_a = self.change_set_a change_set_rereference_b = self.change_set_b self.change_set_a = self.file_io.changes_a self.change_set_b = self.file_io.changes_b self.load_table_contents(self.file_dropped, fileB) self.change_set_a = change_set_rereference_a self.change_set_b = change_set_rereference_b self.file_dropped = "" else: event.ignore() @pyqtSlot() def goto_next_diff(self): """ Scrolls the table window to the next difference incrementally (starts at the first diff) :return: No return value """ if len(self.diff_indices) == 0: return if self.curr_diff_idx == len(self.diff_indices) - 1: self.curr_diff_idx = 0 self.jump_to_line(self.diff_indices[self.curr_diff_idx]) else: self.curr_diff_idx += 1 self.jump_to_line(self.diff_indices[self.curr_diff_idx]) self.select_block() return @pyqtSlot() def goto_prev_diff(self): """ Scrolls the table window to the previous difference incrementally :return: No return value """ if len(self.diff_indices) == 0: return if self.curr_diff_idx == 0 or self.curr_diff_idx == -1: self.curr_diff_idx = len(self.diff_indices) - 1 self.jump_to_line(self.diff_indices[self.curr_diff_idx]) else: self.curr_diff_idx -= 1 self.jump_to_line(self.diff_indices[self.curr_diff_idx]) self.select_block() self.table.repaint() return @pyqtSlot() def undo_last_change(self): """ undoes last change or group of changes :return: No return value """ undo_stack_size = 0 for n in self.block_undo_size: undo_stack_size += n difference = self.undo_ctrlr.undo_buf_size - undo_stack_size for i in range(difference): self.block_undo_size.append(1) if len(self.block_undo_size) != 0: n = self.block_undo_size.pop() self.block_redo_size.append(n) for i in range(n): self.undo_ctrlr.undo() self.undo_ctrlr.undo_buf_size -= 1 else: self.undo_ctrlr.undo() if self.undo_ctrlr.undo_buf_size != 0: self.undo_ctrlr.undo_buf_size -= 1 for row in self.rows: row.set_row_state() @pyqtSlot() def redo_last_undo(self): """ redo last undo performed :return: No return value """ if len(self.block_redo_size) != 0: n = self.block_redo_size.pop() self.block_undo_size.append(n) for i in range(n): self.undo_ctrlr.redo() else: self.undo_ctrlr.redo() for row in self.rows: row.set_row_state() @pyqtSlot() def merge_left(self): """ merge the whole left selection into the right """ self.table.clearSelection() for n in range(self.selected_block[0], self.selected_block[1]): self.rows[n].merge_left() self.block_undo_size.append(self.selected_block[1] - self.selected_block[0]) undo_stack_size = 0 for n in self.block_undo_size: undo_stack_size += n difference = undo_stack_size - self.undo_ctrlr.undo_buf_size difference = abs(difference) for i in range(difference): self.block_undo_size.insert(len(self.block_undo_size) - 1, 1) return @pyqtSlot() def merge_right(self): """ merge the whole right selection into the left """ self.table.clearSelection() for n in range(self.selected_block[0], self.selected_block[1]): self.rows[n].merge_right() self.block_undo_size.append(self.selected_block[1] - self.selected_block[0]) undo_stack_size = 0 for n in self.block_undo_size: undo_stack_size += n difference = undo_stack_size - self.undo_ctrlr.undo_buf_size difference = abs(difference) for i in range(difference): self.block_undo_size.insert(len(self.block_undo_size) - 1, 1) return def jump_to_line(self, line_num, col=0): self.table.clearSelection() self.table.scrollToItem(self.table.item(line_num - 1, col), QtWidgets.QAbstractItemView.PositionAtTop) def add_line( self, right_text: str, left_text: str, line_num: int or str, change_flags, left_line_num=0, right_line_num=0, ): """ Add a row into the table using the right and left text provided as parameters. :param right_line_num: :param left_line_num: :param right_text: Right text to display :param left_text: Left text to display :param line_num: Line number to display. This isn't exactly where it's inserted, just a display value :param change_flags: :return: No return value """ self.table.insertRow(line_num) self.table.setRowHeight(line_num, 28) self.table.setItem(line_num, 0, QTableWidgetItem(str(line_num + 1))) self.table.setItem(line_num, 1, QTableWidgetItem(str(right_text))) self.table.setItem(line_num, 2, QTableWidgetItem("")) self.table.setItem(line_num, 3, QTableWidgetItem("")) self.table.setItem(line_num, 4, QTableWidgetItem(str(left_text))) self.table.item(line_num, 0).setTextAlignment(Qt.AlignCenter) self.table.item(line_num, 2).setTextAlignment(Qt.AlignCenter) self.table.item(line_num, 3).setTextAlignment(Qt.AlignCenter) self.table.item(line_num, 0).setBackground( gui_cfg.COLORS["TBL_LINE_COL_DEFAULT_BG"]) self.table.item(line_num, 2).setBackground( gui_cfg.COLORS["TBL_LINE_COL_DEFAULT_BG"]) self.table.item(line_num, 3).setBackground( gui_cfg.COLORS["TBL_LINE_COL_DEFAULT_BG"]) self.table.item(line_num, 4).setBackground( gui_cfg.COLORS["TBL_LINE_COL_DEFAULT_BG"]) row_instance = table_row.Row(line_num, self.table, right_text, left_text, line_num, change_flags) row_instance.actual_indices[0] = left_line_num row_instance.actual_indices[1] = right_line_num self.rows.append(row_instance) def get_lines_from_tbl(self) -> list: """ Gets the data from the table and returns it as a 2D list. :return: list containing right and left hand file data """ outp_left: list = [] outp_right: list = [] for row in self.rows: if row.row_deleted[1]: outp_left.append(None) else: outp_left.append(row.left_text) if row.row_deleted[0]: outp_right.append(None) else: outp_right.append(row.right_text) return [outp_left, outp_right] def clear_table(self) -> bool: for row in self.rows: try: self.table.removeRow(row.row_num) except (IndexError, Exception): print("Error: could not clear main table.") return False self.rows.clear() self.table.setRowCount(0) del self.change_set_a.change_list[:] del self.change_set_b.change_list[:] self.block_undo_size.clear() self.block_redo_size.clear() self.curr_diff_idx = -1 self.selected_block[0] = 0 self.selected_block[1] = 0 self.diff_indices.clear() self.diff_index_block_end.clear() return True def load_table_contents(self, file1=0, file2=0): if file1 == 0 or file2 == 0: return """ Load the contents of two data structures containing the lines to be displayed, into the tables :param left_lines: left hand data to show :param right_lines: right hand data to show :return: No return value """ self.table.horizontalHeaderItem(1).setText(os.path.abspath(file1)) self.table.horizontalHeaderItem(4).setText(os.path.abspath(file2)) self.left_file = file1 self.right_file = file2 # Get the change information for each line. Skip the last line, as that is the # match token that has been appened on by diff_set in order to capture entire file # contents. for n in range(len(self.change_set_a.change_list) - 1): data_a = [""] data_b = [""] change_type_a = [pymerge_enums.CHANGEDENUM.SAME] change_type_b = [pymerge_enums.CHANGEDENUM.SAME] self.change_set_a.get_change(n, change_type_a, data_a) self.change_set_b.get_change(n, change_type_b, data_b) self.add_line(data_a[0], data_b[0], n, [change_type_a[0], change_type_b[0]]) # generate list of diff lines, to enable prev/next diff jump buttons n = 0 while n < len(self.change_set_a.change_list) - 1: data_a = [""] change_type_a = [pymerge_enums.CHANGEDENUM.SAME] self.change_set_a.get_change(n, change_type_a, data_a) if change_type_a[0] != pymerge_enums.CHANGEDENUM.SAME: self.diff_indices.append(n) while change_type_a[0] != pymerge_enums.CHANGEDENUM.SAME: n += 1 self.change_set_a.get_change(n, change_type_a, data_a) self.diff_index_block_end.append(n) n += 1 @pyqtSlot() def write_merged_files(self): merged_file_contents = self.get_lines_from_tbl() merge_writer = merge_finalizer.MergeFinalizer(self.left_file, self.right_file, "file_backup") if not merge_writer.set_equal(merged_file_contents[0][:-1], merged_file_contents[1][:-1]): print( "Warning: Files are not identicle, merge all lines before saving in order for files to be 100% syncronized." ) merge_writer.finalize_merge(merged_file_contents[0][:-1], merged_file_contents[1][:-1]) def load_test_files(self, file1: str, file2: str): """ Load two arbitrary files as as test :param file1: right hand file to load :param file2: left hand file to load :return: No return value """ self.table.horizontalHeaderItem(1).setText(os.path.abspath(file1)) self.table.horizontalHeaderItem(4).setText(os.path.abspath(file2)) with open(file1, "r") as file: file1_contents = file.read().splitlines() with open(file2, "r") as file: file2_contents = file.read().splitlines() self.load_table_contents(file1_contents, file2_contents) self.jump_to_line(77) def select_block(self, n=-1): if n != -1: j = 0 for i in self.diff_indices: if n >= i: self.curr_diff_idx = j j += 1 self.table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) self.table.clearSelection() self.selected_block[0] = self.diff_indices[self.curr_diff_idx] self.selected_block[1] = self.diff_index_block_end[self.curr_diff_idx] for n in range(self.diff_indices[self.curr_diff_idx], self.diff_index_block_end[self.curr_diff_idx]): self.table.selectRow(n) self.table.setSelectionMode( QtWidgets.QAbstractItemView.SingleSelection) @pyqtSlot() def cellClickedEvent(self): if self.rows[self.table.currentRow( )].change_state_flags[0] == pymerge_enums.CHANGEDENUM.SAME: self.table.clearSelection() else: self.select_block(self.table.currentRow())
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupTrayicon() self.setupVariables() self.setupUi() self.setupConnections() self.show() def setupVariables(self): settings = QSettings() self.workEndTime = QTime( int(settings.value(workHoursKey, 0)), int(settings.value(workMinutesKey, 25)), int(settings.value(workSecondsKey, 0)), ) self.restEndTime = QTime( int(settings.value(restHoursKey, 0)), int(settings.value(restMinutesKey, 5)), int(settings.value(restSecondsKey, 0)), ) self.timeFormat = "hh:mm:ss" self.time = QTime(0, 0, 0, 0) self.workTime = QTime(0, 0, 0, 0) self.restTime = QTime(0, 0, 0, 0) self.totalTime = QTime(0, 0, 0, 0) self.currentMode = Mode.work self.maxRepetitions = -1 self.currentRepetitions = 0 def setupConnections(self): """ Create button connections """ self.startButton.clicked.connect(self.startTimer) self.startButton.clicked.connect( lambda: self.startButton.setDisabled(True)) self.startButton.clicked.connect( lambda: self.pauseButton.setDisabled(False)) self.startButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.pauseButton.clicked.connect(self.pauseTimer) self.pauseButton.clicked.connect( lambda: self.startButton.setDisabled(False)) self.pauseButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.pauseButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.pauseButton.clicked.connect( lambda: self.startButton.setText("continue")) self.resetButton.clicked.connect(self.resetTimer) self.resetButton.clicked.connect( lambda: self.startButton.setDisabled(False)) self.resetButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.resetButton.clicked.connect( lambda: self.resetButton.setDisabled(True)) self.resetButton.clicked.connect( lambda: self.startButton.setText("start")) self.acceptTaskButton.pressed.connect(self.insertTask) self.deleteTaskButton.pressed.connect(self.deleteTask) """ Create spinbox connections """ self.workHoursSpinBox.valueChanged.connect(self.updateWorkEndTime) self.workMinutesSpinBox.valueChanged.connect(self.updateWorkEndTime) self.workSecondsSpinBox.valueChanged.connect(self.updateWorkEndTime) self.restHoursSpinBox.valueChanged.connect(self.updateRestEndTime) self.restMinutesSpinBox.valueChanged.connect(self.updateRestEndTime) self.restSecondsSpinBox.valueChanged.connect(self.updateRestEndTime) self.repetitionsSpinBox.valueChanged.connect(self.updateMaxRepetitions) """ Create combobox connections """ self.modeComboBox.currentTextChanged.connect(self.updateCurrentMode) """ Create tablewidget connections """ self.tasksTableWidget.cellDoubleClicked.connect( self.markTaskAsFinished) def setupUi(self): self.size_policy = sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) """ Create tabwidget """ self.tabWidget = QTabWidget() """ Create tab widgets """ timerWidget = self.setupTimerTab() tasksWidget = self.setupTasksTab() statisticsWidget = self.setupStatisticsTab() """ add tab widgets to tabwidget""" self.timerTab = self.tabWidget.addTab(timerWidget, makeIcon("timer"), "Timer") self.tasksTab = self.tabWidget.addTab(tasksWidget, makeIcon("tasks"), "Tasks") self.statisticsTab = self.tabWidget.addTab(statisticsWidget, makeIcon("statistics"), "Statistics") """ Set mainwindows central widget """ self.setCentralWidget(self.tabWidget) def setupTimerTab(self): settings = QSettings() self.timerContainer = QWidget(self) self.timerContainerLayout = QVBoxLayout(self.timerContainer) self.timerContainer.setLayout(self.timerContainerLayout) """ Create work groupbox""" self.workGroupBox = QGroupBox("Work") self.workGroupBoxLayout = QHBoxLayout(self.workGroupBox) self.workGroupBox.setLayout(self.workGroupBoxLayout) self.workHoursSpinBox = QSpinBox( minimum=0, maximum=24, value=int(settings.value(workHoursKey, 0)), suffix="h", sizePolicy=self.size_policy, ) self.workMinutesSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(workMinutesKey, 25)), suffix="m", sizePolicy=self.size_policy, ) self.workSecondsSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(workSecondsKey, 0)), suffix="s", sizePolicy=self.size_policy, ) """ Create rest groupbox""" self.restGroupBox = QGroupBox("Rest") self.restGroupBoxLayout = QHBoxLayout(self.restGroupBox) self.restGroupBox.setLayout(self.restGroupBoxLayout) self.restHoursSpinBox = QSpinBox( minimum=0, maximum=24, value=int(settings.value(restHoursKey, 0)), suffix="h", sizePolicy=self.size_policy, ) self.restMinutesSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(restMinutesKey, 5)), suffix="m", sizePolicy=self.size_policy, ) self.restSecondsSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(restSecondsKey, 0)), suffix="s", sizePolicy=self.size_policy, ) self.restGroupBoxLayout.addWidget(self.restHoursSpinBox) self.restGroupBoxLayout.addWidget(self.restMinutesSpinBox) self.restGroupBoxLayout.addWidget(self.restSecondsSpinBox) """ Create other groupbox""" self.otherGroupBox = QGroupBox("Other") self.otherGroupBoxLayout = QHBoxLayout(self.otherGroupBox) self.otherGroupBox.setLayout(self.otherGroupBoxLayout) self.repetitionsLabel = QLabel("Repetitions") self.repetitionsSpinBox = QSpinBox( minimum=0, maximum=10000, value=0, sizePolicy=self.size_policy, specialValueText="∞", ) self.modeLabel = QLabel("Mode") self.modeComboBox = QComboBox(sizePolicy=self.size_policy) self.modeComboBox.addItems(["work", "rest"]) self.otherGroupBoxLayout.addWidget(self.repetitionsLabel) self.otherGroupBoxLayout.addWidget(self.repetitionsSpinBox) self.otherGroupBoxLayout.addWidget(self.modeLabel) self.otherGroupBoxLayout.addWidget(self.modeComboBox) """ Create timer groupbox""" self.lcdDisplayGroupBox = QGroupBox("Time") self.lcdDisplayGroupBoxLayout = QHBoxLayout(self.lcdDisplayGroupBox) self.lcdDisplayGroupBox.setLayout(self.lcdDisplayGroupBoxLayout) self.timeDisplay = QLCDNumber(8, sizePolicy=self.size_policy) self.timeDisplay.setFixedHeight(100) self.timeDisplay.display("00:00:00") self.lcdDisplayGroupBoxLayout.addWidget(self.timeDisplay) """ Create pause, start and reset buttons""" self.buttonContainer = QWidget() self.buttonContainerLayout = QHBoxLayout(self.buttonContainer) self.buttonContainer.setLayout(self.buttonContainerLayout) self.startButton = self.makeButton("start", disabled=False) self.resetButton = self.makeButton("reset") self.pauseButton = self.makeButton("pause") """ Add widgets to container """ self.workGroupBoxLayout.addWidget(self.workHoursSpinBox) self.workGroupBoxLayout.addWidget(self.workMinutesSpinBox) self.workGroupBoxLayout.addWidget(self.workSecondsSpinBox) self.timerContainerLayout.addWidget(self.workGroupBox) self.timerContainerLayout.addWidget(self.restGroupBox) self.timerContainerLayout.addWidget(self.otherGroupBox) self.timerContainerLayout.addWidget(self.lcdDisplayGroupBox) self.buttonContainerLayout.addWidget(self.pauseButton) self.buttonContainerLayout.addWidget(self.startButton) self.buttonContainerLayout.addWidget(self.resetButton) self.timerContainerLayout.addWidget(self.buttonContainer) return self.timerContainer def setupTasksTab(self): settings = QSettings() """ Create vertical tasks container """ self.tasksWidget = QWidget(self.tabWidget) self.tasksWidgetLayout = QVBoxLayout(self.tasksWidget) self.tasksWidget.setLayout(self.tasksWidgetLayout) """ Create horizontal input container """ self.inputContainer = QWidget() self.inputContainer.setFixedHeight(50) self.inputContainerLayout = QHBoxLayout(self.inputContainer) self.inputContainerLayout.setContentsMargins(0, 0, 0, 0) self.inputContainer.setLayout(self.inputContainerLayout) """ Create text edit """ self.taskTextEdit = QTextEdit( placeholderText="Describe your task briefly.", undoRedoEnabled=True) """ Create vertical buttons container """ self.inputButtonContainer = QWidget() self.inputButtonContainerLayout = QVBoxLayout( self.inputButtonContainer) self.inputButtonContainerLayout.setContentsMargins(0, 0, 0, 0) self.inputButtonContainer.setLayout(self.inputButtonContainerLayout) """ Create buttons """ self.acceptTaskButton = QToolButton(icon=makeIcon("check")) self.deleteTaskButton = QToolButton(icon=makeIcon("trash")) """ Create tasks tablewidget """ self.tasksTableWidget = QTableWidget(0, 1) self.tasksTableWidget.setHorizontalHeaderLabels(["Tasks"]) self.tasksTableWidget.horizontalHeader().setStretchLastSection(True) self.tasksTableWidget.verticalHeader().setVisible(False) self.tasksTableWidget.setWordWrap(True) self.tasksTableWidget.setTextElideMode(Qt.ElideNone) self.tasksTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tasksTableWidget.setSelectionMode( QAbstractItemView.SingleSelection) self.insertTasks(*settings.value(tasksKey, [])) """ Add widgets to container widgets """ self.inputButtonContainerLayout.addWidget(self.acceptTaskButton) self.inputButtonContainerLayout.addWidget(self.deleteTaskButton) self.inputContainerLayout.addWidget(self.taskTextEdit) self.inputContainerLayout.addWidget(self.inputButtonContainer) self.tasksWidgetLayout.addWidget(self.inputContainer) self.tasksWidgetLayout.addWidget(self.tasksTableWidget) return self.tasksWidget def setupStatisticsTab(self): """ Create statistics container """ self.statisticsContainer = QWidget() self.statisticsContainerLayout = QVBoxLayout(self.statisticsContainer) self.statisticsContainer.setLayout(self.statisticsContainerLayout) """ Create work time groupbox """ self.statisticsWorkTimeGroupBox = QGroupBox("Work Time") self.statisticsWorkTimeGroupBoxLayout = QHBoxLayout() self.statisticsWorkTimeGroupBox.setLayout( self.statisticsWorkTimeGroupBoxLayout) self.statisticsWorkTimeDisplay = QLCDNumber(8) self.statisticsWorkTimeDisplay.display("00:00:00") self.statisticsWorkTimeGroupBoxLayout.addWidget( self.statisticsWorkTimeDisplay) """ Create rest time groupbox """ self.statisticsRestTimeGroupBox = QGroupBox("Rest Time") self.statisticsRestTimeGroupBoxLayout = QHBoxLayout() self.statisticsRestTimeGroupBox.setLayout( self.statisticsRestTimeGroupBoxLayout) self.statisticsRestTimeDisplay = QLCDNumber(8) self.statisticsRestTimeDisplay.display("00:00:00") self.statisticsRestTimeGroupBoxLayout.addWidget( self.statisticsRestTimeDisplay) """ Create total time groupbox """ self.statisticsTotalTimeGroupBox = QGroupBox("Total Time") self.statisticsTotalTimeGroupBoxLayout = QHBoxLayout() self.statisticsTotalTimeGroupBox.setLayout( self.statisticsTotalTimeGroupBoxLayout) self.statisticsTotalTimeDisplay = QLCDNumber(8) self.statisticsTotalTimeDisplay.display("00:00:00") self.statisticsTotalTimeGroupBoxLayout.addWidget( self.statisticsTotalTimeDisplay) """ Add widgets to container """ self.statisticsContainerLayout.addWidget( self.statisticsTotalTimeGroupBox) self.statisticsContainerLayout.addWidget( self.statisticsWorkTimeGroupBox) self.statisticsContainerLayout.addWidget( self.statisticsRestTimeGroupBox) return self.statisticsContainer def setupTrayicon(self): self.trayIcon = QSystemTrayIcon(makeIcon("tomato")) self.trayIcon.setContextMenu(QMenu()) self.quitAction = self.trayIcon.contextMenu().addAction( makeIcon("exit"), "Quit", self.exit) self.quitAction.triggered.connect(self.exit) self.trayIcon.activated.connect(self.onActivate) self.trayIcon.show() def leaveEvent(self, event): super(MainWindow, self).leaveEvent(event) self.tasksTableWidget.clearSelection() def closeEvent(self, event): super(MainWindow, self).closeEvent(event) settings = QSettings() settings.setValue(workHoursKey, self.workHoursSpinBox.value()) settings.setValue( workMinutesKey, self.workMinutesSpinBox.value(), ) settings.setValue( workSecondsKey, self.workSecondsSpinBox.value(), ) settings.setValue(restHoursKey, self.restHoursSpinBox.value()) settings.setValue( restMinutesKey, self.restMinutesSpinBox.value(), ) settings.setValue( restSecondsKey, self.restSecondsSpinBox.value(), ) tasks = [] for i in range(self.tasksTableWidget.rowCount()): item = self.tasksTableWidget.item(i, 0) if not item.font().strikeOut(): tasks.append(item.text()) settings.setValue(tasksKey, tasks) def startTimer(self): try: if not self.timer.isActive(): self.createTimer() except: self.createTimer() def createTimer(self): self.timer = QTimer() self.timer.timeout.connect(self.updateTime) self.timer.timeout.connect(self.maybeChangeMode) self.timer.setInterval(1000) self.timer.setSingleShot(False) self.timer.start() def pauseTimer(self): try: self.timer.stop() self.timer.disconnect() except: pass def resetTimer(self): try: self.pauseTimer() self.time = QTime(0, 0, 0, 0) self.displayTime() except: pass def maybeStartTimer(self): if self.currentRepetitions != self.maxRepetitions: self.startTimer() started = True else: self.currentRepetitions = 0 started = False return started def updateWorkEndTime(self): self.workEndTime = QTime( self.workHoursSpinBox.value(), self.workMinutesSpinBox.value(), self.workSecondsSpinBox.value(), ) def updateRestEndTime(self): self.restEndTime = QTime( self.restHoursSpinBox.value(), self.restMinutesSpinBox.value(), self.restSecondsSpinBox.value(), ) def updateCurrentMode(self, mode: str): self.currentMode = Mode.work if mode == "work" else Mode.rest def updateTime(self): self.time = self.time.addSecs(1) self.totalTime = self.totalTime.addSecs(1) if self.modeComboBox.currentText() == "work": self.workTime = self.workTime.addSecs(1) else: self.restTime = self.restTime.addSecs(1) self.displayTime() def updateMaxRepetitions(self, value): if value == 0: self.currentRepetitions = 0 self.maxRepetitions = -1 else: self.maxRepetitions = 2 * value def maybeChangeMode(self): if self.currentMode is Mode.work and self.time >= self.workEndTime: self.resetTimer() self.modeComboBox.setCurrentIndex(1) self.incrementCurrentRepetitions() started = self.maybeStartTimer() self.showWindowMessage( Status.workFinished if started else Status.repetitionsReached) elif self.currentMode is Mode.rest and self.time >= self.restEndTime: self.resetTimer() self.modeComboBox.setCurrentIndex(0) self.incrementCurrentRepetitions() started = self.maybeStartTimer() self.showWindowMessage( Status.restFinished if started else Status.repetitionsReached) def incrementCurrentRepetitions(self): if self.maxRepetitions > 0: self.currentRepetitions += 1 def insertTask(self): task = self.taskTextEdit.toPlainText() self.insertTasks(task) def insertTasks(self, *tasks): for task in tasks: if task: rowCount = self.tasksTableWidget.rowCount() self.tasksTableWidget.setRowCount(rowCount + 1) self.tasksTableWidget.setItem(rowCount, 0, QTableWidgetItem(task)) self.tasksTableWidget.resizeRowsToContents() self.taskTextEdit.clear() def deleteTask(self): selectedIndexes = self.tasksTableWidget.selectedIndexes() if selectedIndexes: self.tasksTableWidget.removeRow(selectedIndexes[0].row()) def markTaskAsFinished(self, row, col): item = self.tasksTableWidget.item(row, col) font = self.tasksTableWidget.item(row, col).font() font.setStrikeOut(False if item.font().strikeOut() else True) item.setFont(font) def displayTime(self): self.timeDisplay.display(self.time.toString(self.timeFormat)) self.statisticsRestTimeDisplay.display( self.restTime.toString(self.timeFormat)) self.statisticsWorkTimeDisplay.display( self.workTime.toString(self.timeFormat)) self.statisticsTotalTimeDisplay.display( self.totalTime.toString(self.timeFormat)) def showWindowMessage(self, status): if status is Status.workFinished: self.trayIcon.showMessage("Break", choice(work_finished_phrases), makeIcon("tomato")) elif status is Status.restFinished: self.trayIcon.showMessage("Work", choice(rest_finished_phrases), makeIcon("tomato")) else: self.trayIcon.showMessage("Finished", choice(pomodoro_finished_phrases), makeIcon("tomato")) self.resetButton.click() def makeButton(self, text, iconName=None, disabled=True): button = QPushButton(text, sizePolicy=self.size_policy) if iconName: button.setIcon(makeIcon(iconName)) button.setDisabled(disabled) return button def exit(self): self.close() app = QApplication.instance() if app: app.quit() def onActivate(self, reason): if reason == QSystemTrayIcon.Trigger: self.show()
class ExTags(QDialog): """A dialog that shows you the tags in a file In addition, any attached cover art is shown.""" rowChanged = pyqtSignal(object, name='rowChanged') extendedtags = pyqtSignal(dict, name='extendedtags') def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value')]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown(True) header.setStretchLastSection(True) header.setSortIndicator(0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons=True) self.piclabel.imageChanged.connect( self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.piclabel.removepic.clicked.connect( self.removePic) else: self.piclabel.removepic.triggered.connect(self.removePic) if row and row >= 0 and files: buttons = MoveButtons(files, row) buttons.indexChanged.connect(self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip(translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self._reset.clicked.connect(self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveupButton.hide() self.listbuttons.movedownButton.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.okcancel.cancel.connect(self.closeMe) self.table.itemDoubleClicked.connect(self.editField) self.table.itemSelectionChanged.connect(self._checkListBox) self.okcancel.ok.connect(self.okClicked) self.listbuttons.edit.connect(self.editField) self.listbuttons.addButton.clicked.connect(self.addField) self.listbuttons.removeButton.clicked.connect(self.removeField) self.listbuttons.duplicate.connect(self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row and row >= 0 and files: self._prevnext(row) else: self.loadFiles(files) def addField(self): win = EditField(parent=self, field_list=self.get_fieldlist) win.setModal(True) win.show() win.donewithmyshit.connect(self.editFieldBuddy) def _checkListBox(self): if self.table.rowCount() <= 0: self.table.setEnabled(False) self.listbuttons.editButton.setEnabled(False) self.listbuttons.removeButton.setEnabled(False) self.listbuttons.duplicateButton.setEnabled(False) self._reset.setEnabled(False) else: self.table.setEnabled(True) self._reset.setEnabled(True) if len(self.table.selectedIndexes()) / 2 > 1: self.listbuttons.editButton.setEnabled(False) self.listbuttons.duplicateButton.setEnabled(False) else: self.listbuttons.editButton.setEnabled(True) self.listbuttons.removeButton.setEnabled(True) self.listbuttons.duplicateButton.setEnabled(True) self.table.resizeColumnToContents(0) def closeEvent(self, event): self.piclabel.close() QDialog.closeEvent(self, event) def closeMe(self): self.canceled = True self.close() def _deletePressed(self, item): if self.table.deletePressed: self.table.deletePressed = False self.removeField() def duplicate(self): self.editField(True) def editField(self, duplicate=False): """Opens a dialog to edit the currently selected Field. If duplicate is True the Edit Field dialog will be populated with the currently selected field's values. The new field'll then be added to the field list.""" row = self.table.currentRow() if row != -1: prevtag = self.get_field(row) if duplicate is True: win = EditField(prevtag, self, self.get_fieldlist, edit=False) else: win = EditField(prevtag, self, self.get_fieldlist) win.setModal(True) win.show() # Have to check for truth, because this method is # called by the doubleclicked signal. if duplicate is True: buddy = partial(self.editFieldBuddy, duplicate=True) else: buddy = self.editFieldBuddy win.donewithmyshit.connect(buddy) def editFieldBuddy(self, tag, value, prevtag=None, duplicate=False): rowcount = self.table.rowCount() if prevtag is not None: if duplicate: row = rowcount self._settag(rowcount, tag, value, ADD, self.previewMode, True) else: if tag == prevtag[0]: row = self.table.currentRow() self._settag(row, tag, value, EDIT, self.previewMode, True) if row + 1 < rowcount: self.table.selectRow(row + 1) else: cur_item = self.table.item(self.table.currentRow(), 0) self.resetFields([cur_item]) self.table.setCurrentItem(cur_item, QItemSelectionModel.ClearAndSelect) self.table.selectRow(self.table.row(cur_item)) self.removeField() valitem = self._settag(rowcount, tag, value, ADD, self.previewMode, True) cur_item.linked = [valitem] else: self._settag(rowcount, tag, value, ADD, self.previewMode, True) self._checkListBox() self.filechanged = True self.table.clearSelection() def get_field(self, row, status=None): getitem = self.table.item item = getitem(row, 0) tag = str(item.text()) try: value = str(getitem(row, 1).text()) except AttributeError: value = str(self.table.cellWidget(row, 1).currentText()) if status: return (tag, value, item.status) else: return (tag, value) def _imageChanged(self): self.filechanged = True def loadSettings(self): cparser = PuddleConfig() self.get_fieldlist = gettaglist() get = lambda k, v: cparser.get('extendedtags', k, v, True) add = QColor.fromRgb(*get('add', [255, 255, 0])) edit = QColor.fromRgb(*get('edit', [0, 255, 0])) remove = QColor.fromRgb(*get('remove', [255, 0, 0])) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} item = self.table.item for row in range(self.table.rowCount()): field_item = self.get_item(row, 0) field_item.statusColors = self._colors field_item.status = field_item.status val_item = self.get_item(row, 1) val_item.statusColors = self._colors val_item.status = val_item.status def listtotag(self): get_field = self.get_field tags = {} lowered = {} listitems = [get_field(row, True) for row in range(self.table.rowCount())] for field, val, status in listitems: if status != REMOVE: if val == KEEP: continue l_field = field.lower() if l_field in lowered: tags[lowered[l_field]].append(val) else: lowered[l_field] = field tags[field] = [z.strip() for z in val.split('\\') if z.strip()] else: if field.lower() not in lowered: tags[field] = [] lowered[field.lower()] = field return tags def loadFiles(self, audios): if self.filechanged: self.save() self.filechanged = False self.table.clearContents() self.table.setRowCount(0) self.piclabel.lastfilename = audios[0].filepath self.piclabel.setEnabled(False) self.piclabel.setImages(None) if len(audios) == 1: audio = audios[0] self.setWindowTitle(audios[0].filepath) self._loadsingle(audio) else: self.setWindowTitle( translate('Extended Tags', 'Different files.')) from .tagmodel import status k = status['table'].model().taginfo[0] common, numvalues, imagetags = commontags(audios) images = common['__image'] del (common['__image']) previews = set(audios[0].preview) italics = set(audios[0].equal_fields()) self.piclabel.currentFile = audios[0] self.piclabel.filePattern = self.status['cover_pattern'] for audio in audios[1:]: previews = previews.intersection(audio.preview) italics = italics.intersection(audio.equal_fields()) row = 0 for field, values in common.items(): if field in italics: preview = UNCHANGED # field in italics => field in previews. elif field in previews: preview = BOLD else: preview = UNCHANGED if numvalues[field] != len(audios): self._settag(row, field, values, multi=True) row += 1 else: if isinstance(values, str): self._settag(row, field, values, None, preview) row += 1 else: for v in values: self._settag(row, field, v, None, preview) row += 1 self.piclabel.setImageTags(imagetags) if images: self.piclabel.setEnabled(True) self.piclabel.setImages(images) else: self.piclabel.setImages(None) self.piclabel.setEnabled(True) if images == 0: self.piclabel.context = 'Cover Varies' self.piclabel.removepic.setEnabled(True) self._checkListBox() def _loadsingle(self, tags): items = [] d = tags.usertags.copy() italics = tags.equal_fields() self.piclabel.currentFile = tags self.piclabel.filePattern = self.status['cover_pattern'] for key, val in sorted(d.items()): if key in italics: preview = UNCHANGED elif key in tags.preview: preview = BOLD else: preview = UNCHANGED if isinstance(val, str): items.append([key, val, None, preview]) else: [items.append([key, z, None, preview]) for z in val] [self._settag(i, *item) for i, item in enumerate(items)] self.piclabel.lastfilename = tags.filepath if not tags.library: self.piclabel.setImageTags(tags.IMAGETAGS) if tags.IMAGETAGS: if '__image' in tags.preview: images = tags.preview['__image'] else: images = tags.images self.piclabel.setEnabled(True) if images: self.piclabel.setImages(deepcopy(images)) else: self.piclabel.setImages(None) self._checkListBox() self.setWindowTitle(tags[PATH]) def okClicked(self): self.save() self.close() def _prevnext(self, row): if self.filechanged: self.save() self.loadFiles([self._files[row]]) self.rowChanged.emit(row) def get_item(self, row, column=None): if column is None: # Assume QModelIndex passed column = row.column() row = row.row() item = self.table.item(row, column) if item is None: item = self.table.cellWidget(row, column) return item def removeField(self): tb = self.table tb.setSortingEnabled(False) to_remove = {} rows = [] for index in self.table.selectedIndexes(): row = index.row() item = self.get_item(index) if item.status == ADD: to_remove[row] = item rows.append(row) item.status = REMOVE item.status = REMOVE [tb.removeRow(tb.row(z)) for z in to_remove.values()] tb.setSortingEnabled(True) self.filechanged = True self._checkListBox() if rows: row = max(rows) self.table.clearSelection() if row + 1 < self.table.rowCount(): self.table.selectRow(row + 1) def resetFields(self, items=None): box = self.table to_remove = {} # Stores row: item values so that only one item # gets removed per row. max_row = -1 for index in box.selectedIndexes(): row = index.row() item = self.table.item(row, index.column()) if item is None: item = self.table.cellWidget(row, index.column()) for i in item.linked: try: to_remove[box.row(i)] = i except RuntimeError: pass item.reset() if row > max_row: max_row = row if item.status == REMOVE: to_remove[row] = item self.table.clearSelection() if max_row != -1 and max_row + 1 < self.table.rowCount(): self.table.selectRow(max_row + 1) for item in to_remove.values(): self.table.removeRow(self.table.row(item)) self._checkListBox() def removePic(self): if self.piclabel.context == 'Cover Varies': self.piclabel.context = 'No Images' self.piclabel.removepic.setEnabled(False) if not isinstance(self.piclabel.removepic, QAction): self.piclabel.removepic.clicked.disconnect( self.removePic) else: self.piclabel.removepic.triggered.disconnect(self.removePic) self.piclabel.setImages(None) def save(self): if not self.filechanged: table = self.table for row in range(table.rowCount()): combo = table.cellWidget(row, 1) if combo is not None and combo.currentIndex() != 0: self.filechanged = True break if not self.filechanged: return tags = self.listtotag() if self.piclabel.context != 'Cover Varies': if not self.piclabel.images: tags['__image'] = [] else: tags["__image"] = self.piclabel.images newtags = [z for z in tags if z not in self.get_fieldlist] if newtags and newtags != ['__image']: settaglist(newtags + self.get_fieldlist) self.extendedtags.emit(tags) def _settag(self, row, field, value, status=None, preview=False, check=False, multi=False): tb = self.table tb.setSortingEnabled(False) if row >= tb.rowCount(): tb.insertRow(row) field_item = StatusWidgetItem(field, status, self._colors, preview) tb.setItem(row, 0, field_item) if not multi and (len(value) == 1 or isinstance(value, str)): valitem = StatusWidgetItem(to_string(value), status, self._colors, preview) tb.setItem(row, 1, valitem) else: valitem = StatusWidgetCombo(value, status, self._colors, preview) tb.setCellWidget(row, 1, valitem) else: field_item = tb.item(row, 0) field_item.setText(field) field_item.status = status val_item = self.get_item(row, 1) val_item.setText(value) val_item.status = status if check: lowered_tag = field.lower() for row in range(tb.rowCount()): item = tb.item(row, 0) text = str(item.text()) if text != field and text.lower() == lowered_tag: item.setText(field) if item.status not in [ADD, REMOVE]: item.status = EDIT try: tb.item(row, 1).status = EDIT except AttributeError: tb.cellWidget(row, 1).status = EDIT tb.setSortingEnabled(True) return field_item
class tableWidget(QDialog): def __init__(self, parent=None): super(tableWidget, self).__init__(parent) self.setWindowTitle("QTableWidget en PyQt5 por: ANDRES NIÑO") self.setWindowIcon(QIcon("Qt.png")) self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.MSWindowsFixedSizeDialogHint) self.setFixedSize(740, 348) self.initUI() def initUI(self): # ================== WIDGET QTableWidget ================== self.tabla = QTableWidget(self) # Deshabilitar edición self.tabla.setEditTriggers(QAbstractItemView.NoEditTriggers) # Deshabilitar el comportamiento de arrastrar y soltar self.tabla.setDragDropOverwriteMode(False) # Seleccionar toda la fila self.tabla.setSelectionBehavior(QAbstractItemView.SelectRows) # Seleccionar una fila a la vez self.tabla.setSelectionMode(QAbstractItemView.SingleSelection) # Especifica dónde deben aparecer los puntos suspensivos "..." cuando se muestran # textos que no encajan self.tabla.setTextElideMode(Qt.ElideRight)# Qt.ElideNone # Establecer el ajuste de palabras del texto self.tabla.setWordWrap(False) # Deshabilitar clasificación self.tabla.setSortingEnabled(False) # Establecer el número de columnas self.tabla.setColumnCount(6) # Establecer el número de filas self.tabla.setRowCount(0) # Alineación del texto del encabezado self.tabla.horizontalHeader().setDefaultAlignment(Qt.AlignHCenter|Qt.AlignVCenter| Qt.AlignCenter) # Deshabilitar resaltado del texto del encabezado al seleccionar una fila self.tabla.horizontalHeader().setHighlightSections(False) # Hacer que la última sección visible del encabezado ocupa todo el espacio disponible self.tabla.horizontalHeader().setStretchLastSection(True) # Ocultar encabezado vertical self.tabla.verticalHeader().setVisible(False) # Dibujar el fondo usando colores alternados self.tabla.setAlternatingRowColors(True) # Establecer altura de las filas self.tabla.verticalHeader().setDefaultSectionSize(20) # self.tabla.verticalHeader().setHighlightSections(True) nombreColumnas = ("Id","Nombre", "Apellido", "Sexo", "Fecha de nacimiento", "País") # Establecer las etiquetas de encabezado horizontal usando etiquetas self.tabla.setHorizontalHeaderLabels(nombreColumnas) # Menú contextual self.tabla.setContextMenuPolicy(Qt.CustomContextMenu) self.tabla.customContextMenuRequested.connect(self.menuContextual) # Establecer ancho de las columnas for indice, ancho in enumerate((80, 120, 120, 110, 150), start=0): self.tabla.setColumnWidth(indice, ancho) self.tabla.resize(700, 240) self.tabla.move(20, 56) # =================== WIDGETS QPUSHBUTTON ================== botonMostrarDatos = QPushButton("Mostrar datos", self) botonMostrarDatos.setFixedWidth(140) botonMostrarDatos.move(20, 20) menu = QMenu() for indice, columna in enumerate(nombreColumnas, start=0): accion = QAction(columna, menu) accion.setCheckable(True) accion.setChecked(True) accion.setData(indice) menu.addAction(accion) botonMostrarOcultar = QPushButton("Motrar/ocultar columnas", self) botonMostrarOcultar.setFixedWidth(180) botonMostrarOcultar.setMenu(menu) botonMostrarOcultar.move(170, 20) botonEliminarFila = QPushButton("Eliminar fila", self) botonEliminarFila.setFixedWidth(100) botonEliminarFila.move(530, 20) botonLimpiar = QPushButton("Limpiar", self) botonLimpiar.setFixedWidth(80) botonLimpiar.move(640, 20) botonCerrar = QPushButton("Cerrar", self) botonCerrar.setFixedWidth(80) botonCerrar.move(640, 306) # ======================== EVENTOS ========================= botonMostrarDatos.clicked.connect(self.datosTabla) botonEliminarFila.clicked.connect(self.eliminarFila) botonLimpiar.clicked.connect(self.limpiarTabla) botonCerrar.clicked.connect(self.close) self.tabla.itemDoubleClicked.connect(self.Suscribete) menu.triggered.connect(self.mostrarOcultar) # ======================= FUNCIONES ============================ def Suscribete(self, celda): QMessageBox.warning(self, "Suscribirse", "Hola, l@ invito a que se suscriba al " "canal.\nPor cierto hiciste doble clic sobre esta " "celda: {}. ".format(celda.text()), QMessageBox.Ok) def datosTabla(self): datos = [("1", "Andres", "Niño", "Masculino", "06/12/2019", "Colombia"), ("2", "Donald", "Trump", "Masculino", "06/12/1950", "Estados Unidos"), ("3", "María Fernanda", "Espinosa", "Femenino", "06/10/1980", "Ecuador"), ("4", "Alberto", "Canosa", "Masculino", "04/05/1876", "España"), ("5", "Virtud", "Pontes", "Femenino", "23/18/1965", "España"), ("6", "Elon", "Musk", "Masculino", "06/12/1960", "Estados Unidos"), ("7", "Richard", "Branson", "Masculino", "14/12/1956", "Reino Unido"), ("8", "Gabriel", "Garcia Marquez", "Masculino", "19/11/1948", "Colombia"), ("9", "Valentina", "Tereshkova", "Femenino", "06/03/1937", "Rusia"), ("10", "Artur", "Fischer", "Masculino", "31/12/1919", "Alemania"), ("11", "Grace", "Murray Hopper", "Femenino", "09/12/1906", "Estados Unidos"), ("12", "Guido van", "Rossum", "Masculino", "31/01/1956", "Países Bajos")] self.tabla.clearContents() row = 0 for endian in datos: self.tabla.setRowCount(row + 1) idDato = QTableWidgetItem(endian[0]) idDato.setTextAlignment(4) self.tabla.setItem(row, 0, idDato) self.tabla.setItem(row, 1, QTableWidgetItem(endian[1])) self.tabla.setItem(row, 2, QTableWidgetItem(endian[2])) self.tabla.setItem(row, 3, QTableWidgetItem(endian[3])) self.tabla.setItem(row, 4, QTableWidgetItem(endian[4])) self.tabla.setItem(row, 5, QTableWidgetItem(endian[5])) row += 1 def mostrarOcultar(self, accion): columna = accion.data() if accion.isChecked(): self.tabla.setColumnHidden(columna, False) else: self.tabla.setColumnHidden(columna, True) def eliminarFila(self): filaSeleccionada = self.tabla.selectedItems() if filaSeleccionada: fila = filaSeleccionada[0].row() self.tabla.removeRow(fila) self.tabla.clearSelection() else: QMessageBox.critical(self, "Eliminar fila", "Seleccione una fila. ", QMessageBox.Ok) def limpiarTabla(self): self.tabla.clearContents() self.tabla.setRowCount(0) def menuContextual(self, posicion): indices = self.tabla.selectedIndexes() if indices: menu = QMenu() itemsGrupo = QActionGroup(self) itemsGrupo.setExclusive(True) menu.addAction(QAction("Copiar todo", itemsGrupo)) columnas = [self.tabla.horizontalHeaderItem(columna).text() for columna in range(self.tabla.columnCount()) if not self.tabla.isColumnHidden(columna)] copiarIndividual = menu.addMenu("Copiar individual") for indice, item in enumerate(columnas, start=0): accion = QAction(item, itemsGrupo) accion.setData(indice) copiarIndividual.addAction(accion) itemsGrupo.triggered.connect(self.copiarTableWidgetItem) menu.exec_(self.tabla.viewport().mapToGlobal(posicion)) def copiarTableWidgetItem(self, accion): filaSeleccionada = [dato.text() for dato in self.tabla.selectedItems()] if accion.text() == "Copiar todo": filaSeleccionada = tuple(filaSeleccionada) else: filaSeleccionada = filaSeleccionada[accion.data()] print(filaSeleccionada) return
class LabelDataTable(QWidget): labelSelectedSignal = pyqtSignal([object], name='Table selected label changed') def __init__(self): super(LabelDataTable, self).__init__() self.selectedRows = np.array([], dtype=int) self.realScale = None self.polygon = None def initUi(self): self.tableHeaders = [ 'Center X(px)', 'Center Y(px)', 'Area(mm^2)', 'Perimeter(mm)', 'Area(px^2)', 'Perimeter(px)' ] self.tableData = np.array([]) # add action self.toolbarHBox = QHBoxLayout() self.toolbar = QToolBar() # cancel section action self.showAllAction = QAction(QIcon('./images/icons/show.png'), 'Show all regions') self.showAllAction.triggered.connect(self.showAllRegion) # save action self.saveAction = QAction(QIcon('./images/icons/CSV.png'), 'Export table as csv') self.saveAction.triggered.connect(self.saveAsCsv) self.toolbar.addAction(self.showAllAction) self.toolbar.addAction(self.saveAction) self.toolbarHBox.addSpacerItem( QSpacerItem(20, 5, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.toolbarHBox.addWidget(self.toolbar) # add a status of total information, area, perimeter shearPerimeterHBox = QHBoxLayout() shearPerimeterLabel = QLabel('Shear Perimeter: ') shearPerimeterValue = QLineEdit() shearPerimeterValue.setText('0') shearPerimeterValue.setDisabled(True) shearPerimeterHBox.addWidget(shearPerimeterLabel) shearPerimeterHBox.addWidget(shearPerimeterValue) self.shearPerimeterValue = shearPerimeterValue shearAreaHBox = QHBoxLayout() shearAreaLabel = QLabel('Shear Area: ') shearAreaValue = QLineEdit() shearAreaValue.setText('0') shearAreaValue.setDisabled(True) # shearAreaHBox.addWidget(shearAreaLabel) # shearAreaHBox.addWidget(shearAreaValue) shearPerimeterHBox.addWidget(shearAreaLabel) shearPerimeterHBox.addWidget(shearAreaValue) self.shearAreaValue = shearAreaValue totalShearFailureRegionAreaHBox = QHBoxLayout() totalShearFailureRegionAreaLabel = QLabel( 'Shear Failure Region Area (total):') totalShearFailureRegionAreaValue = QLineEdit() totalShearFailureRegionAreaValue.setText('0') totalShearFailureRegionAreaValue.setDisabled(True) totalShearFailureRegionAreaHBox.addWidget( totalShearFailureRegionAreaLabel) totalShearFailureRegionAreaHBox.addWidget( totalShearFailureRegionAreaValue) # totalShearFailureRegionAreaHBox.addSpacerItem(QSpacerItem(20, 5, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.totalShearFailureRegionAreaValue = totalShearFailureRegionAreaValue group = QGroupBox() group.setTitle('Summary') vBox = QVBoxLayout(group) vBox.addLayout(shearPerimeterHBox) vBox.addLayout(shearAreaHBox) vBox.addLayout(totalShearFailureRegionAreaHBox) group.setLayout(vBox) self.table = QTableWidget( 0, 6 ) # id,area(mm^2), perimeter(mm), area(px^2), perimeter(px),cx(px),cy(px) self.table.setHorizontalHeaderLabels(self.tableHeaders) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) # set triggers for select # selectionModel = self.table.selectionModel() self.table.selectionModel().selectionChanged.connect( self.itemClickedAction) self.setWindowTitle('Shear Failure Region Detection Result') self.setWindowIcon(QIcon('./images/icons/table-white.png')) self.table.verticalHeader().setVisible(True) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(group) self.mainLayout.addWidget(self.table) self.mainLayout.addLayout(self.toolbarHBox) self.setLayout(self.mainLayout) self.labelData = [] self.resize(self.table.size().width(), self.size().height()) def setData(self, labelMask, realScale, cropPolygon: QPolygonF): self.initUi() from Image import imageLabelProperty self.realScale = realScale self.labelData = imageLabelProperty(labelMask) self.tableData = np.zeros([len(self.labelData), 6]) for i, row in enumerate(self.labelData): self.tableData[i, 0:6] = [ row.centroid[0], row.centroid[1], row.area * realScale * realScale if realScale else 0, row.perimeter * realScale if realScale else 0, row.area, row.perimeter ] # shapely from shapely.geometry import Polygon data = [] for point in cropPolygon: data.append([point.x(), point.y()]) self.polygon = Polygon(data) if not realScale: self.shearPerimeterValue.setText('%.2f px' % (self.polygon.length)) self.shearAreaValue.setText('%.2f px^2' % (self.polygon.area)) self.totalShearFailureRegionAreaValue.setText( '%.2f px^2' % (np.sum(self.tableData[:, 4]))) else: self.shearPerimeterValue.setText( '%.2f px / %.2f mm' % (self.polygon.length, self.polygon.length * self.realScale)) self.shearAreaValue.setText('%.2f px^2 / %.2f mm^2' % (self.polygon.area, self.polygon.area * self.realScale * self.realScale)) self.totalShearFailureRegionAreaValue.setText( '%.2f px^2 / %.2f mm^2' % (np.sum(self.tableData[:, 4]), np.sum(self.tableData[:, 2]))) self.updateTable() def updateTable(self): self.table.setRowCount(len(self.labelData) + 1) for r in range(self.tableData.shape[0]): for c in range(self.tableData.shape[1]): item = QTableWidgetItem('%.2f' % self.tableData[r, c]) item.setTextAlignment(Qt.AlignCenter | Qt.AlignVCenter) self.table.setItem(r, c, item) totalRow = len(self.tableData) item = QTableWidgetItem('Total') item.setTextAlignment(Qt.AlignCenter | Qt.AlignVCenter) item.setBackground(QColor(240, 240, 240)) self.table.setItem(totalRow, 0, item) self.table.setSpan(totalRow, 0, 1, 2) for i in range(2, 6): item = QTableWidgetItem('%.2f' % np.sum(self.tableData[:, i])) item.setTextAlignment(Qt.AlignCenter | Qt.AlignVCenter) item.setBackground(QColor(240, 240, 240)) self.table.setItem(totalRow, i, item) self.update() def showAllRegion(self): self.table.clearSelection() def saveAsCsv(self): import datetime filePath, fileType = QFileDialog.getSaveFileName( self, 'Save File', './ShearFailureRegion-%s.csv' % (datetime.datetime.now().strftime('%Y%m%d%H%M%S')), 'CSV(*.csv)') if not filePath: QMessageBox.warning(self, 'No Path Selected', 'No Path is selected.') else: with open(filePath, 'w') as stream: writer = csv.writer(stream, lineterminator='\n') writer.writerow( ['Rock Joint Shear Failure Region Detail Information']) if not self.realScale: writer.writerow([ 'Shear Perimeter', '%.2f px' % (self.polygon.length), 'Shear Area', '%.2f px^2' % (self.polygon.area) ]) else: writer.writerow([ 'Shear Perimeter', '%.2f px / %.2f mm' % (self.polygon.length, self.polygon.length * self.realScale), 'Shear Area', '%.2f px^2 / %.2f mm^2' % (self.polygon.area, self.polygon.area * self.realScale * self.realScale), 'Real Scale', self.realScale ]) writer.writerow(self.tableHeaders) writer.writerows(list(self.tableData)) writer.writerow([ 'Total', '', self.table.item(self.table.rowCount() - 1, 2).text(), self.table.item(self.table.rowCount() - 1, 3).text(), self.table.item(self.table.rowCount() - 1, 4).text(), self.table.item(self.table.rowCount() - 1, 5).text(), ]) def itemClickedAction(self, selected, deselected): selectedRows = np.unique( np.array(list(map(lambda x: x.row() + 1, selected.indexes())))) deselectedRows = np.unique( np.array(list(map(lambda x: x.row() + 1, deselected.indexes())))) print("selected add: %s; deselected: %s" % (selectedRows, deselectedRows)) self.selectedRows = np.unique( np.append(self.selectedRows, selectedRows)).astype(np.int) self.selectedRows = np.setdiff1d(self.selectedRows, deselectedRows).astype(np.int) selectedRows = self.selectedRows if not len(self.selectedRows): selectedRows = np.arange(1, self.table.rowCount()) print("selected: %s" % selectedRows) self.labelSelectedSignal.emit(selectedRows)
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.icon_path = 'resources\\warframe.ico' self.app_title = 'Warframe Prime Helper' self.company_name = 'Warframe Tools' self.settings = QSettings(self.company_name, self.app_title) self.setWindowTitle(self.app_title) self.market_api = None self.image_label = None self.image_label2 = None self.warframe_height = 1080 self.warframe_width = 1920 self.table = None self.mission_table = None self.slider_names = None self.sliders = None self.slider_labels = None self.slider_default_values = None self.slider_orig_values = None self.slider_values = None self.is_slider_max_set = False #self.plat_check_box = QCheckBox("Prefer platinum") #self.plat_check_box.setChecked(True) self.update_prices_button = None self.update_ducats_button = None self.update_prices_progress = None self.update_ducats_progress = None self.last_updated_prices_value = None self.last_updated_ducats_value = None self.num_parts_value = None self.latest_item_value = None self.move_to_top_check_box = None self.pause_button = None self.is_paused = False self.hide_crop_check_box = None self.hide_filter_check_box = None self.hide_fissure_check_box = None self.relics = None self.hide_relics = {} self.hidden_relics = set() self.hide_missions = {} self.hidden_missions = set() self.hide_missions_box = None self.mission_names = None self.crop_img = None self.filter_img = None self.dialog = None self.layout = None self.filled_rows = 0 self.max = -1 self.max_row = -1 self.ocr = None self.old_screenshot_shape = 0 self.old_filtered_shape = 0 self.missions = [] self.num_primes = 100 self.api = None self.prices_progress_lock = Lock() self.ducats_progress_lock = Lock() self.ducats_thread = None self.prices_thread = None self.timer = None self.init_image_labels() self.init_tables() self.init_sliders() self.init_dialog() self.set_layout() self.init_timer() self.show() #measured correct values self.setFixedSize(978, 617) def init_timer(self): self.timer = QTimer() self.timer.timeout.connect(self.update_mission_table_time) self.timer.start(1000) def init_image_labels(self): self.image_label = QLabel() image = QPixmap('temp\\crop_27.bmp') self.image_label.setPixmap(image) self.image_label2 = QLabel() self.image_label2.setPixmap(image) def set_layout(self): settings_button_box = self.make_settings_button_box() self.init_imgs() bot_box = self.make_bot_box() self.layout = QVBoxLayout() self.layout.setAlignment(Qt.AlignTop) self.layout.addSpacing(-12) self.layout.addLayout(settings_button_box) self.layout.addWidget(self.crop_img) self.layout.addWidget(self.filter_img) self.layout.addWidget(bot_box) self.setLayout(self.layout) def make_settings_button_box(self): settings_button = QPushButton() # Gear icon is from: https://iconscout.com/icon/gear-222 style_sheet = """ QPushButton { qproperty-icon: url(" "); qproperty-iconSize: 15px 15px; border-image: url("resources/Gear.svg"); background-color: rgba(255, 255, 255, 0); } QPushButton:hover { border-image: url("resources/SelectedGear.svg"); }""" settings_button.setStyleSheet(style_sheet) #settings_button.setStyleSheet("background-color: rgba(0, 0, 0, 255); font-size: 23px;") settings_button.clicked.connect(self.show_preferences) settings_button.setFixedWidth(30) settings_button.setFixedHeight(30) settings_button_hb = QHBoxLayout() settings_button_hb.setAlignment(Qt.AlignRight) settings_button_hb.addWidget(settings_button) settings_button_hb.addSpacing(-11) return settings_button_hb def make_bot_box(self): bot_layout = QHBoxLayout() bot_layout.addWidget(self.table) bot_layout.addWidget(self.mission_table) bot_box = QGroupBox() bot_box.setLayout(bot_layout) bot_box.setFixedHeight(287) return bot_box def init_dialog(self): crop_box = self.make_crop_box() filter_box = self.make_filter_box() other_box = self.make_other_box() settings_layout_1 = QVBoxLayout() settings_layout_1.addWidget(crop_box) settings_layout_1.addWidget(filter_box) settings_layout_1.addWidget(other_box) update_box = self.make_update_box() rate_box = self.make_rate_box() settings_layout_2 = QVBoxLayout() settings_layout_2.addWidget(update_box) settings_layout_2.addWidget(rate_box) hide_box = self.make_hide_box() hide_relics_box = self.make_hide_relics_box() button_box = self.make_button_box() settings_layout_3 = QVBoxLayout() settings_layout_3.addWidget(hide_box) settings_layout_3.addWidget(hide_relics_box) hide_missions_box = self.make_hide_missions_box() settings_layout_4 = QVBoxLayout() settings_layout_4.addWidget(hide_missions_box) settings_layout_4.addWidget(button_box) settings_layout = QHBoxLayout() settings_layout.addLayout(settings_layout_1) settings_layout.addLayout(settings_layout_2) settings_layout.addLayout(settings_layout_3) settings_layout.addLayout(settings_layout_4) self.dialog = QDialog() self.dialog.setWindowTitle("Preferences") self.dialog.setWindowModality(Qt.ApplicationModal) self.dialog.setLayout(settings_layout) def make_button_box(self): button_box = QDialogButtonBox() button_box.setOrientation(Qt.Horizontal) button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) button_box.accepted.connect(self.save_settings) button_box.rejected.connect(self.load_settings) return button_box def load_settings(self): self.settings.sync() slider_orig_values = {'x': 521, 'y': 400, 'w': 908, 'h': 70, 'v1': 197, 'v2': 180, 'd': 4, 'Screencap (hz)': 1, 'Fissure (s)': 30, 'API Threads': 4} self.slider_default_values = {} slider_default_max = {'x': self.warframe_width/2, 'y': self.warframe_height/2, 'w': self.warframe_width, 'h': self.warframe_height, 'v1': 255, 'v2': 255, 'd': 40} for slider_name in self.slider_names: self.slider_default_values[slider_name] = self.settings.value(slider_name,defaultValue=slider_orig_values[slider_name]) if len(slider_name) <= 2: max_val = self.settings.value("{}_max".format(slider_name), defaultValue=slider_default_max[slider_name], type=int) self.sliders[slider_name].setMaximum(max_val) self.sliders[slider_name].setValue(self.slider_default_values[slider_name]) prices = self.settings.value("last_updated_prices_value", defaultValue="Never", type=str) ducats = self.settings.value("last_updated_ducats_value", defaultValue="Never", type=str) num_parts = self.settings.value("num_parts_value", defaultValue=350, type=int) latest_item = self.settings.value("latest_item_value", defaultValue="", type=str) self.last_updated_prices_value.setText(prices) self.last_updated_ducats_value.setText(ducats) self.num_parts_value.setNum(num_parts) self.latest_item_value.setText(latest_item) for relic in self.relics: checked = self.settings.value("hide_{}".format(relic), defaultValue=False,type=bool) self.hide_relics[relic].setChecked(checked) if checked: self.set_hidden_relic(relic) for mission in self.mission_names: checked = self.settings.value("hide_{}".format(mission), defaultValue=False, type=bool) self.hide_missions[mission].setChecked(checked) if checked: self.set_hidden_mission(mission) if self.settings.value("toggle_fissure_table", defaultValue=False, type=bool): self.hide_fissure_check_box.setChecked(True) self.toggle_fissure_table() if self.settings.value("toggle_move_to_top", defaultValue=False, type=bool): self.move_to_top_check_box.setChecked(True) self.toggle_move_to_top() if self.settings.value("toggle_cropped_img", defaultValue=False, type=bool): self.hide_crop_check_box.setChecked(True) self.toggle_cropped_img() if self.settings.value("toggle_filtered_img", defaultValue=False, type=bool): self.hide_filter_check_box.setChecked(True) self.toggle_filtered_img() self.dialog.close() def save_settings(self): for slider_name in self.slider_names: self.settings.setValue(slider_name, self.sliders[slider_name].value()) for relic in self.relics: self.settings.setValue("hide_{}".format(relic), self.hide_relics[relic].isChecked()) for mission in self.mission_names: self.settings.setValue("hide_{}".format(mission), self.hide_missions[mission].isChecked()) self.settings.setValue("toggle_fissure_table", self.hide_fissure_check_box.isChecked()) self.settings.setValue("toggle_move_to_top", self.move_to_top_check_box.isChecked()) self.settings.setValue("toggle_cropped_img", self.hide_crop_check_box.isChecked()) self.settings.setValue("toggle_filtered_img", self.hide_filter_check_box.isChecked()) self.dialog.close() self.settings.sync() def init_imgs(self): self.crop_img = QGroupBox("Crop") crop_img_layout = QVBoxLayout() crop_img_layout.addWidget(self.image_label) self.crop_img.setLayout(crop_img_layout) self.filter_img = QGroupBox("Filtered") filter_img_layout = QVBoxLayout() filter_img_layout.addWidget(self.image_label2) self.filter_img.setLayout(filter_img_layout) def make_hide_missions_box(self, missions=None): if self.hide_missions_box is None: self.hide_missions_box = QGroupBox("Hide Missions") if missions is not None: hide_missions_layout = QGridLayout() hide_missions_layout.setColumnStretch(2, 2) hide_missions_layout.setAlignment(Qt.AlignTop) hide_missions_layout.setContentsMargins(0, 0, 0, 0) skip_missions = ["MT_SECTOR", "MT_PVP", "MT_LANDSCAPE", "MT_EVACUATION", "MT_ASSASSINATION", "MT_ARENA"] seen_missions = set() i = 0 for mission in missions: mission_name = missions[mission]['value'] if mission_name == "Extermination": mission_name = "Exterminate" if mission not in skip_missions and mission_name not in seen_missions: self.hide_missions[mission_name] = QCheckBox(mission_name) self.hide_missions[mission_name].setChecked(False) self.hide_missions[mission_name].stateChanged.connect(partial(self.set_hidden_mission, mission_name)) hide_missions_layout.addWidget(self.hide_missions[mission_name], int(i/2), i % 2) i += 1 seen_missions.add(mission_name) self.hide_missions_box.setLayout(hide_missions_layout) self.mission_names = list(seen_missions) self.load_settings() return self.hide_missions_box def set_hidden_mission(self, mission): if self.hide_missions[mission].isChecked(): self.hidden_missions.add(mission) else: self.hidden_missions.remove(mission) self.update_mission_table_hidden() def make_hide_relics_box(self): hide_relics_layout = QVBoxLayout() hide_relics_layout.setAlignment(Qt.AlignTop) hide_relics_layout.setContentsMargins(0, 0, 0, 0) self.relics = ["Axi", "Neo", "Meso", "Lith", "Requiem"] for relic in self.relics: self.hide_relics[relic] = QCheckBox(relic) self.hide_relics[relic].setChecked(False) self.hide_relics[relic].stateChanged.connect(partial(self.set_hidden_relic, relic)) hide_relics_layout.addWidget(self.hide_relics[relic]) hide_relics_box = QGroupBox("Hide Relics") hide_relics_box.setLayout(hide_relics_layout) return hide_relics_box def make_hide_box(self): hide_layout = QVBoxLayout() hide_layout.setAlignment(Qt.AlignTop) hide_layout.setContentsMargins(0, 0, 0, 0) self.hide_crop_check_box = QCheckBox("Hide Crop") self.hide_crop_check_box.setChecked(False) self.hide_crop_check_box.stateChanged.connect(self.toggle_cropped_img) hide_layout.addWidget(self.hide_crop_check_box) self.hide_filter_check_box = QCheckBox("Hide Filtered") self.hide_filter_check_box.setChecked(False) self.hide_filter_check_box.stateChanged.connect(self.toggle_filtered_img) hide_layout.addWidget(self.hide_filter_check_box) self.hide_fissure_check_box = QCheckBox("Hide Fissure Table") self.hide_fissure_check_box.setChecked(False) self.hide_fissure_check_box.stateChanged.connect(self.toggle_fissure_table) hide_layout.addWidget(self.hide_fissure_check_box) hide_box = QGroupBox("Hide UI") hide_box.setLayout(hide_layout) return hide_box def make_rate_box(self): rate_grid = QGridLayout() rate_grid.setColumnStretch(3, 3) rate_grid.setContentsMargins(0, 0, 0, 0) for i in range(3): slider_name = self.slider_names[i + 7] rate_grid.addWidget(self.slider_labels[slider_name], i, 0) rate_grid.addWidget(self.slider_values[slider_name], i, 1) rate_grid.addWidget(self.sliders[slider_name], i, 2) rate_box = QGroupBox("Rates") rate_box.setLayout(rate_grid) return rate_box def make_other_box(self): self.move_to_top_check_box = QCheckBox("Bring to front") self.move_to_top_check_box.setChecked(True) self.move_to_top_check_box.stateChanged.connect(self.toggle_move_to_top) self.pause_button = QPushButton("Pause") self.pause_button.clicked.connect(self.toggle_button) self.is_paused = False other_layout = QVBoxLayout() other_layout.setAlignment(Qt.AlignTop) other_layout.setContentsMargins(0, 0, 0, 0) other_layout.addWidget(self.move_to_top_check_box) other_layout.addWidget(self.pause_button) other_box = QGroupBox("Other") other_box.setLayout(other_layout) return other_box def make_filter_box(self): filter_grid = QGridLayout() filter_grid.setColumnStretch(3, 3) filter_grid.setAlignment(Qt.AlignTop) filter_grid.setContentsMargins(0, 0, 0, 0) for i in range(3): slider_name = self.slider_names[i + 4] filter_grid.addWidget(self.slider_labels[slider_name], i, 0) filter_grid.addWidget(self.slider_values[slider_name], i, 1) filter_grid.addWidget(self.sliders[slider_name], i, 2) filter_box = QGroupBox("Filter Parameters") filter_box.setLayout(filter_grid) return filter_box def make_crop_box(self): crop_grid = QGridLayout() crop_grid.setColumnStretch(3, 4) crop_grid.setAlignment(Qt.AlignTop) crop_grid.setContentsMargins(0, 0, 0, 0) for i in range(4): slider_name = self.slider_names[i] crop_grid.addWidget(self.slider_labels[slider_name], i, 0) crop_grid.addWidget(self.slider_values[slider_name], i, 1) crop_grid.addWidget(self.sliders[slider_name], i, 2) crop_box = QGroupBox("Crop Parameters") crop_box.setLayout(crop_grid) return crop_box def make_update_box(self): update_layout = QGridLayout() update_layout.setColumnStretch(4, 2) update_layout.setAlignment(Qt.AlignTop) update_layout.setContentsMargins(0, 0, 0, 0) self.update_prices_button = QPushButton("Update Prices") self.update_prices_button.clicked.connect(self.update_prices) self.update_prices_progress = QProgressBar() self.update_prices_progress.setFixedWidth(110) self.update_prices_progress.setRange(0, 100) update_layout.addWidget(self.update_prices_button, 0, 0) update_layout.addWidget(self.update_prices_progress, 0, 1) self.update_ducats_button = QPushButton("Update Ducats") self.update_ducats_button.clicked.connect(self.update_ducats) self.update_ducats_progress = QProgressBar() self.update_ducats_progress.setFixedWidth(110) self.update_ducats_progress.setRange(0, 100) update_layout.addWidget(self.update_ducats_button, 1, 0) update_layout.addWidget(self.update_ducats_progress, 1, 1) last_updated_prices_label = QLabel("Prices Updated") prices = self.settings.value("last_updated_prices_value", defaultValue="Never", type=str) ducats = self.settings.value("last_updated_ducats_value", defaultValue="Never", type=str) num_parts = self.settings.value("num_parts_value", defaultValue=350, type=str) latest_item = self.settings.value("latest_item_value", defaultValue="", type=str) self.last_updated_prices_value = QLabel(prices) self.last_updated_ducats_value = QLabel(ducats) self.num_parts_value = QLabel(num_parts) self.latest_item_value = QLabel(latest_item) update_layout.addWidget(last_updated_prices_label, 2, 0) update_layout.addWidget(self.last_updated_prices_value, 2, 1) last_updated_ducats_label = QLabel("Ducats Updated") update_layout.addWidget(last_updated_ducats_label, 3, 0) update_layout.addWidget(self.last_updated_ducats_value, 3, 1) num_parts_label = QLabel("Prime Parts") update_layout.addWidget(num_parts_label, 4, 0) update_layout.addWidget(self.num_parts_value, 4, 1) latest_item_label = QLabel("Latest Prime") update_layout.addWidget(latest_item_label, 5, 0) update_layout.addWidget(self.latest_item_value, 5, 1) update_box = QGroupBox("Updates") update_box.setLayout(update_layout) return update_box def init_sliders(self): self.slider_names = ['x', 'y', 'w', 'h', 'v1', 'v2', 'd', 'Screencap (hz)', 'Fissure (s)', 'API Threads'] self.sliders = {x: QSlider(Qt.Horizontal) for x in self.slider_names} self.slider_labels = {x: QLabel(x) for x in self.slider_names} self.slider_default_values = {} self.slider_orig_values = {'x': 521, 'y': 400, 'w': 908, 'h': 70, 'v1': 197, 'v2': 180, 'd': 4, 'Screencap (hz)': 1, 'Fissure (s)': 30, 'API Threads': 4} for slider_name in self.slider_names: self.slider_default_values[slider_name] = self.settings.value(slider_name, defaultValue=self.slider_orig_values[slider_name]) self.slider_values = {x: QLabel(str(self.slider_default_values[x])) for x in self.slider_names} self.slider_values['Screencap (hz)'].setNum(self.slider_default_values['Screencap (hz)']/4) self.slider_values['d'].setNum(self.slider_default_values['d'] / 4) self.slider_labels['d'].setText('\u0394') self.sliders['x'].setMaximum(int(self.warframe_width / 2)) self.sliders['y'].setMaximum(int(self.warframe_height / 2)) self.sliders['w'].setMaximum(self.warframe_width) self.sliders['h'].setMaximum(self.warframe_height) self.sliders['v1'].setMaximum(255) self.sliders['v2'].setMaximum(255) self.sliders['d'].setMaximum(40) self.sliders['Screencap (hz)'].setMaximum(20) self.sliders['Screencap (hz)'].setMinimum(1) self.sliders['Fissure (s)'].setMaximum(60) self.sliders['Fissure (s)'].setMinimum(10) self.sliders['API Threads'].setMaximum(10) self.sliders['API Threads'].setMinimum(2) for slider_name in self.slider_names: if len(slider_name) <= 2: self.sliders[slider_name].setMinimum(0) self.sliders[slider_name].setSingleStep(1) self.slider_values[slider_name].setFixedWidth(35) self.sliders[slider_name].setValue(self.slider_default_values[slider_name]) def init_tables(self): self.table = QTableWidget(7, 3) self.table.setHorizontalHeaderLabels(['Prime Part', 'Plat', 'Ducats']) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) header = self.table.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.mission_table = QTableWidget(30, 4) self.mission_table.setHorizontalHeaderLabels(['Relic', 'Mission', 'Type', 'Time Left']) self.mission_table.setEditTriggers(QAbstractItemView.NoEditTriggers) mission_header = self.mission_table.horizontalHeader() for i in range(4): mission_header.setSectionResizeMode(i, QHeaderView.Interactive) mission_header.resizeSection(0, 55) mission_header.resizeSection(1, 150) mission_header.resizeSection(2, 90) mission_header.resizeSection(3, 60) self.mission_table.setFixedWidth(405) def update_prices(self): self.prices_thread = threading.Thread(name="prices_thread", target=self.market_api.update_prices) self.prices_thread.start() self.update_prices_button.setEnabled(False) self.update_ducats_button.setEnabled(False) def update_ducats(self): self.ducats_thread = threading.Thread(name="ducats_thread", target=self.market_api.update_ducats) self.ducats_thread.start() self.update_prices_button.setEnabled(False) self.update_ducats_button.setEnabled(False) def update_primes_info(self, num, latest): self.num_parts_value.setNum(num) self.latest_item_value.setText(latest) self.update_prices_progress.setMaximum(num) self.update_ducats_progress.setMaximum(num) self.num_primes = num def get_datetime(self): return datetime.now().strftime("%b %d %Y %H:%M:%S") def update_ducats_time(self): self.last_updated_ducats_value.setText(self.get_datetime()) self.ducats_progress_lock.acquire() self.update_ducats_progress.setValue(self.num_primes) self.ducats_progress_lock.release() self.settings.setValue("last_updated_ducats_value", self.last_updated_ducats_value.text()) self.settings.setValue("num_parts_value", self.num_parts_value.text()) self.settings.setValue("latest_item_value", self.latest_item_value.text()) def update_prices_time(self): self.last_updated_prices_value.setText(self.get_datetime()) self.prices_progress_lock.acquire() self.update_prices_progress.setValue(self.num_primes) self.prices_progress_lock.release() self.settings.setValue("last_updated_prices_value", self.last_updated_prices_value.text()) self.settings.setValue("num_parts_value", self.num_parts_value.text()) self.settings.setValue("latest_item_value", self.latest_item_value.text()) def set_update_prices_progress(self, val): if self.prices_progress_lock.acquire(): self.update_prices_progress.setValue(val) self.prices_progress_lock.release() def set_update_ducats_progress(self, val): if self.ducats_progress_lock.acquire(): self.update_ducats_progress.setValue(val) self.ducats_progress_lock.release() def finished_update_progress(self): self.update_prices_button.setEnabled(True) self.update_ducats_button.setEnabled(True) def show_preferences(self): self.dialog.exec_() self.load_settings() def toggle_fissure_table(self): if self.hide_fissure_check_box.isChecked(): self.mission_table.hide() else: self.mission_table.show() self.setFixedSize(self.layout.sizeHint()) def toggle_move_to_top(self): self.ocr.set_move_to_top(self.move_to_top_check_box.isChecked()) def toggle_cropped_img(self): if self.hide_crop_check_box.isChecked(): self.crop_img.hide() else: self.crop_img.show() self.setFixedSize(self.layout.sizeHint()) def toggle_filtered_img(self): if self.hide_filter_check_box.isChecked(): self.filter_img.hide() else: self.filter_img.show() self.setFixedSize(self.layout.sizeHint()) #print("{}h,{}w".format(self.frameGeometry().height(),self.frameGeometry().width())) def set_sliders_range(self, x, y): max_values = {'x': int(x/2), 'y': int(y/2), 'w':x, 'h':y} for slider_name in max_values: self.sliders[slider_name].setMaximum(max_values[slider_name]) self.sliders[slider_name].setValue(self.slider_default_values[slider_name]) self.settings.setValue("{}_max", max_values[slider_name]) def toggle_button(self): self.is_paused = not self.is_paused if self.is_paused: self.pause_button.setText("Resume") else: self.pause_button.setText("Pause") if self.ocr is not None: if self.is_paused: self.ocr.save_screenshot() self.ocr.skip_screenshot = True else: self.ocr.skip_screenshot = False def clear_table(self): self.table.clearSelection() self.table.clearContents() self.filled_rows = 0 #self.table.setRowCount(self.filled_rows) def is_plat_preferred(self): return self.plat_check_box.isChecked() def insert_table_row(self, row): for i in range(3): self.table.setItem(self.filled_rows, i, QTableWidgetItem(str(row[i]))) self.filled_rows = self.filled_rows + 1 #self.table.setRowCount(self.filled_rows) def update_mission_table(self, missions): self.missions = list(missions) cur_time = time.time() for i in range(len(missions)): for j in range(3): self.mission_table.setItem(i, j, QTableWidgetItem(str(self.missions[i][j]))) self.mission_table.setItem(i, 3, QTableWidgetItem(self.get_duration_str(self.missions[i][3]-cur_time))) if self.missions[i][0] in self.hidden_relics: self.mission_table.setRowHidden(i, True) if self.missions[i][2] in self.hidden_missions: self.mission_table.setRowHidden(i, True) else: self.mission_table.setRowHidden(i, False) self.mission_table.setRowCount(len(self.missions)-1) def update_mission_table_time(self): cur_time = time.time() needs_update = False for i in range(len(self.missions)): self.mission_table.setItem(i, 3, QTableWidgetItem(self.get_duration_str(self.missions[i][3]-cur_time))) if self.missions[i][3]-cur_time < 0: needs_update = True if needs_update: self.api.filter_expired_missions() def update_mission_table_hidden(self): for i in range(len(self.missions)): if self.missions[i][0] in self.hidden_relics: self.mission_table.setRowHidden(i, True) elif self.missions[i][2] in self.hidden_missions: self.mission_table.setRowHidden(i, True) else: self.mission_table.setRowHidden(i, False) def get_duration_str(self, duration): m, s = divmod(int(duration), 60) h, m = divmod(m, 60) return '{:d}:{:02d}:{:02d}'.format(h, m, s) def set_ocr_connection(self, ocr): for slider_name in self.slider_names: self.sliders[slider_name].valueChanged.connect(partial(self.set_ocr_crop, ocr, slider_name)) self.ocr = ocr self.market_api = MarketReader(ocr=self.ocr, gui=self) def set_api(self, wf_api): self.api = wf_api self.make_hide_missions_box(self.api.mission_types) def set_hidden_relic(self, relic): if self.hide_relics[relic].isChecked(): self.hidden_relics.add(relic) else: self.hidden_relics.remove(relic) self.update_mission_table_hidden() def set_ocr_crop(self, ocr, dim, val): if dim == 'Screencap (hz)' or dim == 'd': val = val / 4 self.slider_values[dim].setNum(val) if val < 0 or val > 100000 or val is None: return if dim == 'x': ocr.set_x_offset(val) if dim == 'y': ocr.set_y_offset(val) if dim == 'w': ocr.set_w(val) if dim == 'h': ocr.set_h(val) if dim == 'v1': ocr.set_v1(val) if dim == 'v2': ocr.set_v2(val) if dim == 'd': self.ocr.set_diff_threshold(val/4) if dim == 'Screencap (hz)': ocr.set_interval(1/val) if dim == 'Fissure (s)': self.api.set_rate(val) if dim == 'API Threads': self.market_api.set_num_threads(val) #def select_max(self): # # TODO doesnt work # self.table.clearSelection() # self.table.selectRow(self.max_row) def update_filtered(self, filtered): filtered_shape = None if not self.hide_filter_check_box.isChecked(): filtered_shape = filtered.shape h, w = filtered.shape bytes_per_line = w filtered_pix = QPixmap(QImage(filtered, w, h, bytes_per_line, QImage.Format_Grayscale8)) filtered_pix = filtered_pix.scaled(908, 70, Qt.KeepAspectRatio) self.image_label2.setPixmap(filtered_pix) #self.update_window_size(None, filtered_shape) def update_screenshot(self, screenshot): screenshot_shape = None if not self.hide_crop_check_box.isChecked(): screenshot_shape = screenshot.shape h, w, ch = screenshot.shape bytes_per_line = ch * w screenshot_pix = QPixmap(QImage(screenshot, w, h, bytes_per_line, QImage.Format_RGB888)) screenshot_pix = screenshot_pix.scaled(908, 70, Qt.KeepAspectRatio) self.image_label.setPixmap(screenshot_pix) #self.update_window_size(screenshot_shape, None) def update_window_size(self, screenshot_shape, filtered_shape): should_update = False if screenshot_shape is not None and screenshot_shape == self.old_screenshot_shape: self.old_screenshot_shape = screenshot_shape should_update = True if filtered_shape is not None and filtered_shape == self.old_filtered_shape: self.old_filtered_shape = filtered_shape should_update = True if should_update: self.setFixedSize(self.layout.sizeHint()) def __exit__(self): self.market_api.exit_now = True self.ocr.exit_now = True
class FourBarOptimizer(QWidget): def __init__(self): super().__init__() # Upon startup, run a user interface routine self.init_ui() def init_ui(self): self.damping = 0.1 self.iterations = 0 self.maxIterations = 1 self.mechanismStartAngle = 0.017 #self.thetaTargets = [0,90,180,270] #self.xTargets = [1,4,6,3] #self.yTargets = [1,0,1,2] self.thetaTargets = [0,90,180,270] self.xTargets = [1,3,1,2] self.yTargets = [1,2,2,1] self.betas = [0,10,20] self.gammas = [5,15,25] self.targets = [self.thetaTargets,self.xTargets,self.yTargets] self.exact = [True,True,False,True] self.exactSelected = sum(self.exact) self.fixedPointCount = min(len(self.thetaTargets),len(self.xTargets),len(self.yTargets)) # Mechanism encoding: [[lengths],[base1],[base2],] self.initialMechanismBases = [[0.06,0.04],[1.5,-0.02]] baselength = self.vLen(self.initialMechanismBases[0],self.initialMechanismBases[1]) #self.initialMechanismLengths = [baselength,0.3,0.75,0.6,1,1.5] self.initialMechanismLengths = [baselength,0.25, 1.1,0.83,1.25,1.46] self.initialAlpha = 0.75 # Four Bar Properties self.mechanismBases = self.initialMechanismBases self.mechanismLengths = self.initialMechanismLengths self.mechanismPoints = self.calculateFourBarPoint(self.initialAlpha) self.plotAngles = np.linspace(0.0,6.28318530718,num=20).tolist() self.plotAngles.append(self.plotAngles[0]) self.pathX = [0]*len(self.plotAngles) self.pathY = [0]*len(self.plotAngles) #Builds GUI self.setGeometry(200,30,1000,700) self.load_button = QPushButton('Load Data',self) self.load_button.clicked.connect(self.load_data) self.runButton = QPushButton('Run Optimization',self) self.runButton.clicked.connect(self.runFourBarOptimization) self.dampingSlider = QSlider(Qt.Horizontal) self.dampingSlider.setMinimum(1) self.dampingSlider.setMaximum(1000) #self.dampingSlider.setTickInterval(10) #self.dampingSlider.setSingleStep(0.01) self.dampingSlider.setValue(self.damping*1000) self.dampingSlider.valueChanged.connect(self.dampingChanged) self.dampingLabel = QLabel("Damping = %1.2f"%(self.damping),self) # Results Labels self.iterationLabel = QLabel("Iterations Not Started",self) self.length1Label = QLabel("Length 1 Not Computed Yet",self) self.length2Label = QLabel("Length 2 Not Computed Yet",self) self.length3Label = QLabel("Length 3 Not Computed Yet",self) self.length4Label = QLabel("Length 4 Not Computed Yet",self) self.length5Label = QLabel("Length 5 Not Computed Yet",self) self.base1Label = QLabel("Base 1 Location Not Computed Yet",self) self.base2Label = QLabel("Base 2 Location Not Computed Yet",self) #Set up a Table to display data self.data_table = QTableWidget() self.data_table.itemSelectionChanged.connect(self.selectedTableItem) self.data_table.cellChanged.connect(self.cellChanged) self.data_table.setColumnCount(4) self.data_table.setRowCount(len(self.thetaTargets)+1) self.data_table.setHorizontalHeaderLabels(['Exact Match?','Crank Angle','X','Y']) self.main_widget = QWidget(self) self.graph_canvas = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=120) targets = [self.thetaTargets,self.xTargets,self.yTargets] self.calculateActualPath(self.plotAngles) path = [self.pathX,self.pathY] mechanismPoints = self.calculateFourBarPoint(self.initialAlpha) self.graph_canvas.plotFourBar(targets, mechanismPoints,path) #Define where the widgets go in the window #We start by defining some boxes that we can arrange #Create a GUI box to put all the table and data widgets in table_box = QGroupBox("Data Table") #Create a layout for that box using the vertical table_box_layout = QVBoxLayout() #Add the widgets into the layout table_box_layout.addWidget(self.load_button) table_box_layout.addWidget(self.data_table) #setup the layout to be displayed in the box table_box.setLayout(table_box_layout) # Results Label Box resultsBox = QGroupBox("Results") resultsBoxLayout = QVBoxLayout() resultsBoxLayout.addWidget(self.iterationLabel) resultsBoxLayout.addWidget(self.length1Label) resultsBoxLayout.addWidget(self.length2Label) resultsBoxLayout.addWidget(self.length3Label) resultsBoxLayout.addWidget(self.length4Label) resultsBoxLayout.addWidget(self.length5Label) resultsBoxLayout.addWidget(self.base1Label) resultsBoxLayout.addWidget(self.base2Label) resultsBox.setLayout(resultsBoxLayout) # Controls Box controlsBox = QGroupBox("Controls") controlsBoxLayout = QVBoxLayout() controlsBoxLayout.addWidget(self.runButton) controlsBoxLayout.addWidget(self.dampingLabel) controlsBoxLayout.addWidget(self.dampingSlider) controlsBox.setLayout(controlsBoxLayout) #Now we can set all the previously defined boxes into the main window grid_layout = QGridLayout() grid_layout.addWidget(table_box,0,0) grid_layout.addWidget(resultsBox,1,0) grid_layout.addWidget(controlsBox,1,1) grid_layout.addWidget(self.graph_canvas,0,1) #grid_layout.addWidget(distribution_box,1,1) self.redrawTable() self.setLayout(grid_layout) self.setWindowTitle('Four Bar Linkage Optimization') self.activateWindow() self.raise_() self.show() def synthesizeFourBar(self,betas,gammas): exactPoints = [] for i in range(0,len(thetaTargets)): if exact[i] == True: exactPoints.append([thetaTargets[i],xTargets[i],yTargets[i]]) delta2 = (exactPoints[1][1]-exactPoints[0][1]) + (exactPoints[1][2]-exactPoints[0][2])j delta3 = (exactPoints[2][1]-exactPoints[0][1]) + (exactPoints[2][2]-exactPoints[0][2])j def load_data(self): #Write this function options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "","All Files (*);;Python Files (*.py)", options=options) if fileName: f= open(fileName,"r") if f.mode == 'r': contents =f.read() # Do stuff with contents def save_data(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self, "QFileDialog.getSaveFileName()","", "All Files (*);;Text Files (*.txt)", options=options) # Check to make sure ends in .txt if fileName: f= open(fileName,"w+") f.write("%i" % (self.counter_value)) f.close() def calculateActualPath(self,angleList): xActual = [0]*len(angleList) yActual = [0]*len(angleList) for i in range(0,len(angleList)): points = self.calculateFourBarPoint(angleList[i]) xActual[i] = points[4][0] yActual[i] = points[4][1] self.pathX = xActual self.pathY = yActual def fourBarExpression(self): # variables = b1x,b1y,b2x,b2y,l0,l1,l2,l3,l4,l5,alpha b1x,b1y,b2x,b2y,l0,l1,l2,l3,l4,l5,alpha, a0 = symbols('b1x,b1y,b2x,b2y,l0,l1,l2,l3,l4,l5,alpha,a0') baseAngle = atan((b2y-b1y)/(b2x-b1x)) x3 = l0*cos(baseAngle) - l1*cos(alpha + a0) y3 = l0*sin(baseAngle) - l1*sin(alpha + a0) theta3ArccosValue = (x3**2 + y3**2 + (-l3)**2 - l5**2)/2 * (-l3) * sqrt(x3**2 + y3**2) theta3 = atan2(y3,x3) + acos(theta3ArccosValue) theta5 = atan2((y3-(-l3)*sin(theta3))/l5, (x3-(-l3)*cos(theta3))/l5) dyadAngle1 = acos((l5**2 + l2**2 - l4**2)/(2*l5*l2)) Cx = b1x+l1*cos(alpha + a0) + l2*cos(theta5 + dyadAngle1) Cy = b1y+l1*sin(alpha + a0) + l2*sin(theta5 + dyadAngle1) return (Cx,Cy) def calculateFourBarPoint(self,alphaInput): # lengths is in format [base, z1,z2,z3,z4,z5] # bases are in format [[base1X,base1Y],[base2X,base2Y]] # alpha is a single float for crank angle from horizontal bases = list(self.mechanismBases) length = list(self.mechanismLengths) alpha = alphaInput + self.mechanismStartAngle point1 = [0,0] point1[0] = bases[0][0] + length[1]*np.cos(alpha) point1[1] = bases[0][1] + length[1]*np.sin(alpha) baseRun = np.float64(bases[1][0] - bases[0][0]) baseRise = np.float64(bases[1][1]-bases[0][1]) #print(baseRun) #print(baseRise) baseAngle = 0 if baseRise == 0: if baseRun > 0: baseAngle = 0 elif baseRun < 0: baseAngle = pi() elif baseRun == 0: if baseRise > 0: baseAngle = pi()/2 elif baseRise < 0: baseAngle = 3*pi()/2 else: baseAngle = np.arctan(baseRise/baseRun) x3 = length[0]*cos(baseAngle) - length[1]*cos(alpha) y3 = length[0]*sin(baseAngle) - length[1]*sin(alpha) theta3ArccosValue = (x3**2 + y3**2 + (-length[3])**2 - length[5]**2)/2 * (-length[3]) * math.sqrt(x3*x3 + y3*y3) theta3ArccosValue = np.float64(theta3ArccosValue) theta3pos = math.atan2(y3,x3) + np.arccos(theta3ArccosValue) #print(theta3pos) theta3neg = math.atan2(y3,x3) - np.arccos(theta3ArccosValue) theta3 = theta3pos theta5 = math.atan2((y3-(-length[3])*np.sin(theta3))/length[5], (x3-(-length[3])*np.cos(theta3))/length[5]) point2 = [0,0] point2[0] = bases[1][0] + length[3]*np.cos(theta3) point2[1] = bases[1][1] + length[3]*np.sin(theta3) dyadAngle1 = np.arccos(np.float64(length[5]**2 + length[2]**2 - length[4]**2)/np.float64(2*length[5]*length[2])) pointC = [0,0] pointC[0] = point1[0]+length[2]*np.cos(theta5 + dyadAngle1) pointC[1] = point1[1]+length[2]*np.sin(theta5 + dyadAngle1) self.mechanismPoints = [bases[0],bases[1],point1,point2,pointC] return self.mechanismPoints def runFourBarOptimization(self): # Calculate betas and gammas for target design - 3 points # Evaluate error from target path # print('Starting Optimization') (xExpression,yExpression) = self.fourBarExpression() objectiveFunction = 0 for i in range(0,len(self.xTargets)): objectiveExpression = (self.xTargets[i] - xExpression)**2 + (self.yTargets[i] - yExpression)**2 objectiveExpression = objectiveExpression.subs(symbols('alpha'),self.thetaTargets[i]) objectiveFunction = objectiveFunction + objectiveExpression variables = ['l0','l1','l2','l3','l4','l5','a0','b1x','b1y','b2x','b2y'] rpSymbol = symbols('rp') g1 = 'l5 - (l2 + l4) + 0.01' g2 = '(l1 + l0 + 0.01) - (l5 + l3)' g3 = '-l1' g4 = '-l2' g5 = '-l3' g6 = '-l4' g7 = '-l5' inequalityConstraints = [g1,g2,g3,g4,g5,g6,g7] equalityConstraints = [] startingPoint = [self.mechanismLengths[0], self.mechanismLengths[1], self.mechanismLengths[2], self.mechanismLengths[3], self.mechanismLengths[4], self.mechanismLengths[5], self.mechanismStartAngle, self.mechanismBases[0][0], self.mechanismBases[0][1], self.mechanismBases[1][0], self.mechanismBases[1][1]] shouldContinue = True rp = 0.5 testSteps = [0,0.01,0.02] expression = objectiveFunction epsilon = 0.001 print('Set up initial constants and expressions') self.iterations = 0 position = startingPoint objectiveValue = opt.evaluateLinearExtendedPenalty(expression, inequalityConstraints=inequalityConstraints, equalityConstraints=equalityConstraints, variables = variables, values = position, rp = rp, evaluate=True) print('Calculated initial objective function:') print(objectiveValue) while shouldContinue == True: # perform 1 iteration # check validity # plot ######################## self.iterations = self.iterations + 1 print('Calculating expression for this rp') expressionHere = opt.evaluateLinearExtendedPenalty(expression, inequalityConstraints=inequalityConstraints, equalityConstraints=equalityConstraints, variables = variables, values = position, rp = rp, evaluate=False) expressionHere = expressionHere.subs(rpSymbol,float(rp)) print('Calculating gradients:') slopeList = opt.getNumGradient(expressionHere,variables,position,normalize=False) print(slopeList) #print(slopeList) # Get three points in that direction at intervals of 0.5,1,2 # #Constant step toward goal """ functionValues = [objectiveValue] for alphaValue in testSteps: if alphaValue != testSteps[0]: testLocation = [] for oldPosition, slope in zip(position,slopeList): testLocation.append(oldPosition-slope*alphaValue) #print('Location to test:') #print(testLocation) functionValues.append(opt.evaluateLinearExtendedPenalty(expression, inequalityConstraints=inequalityConstraints, equalityConstraints=equalityConstraints, variables = variables, values = testLocation, rp = rp)) print('Calculated test positions') # Fit parabola to curve print(functionValues) C = approx.threePointQuadraticApprox(testSteps, functionValues) print(C) # Check parabola is concave up # Calculate alpha that gives minimum alphaStar = 0.0 if C[2] < 0.00001: print("Fitted parabola is concave down. Minimum alpha value is not bounded.") alphaStar = 0.5 else: (alphaStar,bestY) = opt.minimizeParabola(C) """ alphaStar = 0.1 print('Calculating New Position') # Move to position of calculated alpha newPosition = [] for oldPosition, slope in zip(position,slopeList): newPosition.append(oldPosition-slope*self.damping*alphaStar) lastPosition = position position = newPosition objectiveValueLast = objectiveValue print('Finding New error') objectiveValue = opt.evaluateLinearExtendedPenalty(expression, inequalityConstraints=inequalityConstraints, equalityConstraints=equalityConstraints, variables = variables, values = position, rp = rp, evaluate=True) print('New error is:') print(objectiveValue) print('New Design is:') print(position) print('Updating Mechanism parameters') self.mechanismLengths = [position[0],position[1],position[2],position[3],position[4],position[5]] self.mechanismAngle0 = position[6] self.mechanismBases = [[position[7],position[8]],[position[9],position[10]]] print('Redrawing') # Redraw results labels self.redrawResultsLabels() # Redraw graph self.calculateActualPath(self.plotAngles) path = [self.pathX,self.pathY] mechanismPoints = self.calculateFourBarPoint(self.initialAlpha) targets = [self.thetaTargets,self.xTargets,self.yTargets] path = [self.pathX,self.pathY] self.graph_canvas.plotFourBar(targets, mechanismPoints,path) print('Checking Convergence') # Check convergence deltaObjective = abs(float(abs(objectiveValueLast) - abs(objectiveValue))) deltaVariables = [abs(new - old) for new, old in zip(position,lastPosition)] maxDelta = max(deltaVariables) if max(deltaObjective,maxDelta) < epsilon: shouldContinue = False print("Local Optimium found") if self.iterations > self.maxIterations: print("Function timed out. Returning final result") shouldContinue = False ####################### path = [self.pathX,self.pathY] mechanismPoints = self.calculateFourBarPoint(self.initialAlpha) targets = [self.thetaTargets,self.xTargets,self.yTargets] self.graph_canvas.plotFourBar(targets, self.mechanismPoints,path) def vLen(self,point1,point2): # takes in two points in the format [x,y] and returns the float of vector length dx = point1[0] - point2[0] dy = point1[1] - point2[1] length = np.sqrt(dx*dx + dy*dy) return length def dampingChanged(self,value): self.damping = float(value)/1000 self.dampingLabel.setText("Damping = %1.2f"%(self.damping)) def cellChanged(self,row,column): if row == len(self.xTargets): if column == 1: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.thetaTargets.append(cellValue) self.xTargets.append(0) self.yTargets.append(0) self.exact.append(False) elif column == 2: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.xTargets.append(cellValue) self.thetaTargets.append(0) self.ytargets.append(0) self.exact.append(False) elif column == 3: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.thetaTargets.append(0) self.xTargets.append(0) self.yTargets.append(cellValue) self.exact.append(False) else: if column == 1: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.thetaTargets[row] = cellValue elif column == 2: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.xTargets[row] = cellValue elif column == 3: cell = self.data_table.item(row, column) cellText = cell.text() cellValue = float(cellText) self.yTargets[row] = cellValue self.data_table.setRowCount(len(self.thetaTargets)+1) targets = [self.thetaTargets,self.xTargets,self.yTargets] path = [self.pathX,self.pathY] self.graph_canvas.plotFourBar(targets, self.mechanismPoints, path) def selectedTableItem(self): #print("\n") for currentQTableWidgetItem in self.data_table.selectedItems(): #print(currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), currentQTableWidgetItem.text()) if currentQTableWidgetItem.column() == 0: cellValue = self.exact[currentQTableWidgetItem.row()] if cellValue == True: self.exact[currentQTableWidgetItem.row()] = not self.exact[currentQTableWidgetItem.row()] elif cellValue == False: if self.exactSelected < 3: self.exact[currentQTableWidgetItem.row()] = not self.exact[currentQTableWidgetItem.row()] else: self.data_table.editItem(currentQTableWidgetItem) if self.exact[currentQTableWidgetItem.row()] == True: self.data_table.setItem(currentQTableWidgetItem.row(),0, QTableWidgetItem('\u2714')) else: self.data_table.setItem(currentQTableWidgetItem.row(),0, QTableWidgetItem('')) self.exactSelected = sum(self.exact) self.data_table.clearSelection() #self.redrawTable() def redrawTable(self): self.data_table.setRowCount(len(self.thetaTargets)+1) for i in range(0,len(self.thetaTargets)): if self.exact[i] == True: self.data_table.setItem(i,0, QTableWidgetItem('\u2714')) else: self.data_table.setItem(i,0, QTableWidgetItem('')) self.data_table.setItem(i,1, QTableWidgetItem('%2.4f'%(self.thetaTargets[i]))) self.data_table.setItem(i,2, QTableWidgetItem('%2.4f'%(self.xTargets[i]))) self.data_table.setItem(i,3, QTableWidgetItem('%2.4f'%(self.yTargets[i]))) def redrawResultsLabels(self): self.iterationLabel.setText("Iteration %i"%(self.damping)) self.length1Label.setText("Length 1 = %2.4f"%(self.mechanismLengths[1])) self.length2Label.setText("Length 2 = %2.4f"%(self.mechanismLengths[2])) self.length3Label.setText("Length 3 = %2.4f"%(self.mechanismLengths[3])) self.length4Label.setText("Length 4 = %2.4f"%(self.mechanismLengths[4])) self.length5Label.setText("Length 5 = %2.4f"%(self.mechanismLengths[5])) self.base1Label.setText("Base 1 Location = (%2.4f, %2.4f)"%(self.mechanismBases[0][0],self.mechanismBases[0][1])) self.base2Label.setText("Base 2 Location = (%2.4f, %2.4f)"%(self.mechanismBases[1][0],self.mechanismBases[1][1]))
class MeaGrid(QWidget): def __init__(self, parent=None): super().__init__(parent) self.layout = QtWidgets.QVBoxLayout(self) self.grid_table = QTableWidget(self) self.grid_table.setRowCount(16) self.grid_table.setColumnCount(16) self.grid_table.horizontalHeader().setVisible(False) self.grid_table.verticalHeader().setVisible(False) self.layout.addWidget(self.grid_table) self.labels = [] self.label_indices_map = dict() for col, c in enumerate([ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'R' ]): for row, n in enumerate(range(1, 17)): if c == 'A' and n == 1 or c == 'A' and n == 16 or c == 'R' and n == 1 or c == 'R' and n == 16: self.grid_table.setColumnWidth(col, 35) self.grid_table.setItem(row, col, QTableWidgetItem('')) self.grid_table.item(row, col).setFlags(QtCore.Qt.NoItemFlags) # self.grid_table.item(row, col).connect(self.on_channel_selected) continue number_str = str(n) self.labels.append(c + number_str) # if n < 10: # number_str = "0" + number_str label = (c + number_str) self.grid_table.setColumnWidth(col, 35) self.grid_table.setItem(row, col, QTableWidgetItem(label)) # creation of dictionary id = c + str(n) self.label_indices_map[id] = {} if col == 0: self.label_indices_map[id]['grid index'] = row - 1 elif col != 0 and col != 15: self.label_indices_map[id]['grid index'] = row + (col * 16) - 2 else: self.label_indices_map[id]['grid index'] = row + (col * 16) - 3 self.all_channel_indices = set( [v['grid index'] for v in list(self.label_indices_map.values())]) print(self.all_channel_indices) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.on_context_menu) def get_selected_channels(self): selected_channels = [] selected_indices = [] for item in self.grid_table.selectedItems(): label = item.text() selected_channels.append( (label, self.label_indices_map[label]['grid index'])) selected_indices.append( self.label_indices_map[label]['grid index']) selected_indices = set(selected_indices) dead_channels = self.all_channel_indices - selected_indices return selected_channels, dead_channels def select_all(self): self.grid_table.selectAll() def select_none(self): self.grid_table.clearSelection() def invert_selection(self): for row in range(self.grid_table.rowCount()): for column in range(self.grid_table.columnCount()): is_selected = self.grid_table.item(row, column).isSelected() self.grid_table.item(row, column).setSelected(not is_selected) def are_all_selected(self): channel_count = len(self.labels) return len(self.grid_table.selectedItems()) == channel_count def is_none_selected(self): return len(self.grid_table.selectedItems()) == 0 @QtCore.pyqtSlot(QtCore.QPoint) def on_context_menu(self, point): menu = QtWidgets.QMenu(self) select_all_action = QtWidgets.QAction("Select All") select_all_action.triggered.connect(self.select_all) select_all_action.setEnabled(not self.are_all_selected()) menu.addAction(select_all_action) select_none_action = QtWidgets.QAction("Select None") select_none_action.triggered.connect(self.select_none) select_none_action.setEnabled(not self.is_none_selected()) menu.addAction(select_none_action) invert_selection_action = QtWidgets.QAction("Invert Selection") invert_selection_action.triggered.connect(self.invert_selection) menu.addAction(invert_selection_action) menu.exec(self.mapToGlobal(point))
class QShapingDebugger(QSplitter): def __init__(self, editor, project): self.editor = editor self.project = project super(QSplitter, self).__init__() self.text = self.project.debuggingText or self.getReasonableTextForFont( self.project.font) # First box: Text and features self.firstbox = QWidget() self.firstboxLayout = QVBoxLayout() self.firstbox.setLayout(self.firstboxLayout) textbox = QLineEdit() textbox.setText(self.text) textbox.setMaximumHeight(textbox.height()) textbox.textChanged[str].connect(self.textChanged) self.featuregroup = QGroupBox("Features") self.featuregrouplayout = QFlowLayout() self.featuregroup.setLayout(self.featuregrouplayout) self.features = {} self.fillFeatureGroup() self.firstboxLayout.addWidget(textbox) self.firstboxLayout.addWidget(self.featuregroup) # Second box: Variations self.secondbox = QWidget() self.secondboxLayout = QHBoxLayout() self.secondbox.setLayout(self.secondboxLayout) self.sliders = [] if self.project.variations: for axis in self.project.variations.designspace.axes: self.secondboxLayout.addWidget(QLabel(axis.name)) slider = QSlider(0x01) slider.name = axis.name slider.setMinimum(axis.map_forward(axis.minimum)) slider.setMaximum(axis.map_forward(axis.maximum)) self.sliders.append(slider) slider.valueChanged.connect(self.shapeText) self.secondboxLayout.addWidget(slider) # Third box: Output and renderer self.thirdbox = QWidget() self.thirdboxLayout = QVBoxLayout() self.thirdbox.setLayout(self.thirdboxLayout) self.shaperOutput = QLabel() self.shaperOutput.setWordWrap(True) sp = self.shaperOutput.sizePolicy() sp.setVerticalPolicy(QSizePolicy.Maximum) self.shaperOutput.setSizePolicy(sp) self.qbr = QBufferRenderer(project, VariationAwareBuffer(self.project.font)) sp = self.thirdbox.sizePolicy() sp.setHorizontalPolicy(QSizePolicy.Maximum) sp.setVerticalPolicy(QSizePolicy.MinimumExpanding) self.thirdbox.setSizePolicy(sp) self.thirdboxLayout.addWidget(self.shaperOutput) self.thirdboxLayout.addWidget(self.qbr) # Third box: message table self.messageTable = QTableWidget() self.messageTable.setColumnCount(2) self.messageTable.verticalHeader().setVisible(False) self.messageTable.setHorizontalHeaderLabels(["message", "buffer"]) header = self.messageTable.horizontalHeader() headerWidth = self.messageTable.viewport().size().width() header.resizeSection(0, headerWidth * 2 / 3) header.setStretchLastSection(True) self.messageTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.messageTable.selectionModel().selectionChanged.connect( self.renderPartialTrace) self.setOrientation(Qt.Vertical) self.addWidget(self.firstbox) if self.project.variations: self.addWidget(self.secondbox) self.addWidget(self.thirdbox) self.addWidget(self.messageTable) self.fullBuffer = None self.lastBuffer = None self.shapeText() def clearLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self.clearLayout(item.layout()) def fillFeatureGroup(self): prev = self.features fkeys = self.project.fontfeatures.features.keys() self.clearLayout(self.featuregrouplayout) self.features = {} for k in fkeys: box = self.features[k] = QCheckBox(k) box.setTristate() if k in prev: box.setCheckState(prev[k].checkState()) else: box.setCheckState(Qt.PartiallyChecked) box.stateChanged.connect(self.shapeText) self.featuregrouplayout.addWidget(box) def update(self): self.fillFeatureGroup() self.shapeText() def buildBuffer(self): buf = VariationAwareBuffer(self.project.font) t = self.text i = 0 while i < len(t): if t[i] == "/": # Start of glyph name i = i + 1 glyphname = "" while i < len(t) and t[i] in valid_glyph_name_chars: glyphname += t[i] i = i + 1 if len(glyphname) and glyphname in self.project.font: item = VariationAwareBufferItem.new_glyph( glyphname, self.project.font, buf) item.codepoint = self.project.font.codepointForGlyph( glyphname) buf.items.append(item) else: buf.items.extend([ VariationAwareBufferItem.new_unicode(ord(x), buf) for x in "/" + glyphname ]) else: item = VariationAwareBufferItem.new_unicode(ord(t[i]), buf) i = i + 1 buf.items.append(item) buf.guess_segment_properties() return buf def shapeText(self): features = [] for k, box in self.features.items(): if box.checkState() == Qt.PartiallyChecked: continue features.append({"tag": k, "value": box.isChecked()}) buf = self.buildBuffer() self.messageTable.setRowCount(0) if not self.text: buf.clear_mask() self.qbr.set_buf(buf) self.fullBuffer = buf self.shaperOutput.setText(buf.serialize()) return self.messageTable.clearSelection() self.lastBuffer = None self.skipped = [] self.partialBuffers = {} shaper = Shaper( self.project.fontfeatures, self.project.font, message_function=self.addToTable, ) self.prep_shaper(shaper, buf, features) shaper.execute(buf, features=features) self.qbr.set_buf(buf) self.fullBuffer = buf self.shaperOutput.setText(buf.serialize()) def prep_shaper(self, shaper, buf, features): if not self.sliders: return buf.vf = self.project.variations loc = {slider.name: slider.value() for slider in self.sliders} buf.location = loc self.qbr.set_location(loc) def addToTable(self, msg, buffer=None, serialize_options=None): if msg.startswith("Before"): return if not buffer: # Easy one rowPosition = self.messageTable.rowCount() self.messageTable.insertRow(rowPosition) message_item = QTableWidgetItem(msg) self.messageTable.setItem(rowPosition, 0, message_item) return # Urgh b = BaseShaper(None, None, buffer) for i in range(0, len(buffer.items)): b.propagate_attachment_offsets(i) ser = buffer.serialize(additional=serialize_options) if self.lastBuffer == ser: m = re.match(r"After (\w+ \(\w+\))", msg) if m: self.skipped.append(m[1]) return elif self.skipped: rowPosition = self.messageTable.rowCount() self.messageTable.insertRow(rowPosition) message_item = QTableWidgetItem( "Routines executed but had no effect: %s" % ",".join(self.skipped)) self.messageTable.setItem(rowPosition, 0, message_item) self.skipped = [] self.lastBuffer = ser rowPosition = self.messageTable.rowCount() self.messageTable.insertRow(rowPosition) message_item = QTableWidgetItem(msg) self.messageTable.setItem(rowPosition, 0, message_item) self.partialBuffers[rowPosition] = (copy(buffer), msg) self.partialBuffers[rowPosition][0].items = deepcopy(buffer.items) buffer_item = QTableWidgetItem(ser) self.messageTable.setItem(rowPosition, 1, buffer_item) def renderPartialTrace(self): indexes = self.messageTable.selectedIndexes() if len(indexes) != 2: return row = indexes[0].row() if row in self.partialBuffers: buf, msg = self.partialBuffers[row] self.qbr.set_buf(buf) m = re.match(r"After (\w+) \((\w+)\)", msg) if m and self.editor: routine, feature = m[1], m[2] self.editor.fontfeaturespanel.lookuplist.highlight(routine) self.editor.fontfeaturespanel.featurelist.highlight( feature, routine) # else: # self.qbr.set_buf(self.fullBuffer) def textChanged(self, text): self.text = text self.project.debuggingText = text self.shapeText() def getReasonableTextForFont(self, font): text = "" if font.glyphForCodepoint(0x627, fallback=False): # Arabic text = text + "ابج " if font.glyphForCodepoint(0x915, fallback=False): # Devanagari text = text + "कचण " if font.glyphForCodepoint(0x61, fallback=False): # Latin text = text + "abc " return text.strip()
class Mostrar_Notificacion(QDialog): global nombres global tareas def __init__(self, parent=None): super(Mostrar_Notificacion, self).__init__(parent) self.setWindowTitle("Tabla de notificaciones") self.setWindowIcon(QIcon("Qt.png")) self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.MSWindowsFixedSizeDialogHint) self.setFixedSize(740, 348) self.initUI() def initUI(self): # ================== WIDGET QTableWidget ================== self.tabla = QTableWidget(self) # Deshabilitar edición self.tabla.setEditTriggers(QAbstractItemView.NoEditTriggers) # Deshabilitar el comportamiento de arrastrar y soltar self.tabla.setDragDropOverwriteMode(False) # Seleccionar toda la fila self.tabla.setSelectionBehavior(QAbstractItemView.SelectRows) # Seleccionar una fila a la vez self.tabla.setSelectionMode(QAbstractItemView.SingleSelection) # Especifica dónde deben aparecer los puntos suspensivos "..." cuando se muestran # textos que no encajan self.tabla.setTextElideMode(Qt.ElideRight) # Qt.ElideNone # Establecer el ajuste de palabras del texto self.tabla.setWordWrap(False) # Deshabilitar clasificación # Establecer el número de columnas self.tabla.setColumnCount(2) # Establecer el número de filas self.tabla.setRowCount(0) # Alineación del texto del encabezado self.tabla.horizontalHeader().setDefaultAlignment(Qt.AlignHCenter | Qt.AlignVCenter | Qt.AlignCenter) # Deshabilitar resaltado del texto del encabezado al seleccionar una fila self.tabla.horizontalHeader().setHighlightSections(False) # Hacer que la última sección visible del encabezado ocupa todo el espacio disponible self.tabla.horizontalHeader().setStretchLastSection(True) # Ocultar encabezado vertical self.tabla.verticalHeader().setVisible(False) # Dibujar el fondo usando colores alternados self.tabla.setAlternatingRowColors(True) # Establecer altura de las filas self.tabla.verticalHeader().setDefaultSectionSize(20) # self.tabla.verticalHeader().setHighlightSections(True) nombreColumnas = ("Planta", "Tarea") # Establecer las etiquetas de encabezado horizontal usando etiquetas self.tabla.setHorizontalHeaderLabels(nombreColumnas) # Menú contextual self.tabla.setContextMenuPolicy(Qt.CustomContextMenu) self.tabla.customContextMenuRequested.connect(self.menuContextual) # Establecer ancho de las columnas for indice, ancho in enumerate((80, 120, 120, 110, 150), start=0): self.tabla.setColumnWidth(indice, ancho) self.tabla.resize(700, 240) self.tabla.move(20, 56) # =================== WIDGETS QPUSHBUTTON ================== botonMostrarDatos = QPushButton("Mostrar datos", self) botonMostrarDatos.setFixedWidth(140) botonMostrarDatos.move(20, 20) menu = QMenu() for indice, columna in enumerate(nombreColumnas, start=0): accion = QAction(columna, menu) accion.setCheckable(True) accion.setChecked(True) accion.setData(indice) menu.addAction(accion) botonMostrarOcultar = QPushButton("Motrar/ocultar columnas", self) botonMostrarOcultar.setFixedWidth(180) botonMostrarOcultar.setMenu(menu) botonMostrarOcultar.move(170, 20) botonCerrar = QPushButton("Cerrar", self) botonCerrar.setFixedWidth(80) botonCerrar.move(640, 306) # ======================== EVENTOS ========================= botonMostrarDatos.clicked.connect(self.datosTabla) botonCerrar.clicked.connect(self.close) menu.triggered.connect(self.mostrarOcultar) # ======================= FUNCIONES ============================ def datosTabla(self): global nombres global tareas nombres = tuple(nombres) tareas = tuple(tareas) datos = [] for i in range(len(nombres)): par = [] par.append(nombres[i]) par.append(tareas[i]) par = tuple(par) datos.append(par) self.tabla.clearContents() row = 0 for endian in datos: self.tabla.setRowCount(row + 1) idDato = QTableWidgetItem(endian[0]) idDato.setTextAlignment(4) self.tabla.setItem(row, 0, idDato) self.tabla.setItem(row, 1, QTableWidgetItem(endian[1])) row += 1 def mostrarOcultar(self, accion): columna = accion.data() if accion.isChecked(): self.tabla.setColumnHidden(columna, False) else: self.tabla.setColumnHidden(columna, True) def eliminarFila(self): filaSeleccionada = self.tabla.selectedItems() if filaSeleccionada: fila = filaSeleccionada[0].row() self.tabla.removeRow(fila) self.tabla.clearSelection() else: QMessageBox.critical(self, "Eliminar fila", "Seleccione una fila. ", QMessageBox.Ok) def menuContextual(self, posicion): indices = self.tabla.selectedIndexes() if indices: menu = QMenu() itemsGrupo = QActionGroup(self) itemsGrupo.setExclusive(True) menu.addAction(QAction("Copiar todo", itemsGrupo)) columnas = [ self.tabla.horizontalHeaderItem(columna).text() for columna in range(self.tabla.columnCount()) if not self.tabla.isColumnHidden(columna) ] copiarIndividual = menu.addMenu("Copiar individual") for indice, item in enumerate(columnas, start=0): accion = QAction(item, itemsGrupo) accion.setData(indice) copiarIndividual.addAction(accion) itemsGrupo.triggered.connect(self.copiarTableWidgetItem) menu.exec_(self.tabla.viewport().mapToGlobal(posicion)) def copiarTableWidgetItem(self, accion): filaSeleccionada = [dato.text() for dato in self.tabla.selectedItems()] if accion.text() == "Copiar todo": filaSeleccionada = tuple(filaSeleccionada) else: filaSeleccionada = filaSeleccionada[accion.data()] print(filaSeleccionada) return
class EnrollPlayers(QWidget): current_id_num = 0 def __init__(self, tournament): super().__init__() # Runs the __init__ of QWidget, which EnrollPlayers inherits from if os.path.exists(tournament): # Check to make sure the tournament exists self.tournament = tournament self.tree = self.loadTournament(self.tournament) self.tournament_root = self.tree.getroot() # self.tournament, self.tree, and self.tournament_root are all used # to help write the xml this_tournament_name = self.tournament_root[0][0].text # Used to make the GUI display the tournament name # as the title of the window self.initUI(this_tournament_name) else: error = QMessageBox() error.setIcon(QMessageBox.Information) error.setText('No Tournament Found!') error.setWindowTitle('No Tournament Found') error.exec_() sys.exit(0) def initUI(self, tournamentName): window_layout = QHBoxLayout() self.setLayout(window_layout) window_layout.setSpacing(5) # Sets a horizontal layout for the window itself ### Enrolling Players Section ### add_player_layout = QVBoxLayout() add_player_layout.addStretch(1) first_name_layout = QHBoxLayout() first_name_layout.setSpacing(1) self.first_name_label = QLabel('First Name: ', self) self.player_first_name = QLineEdit() self.player_first_name.returnPressed.connect(self.addPlayer) self.player_first_name.setFocus(True) # Line that holds the player's first name first_name_layout.addWidget(self.first_name_label) first_name_layout.addWidget(self.player_first_name) add_player_layout.addLayout(first_name_layout) last_name_layout = QHBoxLayout() last_name_layout.setSpacing(2) self.lastNameLabel = QLabel('Last Name: ', self) self.player_last_name = QLineEdit() self.player_last_name.returnPressed.connect(self.addPlayer) # Line that holds the player's last name last_name_layout.addWidget(self.lastNameLabel) last_name_layout.addWidget(self.player_last_name) add_player_layout.addLayout(last_name_layout) button_layout = QHBoxLayout() self.enroll_button = QPushButton('Enroll Player', self) self.enroll_button.clicked.connect(self.addPlayer) self.enroll_button.setAutoDefault(True) button_layout.addWidget(self.enroll_button) self.clear_button = QPushButton('Clear', self) self.clear_button.clicked.connect(self.clear) self.clear_button.setAutoDefault(True) button_layout.addWidget(self.clear_button) add_player_layout.addLayout(button_layout) add_player_layout.addStretch(1) begin_tournament_layout = QHBoxLayout() begin_tournament_button = QPushButton('Begin Tournament', self) begin_tournament_button.clicked.connect(self.beginTournament) begin_tournament_layout.addStretch(1) begin_tournament_layout.addWidget(begin_tournament_button) add_player_layout.addLayout(begin_tournament_layout) ### Table View For Enrolled Players Section ### self.enrolled_table = QTableWidget() # The following options all change how the user interacts # with the table self.enrolled_table.setEditTriggers(QAbstractItemView.NoEditTriggers) # These prevent the user from editing the table cells, self.enrolled_table.setSelectionBehavior(QAbstractItemView.SelectRows) # Select the whole row when one is clicked self.enrolled_table.setSelectionMode(QAbstractItemView.SingleSelection) # Prevents selection of multiple rows. self.enrolled_table.verticalHeader().hide() # and hide the headers that appear to the left of the rows. self.enrolled_table.setColumnCount(2) self.enrolled_table.setHorizontalHeaderItem(0, QTableWidgetItem('Player')) self.enrolled_table.setHorizontalHeaderItem(1, QTableWidgetItem('ID')) self.enrolled_table.setColumnWidth(0, 235) self.enrolled_table.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeMode(2)) # ResizeMode(2) is fixed size, meaning the user can't change the size # of the table window_layout.addWidget(self.enrolled_table) ### Misc Window Settings ### window_layout.addLayout(add_player_layout) self.setGeometry(200, 200, 700, 500) self.setWindowTitle(tournamentName) self.player_first_name.setFocus(True) self.show() def addPlayer(self): first_name = self.player_first_name.text() lastName = self.player_last_name.text() if first_name.strip() != '' and lastName.strip() != '': self.current_id_num += 1 tempID = 'TEMP{}'.format(str(self.current_id_num).zfill(4)) # Builds a temporary ID for the player. # Temp IDs are good for just getting a tournament up and running. self.enrolled_table.insertRow(self.enrolled_table.rowCount()) currentRow = self.enrolled_table.rowCount() - 1 self.enrolled_table.setItem(currentRow, 0, QTableWidgetItem('{}, {}'. format(lastName, first_name))) self.enrolled_table.setItem(currentRow, 1, QTableWidgetItem(tempID)) self.enrolled_table.setSortingEnabled(True) self.enrolled_table.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder) self.enrolled_table.setSortingEnabled(False) # This quickly enables sorting, sorts by the first column which is player # name, and then disables sorting so the user can't change the order. # This doesn't care about the order people were entered in, users just # need to be able to quickly see who is enrolled self.clear() # Calls clear to erase the entrant's name after they've been enrolled. self.player_first_name.setFocus() # Returns the cursor to the first name box to make entering # players faster! else: nameError = QMessageBox() nameError.setIcon(QMessageBox.Information) nameError.setWindowTitle('Blank Name') if first_name.strip() == '': self.player_first_name.setFocus() nameError.setText("The Player's first name is blank.") else: self.player_last_name.setFocus() nameError.setText("The Player's last name is blank.") nameError.exec_() def contextMenuEvent(self, event): selection = self.enrolled_table.selectedItems() if len(selection) > 0: table_menu = QMenu(self) dropPlayer = table_menu.addAction('Drop Player') renamePlayer = table_menu.addAction('Rename Player') action = table_menu.exec_(self.mapToGlobal(event.pos())) if action == dropPlayer: row = selection[0].row() self.enrolled_table.removeRow(row) elif action == renamePlayer: player_last_name, player_first_name = selection[0].text().split(', ') editNameWindow = QDialog(None, Qt.WindowCloseButtonHint) # Creates a dialog box that will pop up if the user choses rename player dialogLayout = QVBoxLayout() ### Dialog First Name Change ### dialogFirstNameLayout = QHBoxLayout() editFirstNameLabel = QLabel('New First Name:') editFirstName = QLineEdit() editFirstName.setText(player_first_name) dialogFirstNameLayout.addWidget(editFirstNameLabel) dialogFirstNameLayout.addWidget(editFirstName) dialogLayout.addLayout(dialogFirstNameLayout) ### Dialog Last Name Change ### dialogLastNameLayout = QHBoxLayout() editLastNameLabel = QLabel('New Last Name:') editLastName = QLineEdit() editLastName.setText(player_last_name) dialogLastNameLayout.addWidget(editLastNameLabel) dialogLastNameLayout.addWidget(editLastName) dialogLayout.addLayout(dialogLastNameLayout) ### Dialog Buttons ### dialogButtons = QHBoxLayout() editName = QPushButton('Edit Name') editName.clicked.connect(editNameWindow.accept) cancelName = QPushButton('Cancel') cancelName.clicked.connect(editNameWindow.reject) dialogButtons.addWidget(editName) dialogButtons.addWidget(cancelName) dialogLayout.addLayout(dialogButtons) ### Misc Window Settings ### editNameWindow.setWindowTitle('Change Player Name') editNameWindow.setGeometry(300, 300, 300, 100) editNameWindow.setLayout(dialogLayout) choice = editNameWindow.exec_() # Returns a number corresponding to the user's choice. # 1 = Accept (Hit the ok button) # 0 = Reject (Hit the cancel button) if choice == 1: selection[0].setText('{}, {}'.format(editLastName.text(), editFirstName.text())) self.enrolled_table.setSortingEnabled(True) self.enrolled_table.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder) self.enrolled_table.setSortingEnabled(False) # Resorts the players names after a name is changed self.enrolled_table.clearSelection() # Automatically clears the selection after it is right clicked # So the user doesn't accidently keep clicking the same cell def clear(self): # Helper function to clear the player's name after something is done, # such as when a player is entered self.player_first_name.setText('') self.player_last_name.setText('') self.player_first_name.setFocus(True) def getTableData(self): enrolled_players = [] for row in range(self.enrolled_table.rowCount()): playerNameItem = self.enrolled_table.item(row, 0) playerIDItem = self.enrolled_table.item(row, 1) # gets the items from the table player_last_name, player_first_name = playerNameItem.text().split(', ') # Split's first and last name, as they are combined in the cell # but saved separately in the xml document playerID = playerIDItem.text() enrolled_players.append((playerID, player_first_name, player_last_name)) # appends a tuple with the information to the enrolled_players list return enrolled_players # This is passed to writeToFile def loadTournament(self, tournament): # Helper function to load the event so it can be accessed by # the other methods tree = ET.parse(tournament) return tree # this is used by the __init__ to load the xml tree def writeToFile(self, enrolled_players): players = self.tournament[1] for idNum, first, last in enrolled_players: # Unpacks the tuples saved in enrolled_players # to be written to the xml file player = ET.SubElement(players, 'Player') idNumber = ET.SubElement(player, 'IDNumber') idNumber.text = idNum first_name = ET.SubElement(player, 'FirstName') first_name.text = first lastName = ET.SubElement(player, 'LastName') lastName.text = last wins = ET.SubElement(player, 'Wins') wins.text = '0' draws = ET.SubElement(player, 'Draws') draws.text = '0' # Wins and draws are both used during player pairings to_write = self.tree to_write.write(self.tournament, encoding = 'utf-8', xml_declaration = True) def beginTournament(self): enrolled_players = self.getTableData() total_players = len(enrolled_players) if len(enrolled_players) < 4: # Prevents the user from creating an event with less than 4 players lessThan4Error = QMessageBox() lessThan4Error.setIcon(QMessageBox.Information) lessThan4Error.setText('A tournament cannot have less than 4 players!') lessThan4Error.setWindowTitle('Not Enough Players') # Tournaments with less than 4 players just don't work. With 4 players # You can have 3 rounds and a definite winner. self.player_first_name.setFocus() lessThan4Error.exec_() else: tournament_created = QMessageBox() tournament_created.setIcon(QMessageBox.Information) tournament_created.setText('{} Players were enrolled.'.format(total_players)) tournament_created.setWindowTitle('Players Successfully Enrolled') # Alerts the user that players have been entered, and gives the total number # so the user can double check how many people have been entered. tournament_created.exec_() self.writeToFile(enrolled_players) if __name__ == '__main__': self.close()
class SerialScan(QMainWindow): def __init__(self, serial, parent=None): super(SerialScan, self).__init__(parent) self.serial = serial self.setWindowIcon(QIcon('resources/mouse.PNG')) self.IdToName = {} self.front() self.mouseThread = ScanMouseIdThread(self.serial) self.mouseThread.start() self.mouseThread.sig1.connect(self.on_info) def front(self): self.setGeometry(500, 500, 500, 500) self.setWindowTitle("TEAM") self.setWindowIcon(QIcon('resources/mouse.PNG')) self.setStyleSheet("background-color: #03795E") grid = QGridLayout() logo_title = QLabel("Serial Input: Scan IDs", self) font = QFont('Arial', 30) font.setBold(True) logo_title.setStyleSheet("QLabel {color: #fcba03}") logo_title.setFont(font) logo_title.setAlignment(Qt.AlignCenter) grid.addWidget(logo_title, 0, 1) logo_instr = QLabel( "Scan mouse tags, enter what cage they are for, and their name:", self) font = QFont('Arial', 15) font.setBold(True) logo_instr.setStyleSheet("QLabel {color: #fcba03}") logo_instr.setFont(font) logo_instr.setAlignment(Qt.AlignCenter) grid.addWidget(logo_instr, 1, 1) self.miceTable = QTableWidget() self.miceTable.setStyleSheet("QTableWidget {background: #ffffff}") self.miceTable.setRowCount(1) self.miceTable.setColumnCount(3) self.miceTable.setHorizontalHeaderLabels( ["Mouse Tag", "Cage", "Mouse Name"]) header = self.miceTable.horizontalHeader() header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) self.miceTable.move(0, 0) grid.addWidget(self.miceTable, 2, 1) self.back = QPushButton("Back", self) self.back.setStyleSheet( "QPushButton:hover:!pressed{ background: #fcba03}") self.back.clicked.connect(self.stopThread) grid.addWidget(self.back, 4, 0, 1, 1) self.start = QPushButton("Start", self) self.start.setStyleSheet( "QPushButton:hover:!pressed{ background: #fcba03}") self.start.clicked.connect(self.stopThread) grid.addWidget(self.start, 4, 2, 1, 1) central = QWidget() central.setLayout(grid) self.setCentralWidget(central) def on_info(self, info): rowPos = self.miceTable.rowCount() - 1 item = QTableWidgetItem(info) self.miceTable.setItem(rowPos, 0, item) self.miceTable.insertRow(rowPos + 1) def stopThread(self): try: self.mouseThread.running = False time.sleep(3) self.miceTable.clearSelection() for row in range(self.miceTable.rowCount()): if self.miceTable.item(row, 0) and self.miceTable.item( row, 1) and self.miceTable.item(row, 2): mouseId = self.miceTable.item(row, 0).text().strip() cage = int(self.miceTable.item(row, 1).text().strip()) name = self.miceTable.item(row, 2).text().strip() self.IdToName[mouseId] = [cage, name] self.mouseThread.serial_port.close() self.hide() except: traceback.print_exc()
class flavorDlg(ArtisanResizeablDialog): def __init__(self, parent=None, aw=None): super(flavorDlg, self).__init__(parent, aw) self.setModal(True) rcParams['path.effects'] = [] #avoid question mark context help flags = self.windowFlags() helpFlag = Qt.WindowContextHelpButtonHint flags = flags & (~helpFlag) self.setWindowFlags(flags) self.setWindowTitle( QApplication.translate("Form Caption", "Cup Profile", None)) settings = QSettings() if settings.contains("FlavorProperties"): self.restoreGeometry(settings.value("FlavorProperties")) defaultlabel = QLabel(QApplication.translate("Label", "Default", None)) self.defaultcombobox = QComboBox() self.defaultcombobox.addItems([ "", "Artisan", "SCCA", "CQI", "SweetMarias", "C", "E", "CoffeeGeek", "Intelligentsia", "IIAC", "WCRC", "*CUSTOM*" ]) self.defaultcombobox.setCurrentIndex(0) self.lastcomboboxIndex = 0 self.defaultcombobox.currentIndexChanged.connect(self.setdefault) self.flavortable = QTableWidget() self.flavortable.setTabKeyNavigation(True) self.createFlavorTable() leftButton = QPushButton("<") leftButton.setFocusPolicy(Qt.NoFocus) leftButton.clicked.connect(self.moveLeft) rightButton = QPushButton(">") rightButton.setFocusPolicy(Qt.NoFocus) rightButton.clicked.connect(self.moveRight) addButton = QPushButton(QApplication.translate("Button", "Add", None)) addButton.setFocusPolicy(Qt.NoFocus) addButton.clicked.connect(self.addlabel) delButton = QPushButton(QApplication.translate("Button", "Del", None)) delButton.setFocusPolicy(Qt.NoFocus) delButton.clicked.connect(self.poplabel) saveImgButton = QPushButton( QApplication.translate("Button", "Save Image", None)) saveImgButton.setFocusPolicy(Qt.NoFocus) #saveImgButton.clicked.connect(self.aw.resizeImg_0_1) # save as PNG (raster) saveImgButton.clicked.connect( self.aw.saveVectorGraph_PDF) # save as PDF (vector) # connect the ArtisanDialog standard OK/Cancel buttons self.dialogbuttons.accepted.connect(self.close) self.dialogbuttons.removeButton( self.dialogbuttons.button(QDialogButtonBox.Cancel)) self.backgroundCheck = QCheckBox( QApplication.translate("CheckBox", "Background", None)) if self.aw.qmc.flavorbackgroundflag: self.backgroundCheck.setChecked(True) self.backgroundCheck.clicked.connect(self.showbackground) aspectlabel = QLabel( QApplication.translate("Label", "Aspect Ratio", None)) self.aspectSpinBox = QDoubleSpinBox() self.aspectSpinBox.setToolTip( QApplication.translate("Tooltip", "Aspect Ratio", None)) self.aspectSpinBox.setRange(0., 2.) self.aspectSpinBox.setSingleStep(.1) self.aspectSpinBox.setValue(self.aw.qmc.flavoraspect) self.aspectSpinBox.valueChanged.connect(self.setaspect) flavorLayout = QHBoxLayout() flavorLayout.addWidget(self.flavortable) comboLayout = QHBoxLayout() comboLayout.addWidget(defaultlabel) comboLayout.addWidget(self.defaultcombobox) comboLayout.addStretch() aspectLayout = QHBoxLayout() aspectLayout.addWidget(self.backgroundCheck) aspectLayout.addWidget(aspectlabel) aspectLayout.addWidget(self.aspectSpinBox) aspectLayout.addStretch() blayout1 = QHBoxLayout() blayout1.addStretch() blayout1.addWidget(addButton) blayout1.addWidget(delButton) blayout1.addStretch() extralayout = QVBoxLayout() extralayout.addLayout(comboLayout) extralayout.addLayout(aspectLayout) extraGroupLayout = QGroupBox() extraGroupLayout.setLayout(extralayout) blayout = QHBoxLayout() blayout.addStretch() blayout.addWidget(leftButton) blayout.addWidget(rightButton) blayout.addStretch() mainButtonsLayout = QHBoxLayout() mainButtonsLayout.addWidget(saveImgButton) mainButtonsLayout.addStretch() mainButtonsLayout.addWidget(self.dialogbuttons) mainLayout = QVBoxLayout() mainLayout.addLayout(flavorLayout) mainLayout.addLayout(blayout1) mainLayout.addWidget(extraGroupLayout) mainLayout.addLayout(blayout) # mainLayout.addStretch() mainLayout.addLayout(mainButtonsLayout) self.setLayout(mainLayout) self.aw.qmc.flavorchart() self.dialogbuttons.button(QDialogButtonBox.Ok).setFocus() @pyqtSlot(float) def setaspect(self, _): self.aw.qmc.flavoraspect = self.aspectSpinBox.value() self.aw.qmc.flavorchart() def createFlavorTable(self): nflavors = len(self.aw.qmc.flavorlabels) # self.flavortable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.flavortable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.flavortable.clearSelection( ) # this seems to work also for Ubuntu 16.04 if nflavors: self.flavortable.setRowCount(nflavors) self.flavortable.setColumnCount(3) self.flavortable.setHorizontalHeaderLabels([ QApplication.translate("Table", "Label", None), QApplication.translate("Table", "Value", None), "" ]) self.flavortable.setAlternatingRowColors(True) self.flavortable.setEditTriggers(QTableWidget.NoEditTriggers) self.flavortable.setSelectionBehavior(QTableWidget.SelectRows) self.flavortable.setSelectionMode(QTableWidget.SingleSelection) self.flavortable.setShowGrid(True) #self.flavortable.verticalHeader().setSectionResizeMode(2) #populate table for i in range(nflavors): labeledit = QLineEdit(self.aw.qmc.flavorlabels[i]) labeledit.textChanged.connect(self.setlabel) valueSpinBox = MyQDoubleSpinBox() valueSpinBox.setRange(0., 10.) valueSpinBox.setSingleStep(.25) valueSpinBox.setAlignment(Qt.AlignRight) val = self.aw.qmc.flavors[i] if self.aw.qmc.flavors[0] < 1. and self.aw.qmc.flavors[ -1] < 1.: # < 0.5.0 version style compatibility val *= 10. valueSpinBox.setValue(val) valueSpinBox.valueChanged.connect(self.setvalue) #add widgets to the table self.flavortable.setCellWidget(i, 0, labeledit) self.flavortable.setCellWidget(i, 1, valueSpinBox) self.flavortable.resizeColumnsToContents() header = self.flavortable.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Stretch) @pyqtSlot(bool) def showbackground(self, _): if self.backgroundCheck.isChecked(): if not self.aw.qmc.background: message = QApplication.translate( "Message", "Background profile not found", None) self.aw.sendmessage(message) self.backgroundCheck.setChecked(False) else: if len(self.aw.qmc.backgroundFlavors) != len( self.aw.qmc.flavors): message = QApplication.translate( "Message", "Background does not match number of labels", None) self.aw.sendmessage(message) self.aw.qmc.flavorbackgroundflag = False self.backgroundCheck.setChecked(False) else: self.aw.qmc.flavorbackgroundflag = True self.aw.qmc.flavorchart() else: self.aw.qmc.flavorbackgroundflag = False self.aw.qmc.flavorchart() @pyqtSlot(bool) def moveLeft(self, _): self.aw.qmc.flavorstartangle += 5 self.aw.qmc.flavorchart() @pyqtSlot(bool) def moveRight(self, _): self.aw.qmc.flavorstartangle -= 5 self.aw.qmc.flavorchart() def savetable(self): for i in range(len(self.aw.qmc.flavorlabels)): labeledit = self.flavortable.cellWidget(i, 0) valueSpinBox = self.flavortable.cellWidget(i, 1) label = labeledit.text() if "\\n" in label: #make multiple line text if "\n" found in label string parts = label.split("\\n") label = chr(10).join(parts) self.aw.qmc.flavorlabels[i] = label self.aw.qmc.flavors[i] = valueSpinBox.value() if self.lastcomboboxIndex == 10: # store the current labels as *CUSTOM* self.aw.qmc.customflavorlabels = self.aw.qmc.flavorlabels @pyqtSlot() @pyqtSlot("QString") def setlabel(self, _): x = self.aw.findWidgetsRow(self.flavortable, self.sender(), 0) if x is not None: labeledit = self.flavortable.cellWidget(x, 0) self.aw.qmc.flavorlabels[x] = labeledit.text() self.aw.qmc.updateFlavorchartLabel(x) # fast incremental redraw @pyqtSlot(float) def setvalue(self, _): x = self.aw.findWidgetsRow(self.flavortable, self.sender(), 1) if x is not None: valueSpinBox = self.flavortable.cellWidget(x, 1) self.aw.qmc.flavors[x] = valueSpinBox.value() # self.aw.qmc.flavorchart() # slow full redraw self.aw.qmc.updateFlavorchartValues() # fast incremental redraw @pyqtSlot(int) def setdefault(self, _): if self.lastcomboboxIndex == 10: # store the current labels as *CUSTOM* self.aw.qmc.customflavorlabels = self.aw.qmc.flavorlabels dindex = self.defaultcombobox.currentIndex() #["","Artisan","SCCA","CQI","SweetMarias","C","E","coffeegeek","Intelligentsia","WCRC"] if dindex > 0 or dindex < 11: self.aw.qmc.flavorstartangle = 90 if dindex == 1: self.aw.qmc.flavorlabels = list( self.aw.qmc.artisanflavordefaultlabels) elif dindex == 2: self.aw.qmc.flavorlabels = list( self.aw.qmc.SCCAflavordefaultlabels) elif dindex == 3: self.aw.qmc.flavorlabels = list(self.aw.qmc.CQIflavordefaultlabels) elif dindex == 4: self.aw.qmc.flavorlabels = list( self.aw.qmc.SweetMariasflavordefaultlabels) elif dindex == 5: self.aw.qmc.flavorlabels = list(self.aw.qmc.Cflavordefaultlabels) elif dindex == 6: self.aw.qmc.flavorlabels = list(self.aw.qmc.Eflavordefaultlabels) elif dindex == 7: self.aw.qmc.flavorlabels = list( self.aw.qmc.coffeegeekflavordefaultlabels) elif dindex == 8: self.aw.qmc.flavorlabels = list( self.aw.qmc.Intelligentsiaflavordefaultlabels) elif dindex == 9: self.aw.qmc.flavorlabels = list( self.aw.qmc.IstitutoInternazionaleAssaggiatoriCaffe) elif dindex == 10: self.aw.qmc.flavorlabels = list( self.aw.qmc.WorldCoffeeRoastingChampionship) elif dindex == 11: self.aw.qmc.flavorlabels = list(self.aw.qmc.customflavorlabels) else: return self.aw.qmc.flavors = [5.] * len(self.aw.qmc.flavorlabels) self.createFlavorTable() self.aw.qmc.flavorchart() self.lastcomboboxIndex = dindex @pyqtSlot(bool) def addlabel(self, _): self.aw.qmc.flavorlabels.append("???") self.aw.qmc.flavors.append(5.) self.createFlavorTable() self.aw.qmc.flavorchart() @pyqtSlot(bool) def poplabel(self): fn = len(self.aw.qmc.flavors) self.aw.qmc.flavors = self.aw.qmc.flavors[:(fn - 1)] self.aw.qmc.flavorlabels = self.aw.qmc.flavorlabels[:(fn - 1)] self.createFlavorTable() self.aw.qmc.flavorchart() def closeEvent(self, _): self.close() @pyqtSlot() def close(self): settings = QSettings() #save window geometry settings.setValue("FlavorProperties", self.saveGeometry()) self.savetable() self.aw.qmc.fileDirty() if self.aw.qmc.ax1 is not None: try: self.aw.qmc.fig.delaxes(self.aw.qmc.ax1) except: pass self.aw.qmc.fig.clf() self.aw.qmc.clearFlavorChart() self.aw.redrawOnResize = True self.aw.qmc.redraw(recomputeAllDeltas=False) self.aw.showControls() self.accept()
class SettingsWidget(QWidget): def __init__(self, parent=None): super(SettingsWidget, self).__init__(parent) # 软件设置面板 path_settings_groupbox = QGroupBox() path_settings_groupbox.setTitle("本地设置") path_settings_groupbox.setFixedHeight(200) path_settings_groupbox_layout = QVBoxLayout() # 本地客户端路径 path_widget1_layout = QHBoxLayout() local_client_path_label = QLabel() local_client_path_label.setText("本地客户端路径:") # local_client_path_label.setFixedWidth(105) self.local_client_path_lineEdit = QLineEdit() self.local_client_path_btn = QPushButton() self.local_client_path_btn.setText("浏览") path_widget1_layout.addWidget(local_client_path_label) path_widget1_layout.addWidget(self.local_client_path_lineEdit) path_widget1_layout.addWidget(self.local_client_path_btn) # 本地服务端路径 path_widget2_layout = QHBoxLayout() local_server_path_label = QLabel() local_server_path_label.setText("本地服务端路径:") # local_server_path_label.setFixedWidth(105) self.local_server_path_lineEdit = QLineEdit() self.local_server_path_lineEdit.setDisabled(True) self.local_server_path_btn = QPushButton() self.local_server_path_btn.setText("浏览") server_betacode_label = QLabel() server_betacode_label.setText("测试代码:") self.server_betacode_lineEdit = QLineEdit() self.server_betacode_lineEdit.setFixedWidth(80) path_widget2_layout.addWidget(local_server_path_label) path_widget2_layout.addWidget(self.local_server_path_lineEdit) path_widget2_layout.addWidget(self.local_server_path_btn) path_widget2_layout.addWidget(server_betacode_label) path_widget2_layout.addWidget(self.server_betacode_lineEdit) # Steamcmd路径 path_widget4_layout = QHBoxLayout() steamcmd_path_label = QLabel() steamcmd_path_label.setText("SteamCMD路径:") # local_cluster_path_label.setFixedWidth(105) self.steamcmd_path_lineEdit = QLineEdit() self.steamcmd_path_btn = QPushButton() self.steamcmd_path_btn.setText("浏览") self.install_server_button = QPushButton() self.install_server_button.setText("安装本地服务端") self.install_server_button.clicked.connect(self.install_server) path_widget4_layout.addWidget(steamcmd_path_label) path_widget4_layout.addWidget(self.steamcmd_path_lineEdit) path_widget4_layout.addWidget(self.steamcmd_path_btn) path_widget4_layout.addWidget(self.install_server_button) # 本地存档路径 path_widget3_layout = QHBoxLayout() local_cluster_path_label = QLabel() local_cluster_path_label.setText("本地存档路径:") # local_cluster_path_label.setFixedWidth(105) self.local_cluster_path_lineEdit = QLineEdit() self.local_cluster_path_lineEdit.setDisabled(True) self.local_cluster_path_btn = QPushButton() # self.local_cluster_path_btn.setText("浏览") self.local_cluster_path_btn.setText("打开") path_widget3_layout.addWidget(local_cluster_path_label) path_widget3_layout.addWidget(self.local_cluster_path_lineEdit) path_widget3_layout.addWidget(self.local_cluster_path_btn) path_settings_groupbox_layout.addLayout(path_widget1_layout) path_settings_groupbox_layout.addLayout(path_widget2_layout) path_settings_groupbox_layout.addLayout(path_widget4_layout) path_settings_groupbox_layout.addLayout(path_widget3_layout) path_settings_groupbox.setLayout(path_settings_groupbox_layout) sc_settings_groupbox = QGroupBox() sc_settings_groupbox.setFixedHeight(75) sc_settings_groupbox.setTitle("消息通知设置") sc_settings_groupbox_layout = QHBoxLayout() self.sc_enable_checkBox = QCheckBox() self.sc_enable_checkBox.setText("开启Server酱消息通知") sc_key_label = QLabel() sc_key_label.setText("Server酱密钥:") self.sc_key_lineEdit = QLineEdit() sc_settings_groupbox_layout.addWidget(self.sc_enable_checkBox) sc_settings_groupbox_layout.addWidget(sc_key_label) sc_settings_groupbox_layout.addWidget(self.sc_key_lineEdit) sc_settings_groupbox.setLayout(sc_settings_groupbox_layout) server_settings_groupbox = QGroupBox() server_settings_groupbox.setTitle("云主机设置(截图请注意隐藏IP和密码)") server_settings_groupbox_layout = QVBoxLayout() token_layout = QHBoxLayout() server_token_label = QLabel() server_token_label.setText("服务器令牌:") self.server_token_lineEdit = QLineEdit() token_layout.addWidget(server_token_label) token_layout.addWidget(self.server_token_lineEdit) server_frame_layout = QHBoxLayout() self.server_table = QTableWidget() self.server_table.setColumnCount(5) # 禁止编辑 self.server_table.setEditTriggers(QAbstractItemView.NoEditTriggers) # 设置标题 self.server_table.setHorizontalHeaderLabels( ['名称', 'IP或域名', '用户名', '密码', '备注']) # 设置选择整行 self.server_table.setSelectionBehavior(QAbstractItemView.SelectRows) # 只选中单行 self.server_table.setSelectionMode(QAbstractItemView.SingleSelection) # 自动列宽,随内容 self.server_table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.server_table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) server_edit_btn_layout = QVBoxLayout() self.add_new_server_btn = QPushButton() self.add_new_server_btn.setText("添加新的") self.edit_server_btn = QPushButton() self.edit_server_btn.setText("修改选中") self.delete_server_btn = QPushButton() self.delete_server_btn.setText("删除选中") self.test_server_btn = QPushButton() self.test_server_btn.setText("连接测试") server_edit_btn_layout.addWidget(self.add_new_server_btn) server_edit_btn_layout.addWidget(self.edit_server_btn) server_edit_btn_layout.addWidget(self.delete_server_btn) server_edit_btn_layout.addWidget(self.test_server_btn) spacerItem = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) server_edit_btn_layout.addItem(spacerItem) server_frame_layout.addWidget(self.server_table) server_frame_layout.addLayout(server_edit_btn_layout) server_settings_groupbox_layout.addLayout(token_layout) server_settings_groupbox_layout.addLayout(server_frame_layout) server_settings_groupbox.setLayout(server_settings_groupbox_layout) self.save_settings_btn = QPushButton() self.save_settings_btn.setText("保存设置(修改完记得保存)") settings_layout = QVBoxLayout() settings_layout.addWidget(path_settings_groupbox) settings_layout.addWidget(sc_settings_groupbox) settings_layout.addWidget(server_settings_groupbox) settings_layout.addWidget(self.save_settings_btn) self.setLayout(settings_layout) # 按钮函数绑定 self.add_new_server_btn.clicked.connect(self.add_new_server) self.delete_server_btn.clicked.connect(self.delete_server) self.edit_server_btn.clicked.connect(self.edit_server) self.test_server_btn.clicked.connect(self.test_server) self.save_settings_btn.clicked.connect(self.save_settings_data) self.local_server_path_btn.clicked.connect(self.select_server_dir) # self.local_cluster_path_btn.clicked.connect(self.select_cluster_dir) self.local_cluster_path_btn.clicked.connect(self.open_cluster) self.local_client_path_btn.clicked.connect(self.select_client_dir) self.steamcmd_path_btn.clicked.connect(self.select_steamcmd_dir) self.install_server_button.clicked.connect(self.install_server) self.init_settings_data() # 添加新服务器 def add_new_server(self): self.server_table.clearSelection() self.serverDialog = ServerDialog(self) self.serverDialog.setWindowTitle("添加服务器") self.serverDialog.serverSignal.connect(self.add_server) self.serverDialog.exec() def is_server_not_exist(self, ip): serverlist = self.get_server_list() for server in serverlist: if ip == server[1]: return False return True def add_server(self, server): flag = True server_row_num = self.server_table.currentRow() if server_row_num == -1: if self.is_server_not_exist(server[1]): server_row_num = self.server_table.rowCount() self.server_table.setRowCount(server_row_num + 1) else: QMessageBox.warning(self, "警告", "服务器已存在!", QMessageBox.Yes) flag = False if flag: self.serverDialog.hide() for col in range(5): self.server_table.setItem(server_row_num, col, QTableWidgetItem(server[col])) self.server_table.item(server_row_num, col).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) def edit_server(self): row = self.server_table.currentRow() if row > -1: serverDialog = ServerDialog(self) serverDialog.setWindowTitle("修改服务器") serverDialog.name_lineEdit.setText( self.server_table.item(row, 0).text()) serverDialog.ip_lineEdit.setText( self.server_table.item(row, 1).text()) serverDialog.user_lineEdit.setText( self.server_table.item(row, 2).text()) serverDialog.passwd_lineEdit.setText( self.server_table.item(row, 3).text()) serverDialog.tips_lineEdit.setText( self.server_table.item(row, 4).text()) serverDialog.serverSignal.connect(self.add_server) serverDialog.exec() else: QMessageBox.warning(self, "警告", "你没有选中服务器!", QMessageBox.Yes) def delete_server(self): row = self.server_table.currentRow() if row > -1: self.server_table.removeRow(row) else: QMessageBox.warning(self, "警告", "你没有选中服务器!", QMessageBox.Yes) def test_server(self): row = self.server_table.currentRow() if row > -1: ip = self.server_table.item(row, 1).text() if ip == "127.0.0.1": QMessageBox.information(self, "无需测试", "本地服请确保本地已安装服务端且已配好路径!", QMessageBox.Yes) else: username = self.server_table.item(row, 2).text() passwd = self.server_table.item(row, 3).text() try: # 创建一个SSH客户端对象 ssh = paramiko.SSHClient() # 设置访问策略 ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 与远程主机进行连接 ssh.connect(hostname=ip, port=22, username=username, password=passwd) QMessageBox.information(self, "连接正确", "服务器连接测试通过!", QMessageBox.Yes) except Exception as e: QMessageBox.critical(self, "连接错误", str(e), QMessageBox.Yes) finally: ssh.close() else: QMessageBox.warning(self, "警告", "你没有选中服务器!", QMessageBox.Yes) def read_json_data(self, filename): jsonfile = os.path.join(ROOT_DIR, "settings.json") if os.path.exists(jsonfile): with open(jsonfile, 'r', encoding="utf-8") as f: data = json.load(f) else: data = {} return data def write_json_data(self, data): jsonfile = os.path.join(ROOT_DIR, 'settings.json') with open(jsonfile, 'w', encoding="utf-8") as f: json.dump(data, f) # 初始化设置 def init_settings_data(self): settings = self.read_json_data('settings.json') if settings: self.steamcmd_path_lineEdit.setText(settings['steamcmdpath']) self.local_client_path_lineEdit.setText( settings['localclientpath']) self.local_server_path_lineEdit.setText( settings['localserverpath']) self.local_cluster_path_lineEdit.setText( settings['localclusterpath']) self.sc_key_lineEdit.setText(settings['sckey']) self.sc_enable_checkBox.setChecked(settings['scenable']) self.server_token_lineEdit.setText(settings['servertoken']) self.set_server_list(settings['servers']) else: self.local_cluster_path_lineEdit.setText(CLUSTER_DIR) def get_server_list(self): slist = [] rowCount = self.server_table.rowCount() for row in range(rowCount): slist.append([ self.server_table.item(row, 0).text(), self.server_table.item(row, 1).text(), self.server_table.item(row, 2).text(), self.server_table.item(row, 3).text(), self.server_table.item(row, 4).text() ]) return slist def set_server_list(self, serverlist): row = self.server_table.rowCount() flag = False for list in serverlist: if list[1] == "127.0.0.1": flag = True self.server_table.setRowCount(row + 1) self.server_table.setItem(row, 0, QTableWidgetItem(list[0])) self.server_table.setItem(row, 1, QTableWidgetItem(list[1])) self.server_table.setItem(row, 2, QTableWidgetItem(list[2])) self.server_table.setItem(row, 3, QTableWidgetItem(list[3])) self.server_table.setItem(row, 4, QTableWidgetItem(list[4])) self.server_table.item(row, 0).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.server_table.item(row, 1).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.server_table.item(row, 2).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.server_table.item(row, 3).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.server_table.item(row, 4).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) row += 1 if not flag: self.set_server_list( [["本地服务器", "127.0.0.1", "如无必要", "请勿删除", "这个选项"]]) if 0 == len(serverlist): self.set_server_list( [["本地服务器", "127.0.0.1", "如无必要", "请勿删除", "这个选项"]]) # 保存设置 def save_settings_data(self): settings = {} settings['steamcmdpath'] = self.steamcmd_path_lineEdit.text() settings['localclientpath'] = self.local_client_path_lineEdit.text() settings['localserverpath'] = self.local_server_path_lineEdit.text() settings['localclusterpath'] = self.local_cluster_path_lineEdit.text() settings['sckey'] = self.sc_key_lineEdit.text() settings['scenable'] = self.sc_enable_checkBox.isChecked() settings['servertoken'] = self.server_token_lineEdit.text() settings['servers'] = self.get_server_list() self.write_json_data(settings) def getServerPath(self): return self.local_server_path_lineEdit.text() def select_client_dir(self): client_dir = self.local_client_path_lineEdit.text() root_dir = USER_HOME if os.path.exists(client_dir): root_dir = client_dir if sys.platform == "darwin": fileName, _ = QFileDialog.getOpenFileName( self, "选择本地客户端路径", root_dir, "Mac Applications (*.app);;All Files (*)") if fileName: client_dir = fileName else: client_dir = QFileDialog.getExistingDirectory( self, "选择本地服务端路径", root_dir) if sys.platform == "win32": client_dir = client_dir.replace('/', '\\') # self.local_server_path_lineEdit.setText(str(server_dir)) # client_dir = QFileDialog.getExistingDirectory(self, "选择本地客户端路径", USER_HOME) if client_dir != "": self.local_client_path_lineEdit.setText(str(client_dir)) def select_cluster_dir(self): cluster_dir = QFileDialog.getExistingDirectory(self, "选择本地存档路径", USER_HOME) # 官方存档路径 cluster = os.path.join(USER_HOME, 'Documents', 'Klei', 'DoNotStarveTogether') if cluster_dir != cluster: self.local_cluster_path_lineEdit.setText(str(cluster_dir)) else: QMessageBox.warning(self, "警告", "不可选择和官方相同的路径", QMessageBox.Yes) def open_cluster(self): cluster_path = self.local_cluster_path_lineEdit.text() if cluster_path == "": cluster_path = CLUSTER_DIR self.local_cluster_path_lineEdit.setText(CLUSTER_DIR) self.save_settings_data() if os.path.exists(cluster_path): if sys.platform == "darwin": os.system("open %s" % cluster_path) elif sys.platform == "win32": os.system("start explorer %s" % cluster_path) else: QMessageBox.warning(self, "警告", "当前系统不支持该操作", QMessageBox.Yes) else: QMessageBox.warning(self, "警告", "存档文件夹不存在", QMessageBox.Yes) def select_server_dir(self): server_dir = self.local_server_path_lineEdit.text() root_dir = USER_HOME if os.path.exists(server_dir): root_dir = server_dir if sys.platform == "darwin": fileName, _ = QFileDialog.getOpenFileName( self, "选择本地服务端路径", root_dir, "All Files (*);;Mac Applications (*.app)") if fileName: server_dir = fileName else: server_dir = QFileDialog.getExistingDirectory( self, "选择本地服务端路径", root_dir) if sys.platform == "win32": server_dir = server_dir.replace('/', '\\') if server_dir != "": self.local_server_path_lineEdit.setText(str(server_dir)) def select_steamcmd_dir(self): steamcmd_dir = self.steamcmd_path_lineEdit.text() root_dir = USER_HOME if os.path.exists(steamcmd_dir): root_dir = steamcmd_dir if sys.platform == "darwin": fileName, _ = QFileDialog.getOpenFileName( self, "选择SteamCMD路径", root_dir, "All Files (*);;Mac Applications (*.app)") if fileName: steamcmd_dir = fileName else: steamcmd_dir = QFileDialog.getOpenFileName( self, "选择SteamCMD路径", root_dir, "SteamCMD(steamcmd.exe)") if sys.platform == "win32": steamcmd_dir = steamcmd_dir[0].replace('/', '\\') if steamcmd_dir != "": self.steamcmd_path_lineEdit.setText(str(steamcmd_dir)) local_server_path = os.path.join( os.path.dirname(steamcmd_dir), "steamapps", "common", "Don't Starve Together Dedicated Server") if self.local_server_path_lineEdit.text() == "": self.local_server_path_lineEdit.setText(local_server_path) def install_server(self): if sys.platform == "win32": steamcmd = self.steamcmd_path_lineEdit.text() if steamcmd == "": QMessageBox.warning(self, "警告", "请先设置SteamCMD路径!", QMessageBox.Yes) return server_dir = self.local_server_path_lineEdit.text() if server_dir == "": QMessageBox.warning(self, "警告", "请先设置服务端安装路径!", QMessageBox.Yes) return
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 table' self.left = 0 self.top = 0 self.width = 2700 self.height = 2500 self.positionY = -1 self.positionX = -1 self.type = -1 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setStyleSheet('background-color: white') self.setGeometry(self.left, self.top, self.width, self.height) # mode = input('Auto mode or Manual mode (1-Auto, 2-Manual): ') self.mode = '1' if self.mode == '1': self.auto_input_alg = input( 'alpha-beta should be activated or not? (1-YES, 2-NO just Mini Max): ' ) # auto_input_file = input('should generate a trace of the minimax / alpha-beta or not? (1-YES, 2-NO): ') auto_input_sequence = input( 'Computer Player: play first or second? (1-First, 2-Second): ') # auto mode if auto_input_sequence == '2': self.curPlayer = "Human Player" else: self.curPlayer = "Computer Player" auto_input_choice = input( self.curPlayer + ': play color or dot? (1-Color, 2-Dot): ') if auto_input_choice == '1': self.choice = Choice.COLOR elif auto_input_choice == '2': self.choice = Choice.DOT # if auto_input_choice == '1' and auto_input_sequence == '1': # self.choice = Choice.COLOR # elif auto_input_choice == '2' and auto_input_sequence == '2': # self.choice = Choice.DOT # elif auto_input_choice == '2' and auto_input_sequence == '1': # self.choice = Choice.DOT # elif auto_input_choice == '2' and auto_input_sequence == '2': # self.choice = Choice.COLOR else: manual_input = input( 'Player 1: play color or dot? (1-Color, 2-Dot): ') # manual mode self.curPlayer = "Player 1" if manual_input == '1': self.choice = Choice.COLOR else: self.choice = Choice.DOT font = QtGui.QFont() font.setPixelSize(50) font.setBold(True) smallfont = QtGui.QFont() smallfont.setPixelSize(40) self.textLable = QLabel() self.countLable = QLabel() self.invalidLable = QLabel() self.playerLable = QLabel() self.textLable.setFont(font) self.countLable.setFont(font) self.invalidLable.setFont(font) self.playerLable.setFont(smallfont) self.phase = 'Normal Phase' self.textLable.setText(self.phase) self.playerLable.setText(self.curPlayer + '(' + str(self.choice) + ')') self.game = Game(self.choice) self.createTable() self.createButton() self.editor = QtWidgets.QTextEdit() self.editor.setText("hello") # Add box layout, add table to box layout and add box layout to widget self.tableLayout = QHBoxLayout() self.tableLayout.addWidget(self.tableWidget) self.lableLayout = QVBoxLayout() self.lableLayout.addStretch(0.5) self.lableLayout.addWidget(self.textLable) self.lableLayout.addStretch(0.7) self.lableLayout.addWidget(self.playerLable) self.lableLayout.addStretch(0.7) self.lableLayout.addWidget(self.countLable) self.lableLayout.addStretch(0.7) self.lableLayout.addWidget(self.invalidLable) self.lableLayout.addStretch(0.5) self.lableLayout.addWidget(self.editor) self.lableLayout.addStretch(0.6) self.lableLayout.addWidget(self.editor_button) self.buttonSide1Layout = QVBoxLayout() self.buttonSide1Layout.addWidget(self.button1) self.buttonSide1Layout.addWidget(self.button2) self.buttonSide1Layout.addWidget(self.button3) self.buttonSide1Layout.addWidget(self.button4) self.buttonSide2Layout = QVBoxLayout() self.buttonSide2Layout.addWidget(self.button5) self.buttonSide2Layout.addWidget(self.button6) self.buttonSide2Layout.addWidget(self.button7) self.buttonSide2Layout.addWidget(self.button8) self.AnglebuttonSideLayout = QVBoxLayout() self.AnglebuttonSideLayout.addWidget(self.RecycleButton) self.AnglebuttonSideLayout.addWidget(self.computerPlayerButton) self.Operationlayout = QHBoxLayout() self.Operationlayout.addStretch(1) self.Operationlayout.addLayout(self.lableLayout) self.Operationlayout.addStretch(1) self.Operationlayout.addLayout(self.buttonSide1Layout) self.Operationlayout.addStretch(1) self.Operationlayout.addLayout(self.buttonSide2Layout) self.Operationlayout.addStretch(1) self.Operationlayout.addLayout(self.AnglebuttonSideLayout) self.layout = QHBoxLayout() self.layout.addLayout(self.tableLayout) self.layout.addLayout(self.Operationlayout) self.setLayout(self.layout) # Show widget self.show() def createTable(self): # Create table self.tableWidget = QTableWidget() self.tableWidget.setRowCount(12) self.tableWidget.setColumnCount(8) self.tableWidget.setHorizontalHeaderLabels( ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H')) self.tableWidget.setVerticalHeaderLabels( ('12', '11', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1')) self.tableWidget.horizontalHeader().setDefaultSectionSize(130) self.tableWidget.verticalHeader().setDefaultSectionSize(130) for i in range(self.tableWidget.columnCount()): for j in range(self.tableWidget.rowCount()): self.tableWidget.setItem(j, i, QTableWidgetItem("")) self.tableWidget.move(0, 0) # table selection change self.tableWidget.clicked.connect(self.table_on_click) @pyqtSlot() def table_on_click(self): for currentQTableWidgetItem in self.tableWidget.selectedItems(): self.positionX = currentQTableWidgetItem.column() self.positionY = currentQTableWidgetItem.row() print(12 - self.positionY, str(chr(65 + self.positionX))) if self.tableWidget.currentItem().text() != "": # call method return the other segment card = self.game.get_card(self.positionX, 11 - self.positionY) self.recylePosition1 = [ card.seg[0].x, card.seg[0].y, card.seg[1].x, card.seg[1].y ] print(card.seg[0].x, card.seg[0].y, card.seg[1].x, card.seg[1].y) self.tableWidget.setRangeSelected( # select the whole card QTableWidgetSelectionRange(11 - card.seg[0].y, card.seg[0].x, 11 - card.seg[1].y, card.seg[1].x), True) def createButton(self): self.button1 = QPushButton('', self) self.button2 = QPushButton('', self) self.button3 = QPushButton('', self) self.button4 = QPushButton('', self) self.button5 = QPushButton('', self) self.button6 = QPushButton('', self) self.button7 = QPushButton('', self) self.button8 = QPushButton('', self) self.createButtonHelper(self.button1, 'Type1.png', 300, 200, 1) self.createButtonHelper(self.button2, 'Type2.png', 200, 200, 2) self.createButtonHelper(self.button3, 'Type3.png', 300, 200, 3) self.createButtonHelper(self.button4, 'Type4.png', 200, 200, 4) self.createButtonHelper(self.button5, 'Type5.png', 300, 200, 5) self.createButtonHelper(self.button6, 'Type6.png', 200, 200, 6) self.createButtonHelper(self.button7, 'Type7.png', 300, 200, 7) self.createButtonHelper(self.button8, 'Type8.png', 200, 200, 8) self.editor_button = QPushButton('play', self) self.editor_button.clicked.connect(self.Editor_button_on_click) self.editor_button.setStyleSheet('background: transparent') self.editor_button.setIcon(QtGui.QIcon('Play_image.png')) self.editor_button.setIconSize(QtCore.QSize(100, 100)) self.RecycleButton = QPushButton('', self) self.RecycleButton.isFlat() self.RecycleButton.setStyleSheet('background: transparent') self.RecycleButton.setIcon(QtGui.QIcon('Recyle.jpg')) self.RecycleButton.setIconSize(QtCore.QSize(200, 200)) self.RecycleButton.clicked.connect(self.RecycleButton_on_click) self.computerPlayerButton = QPushButton('', self) self.computerPlayerButton.isFlat() self.computerPlayerButton.setStyleSheet('background: transparent') self.computerPlayerButton.setIcon(QtGui.QIcon('Play_image.png')) self.computerPlayerButton.setIconSize(QtCore.QSize(200, 200)) self.computerPlayerButton.clicked.connect( self.computerPlayerButton_on_click) def createButtonHelper(self, button, icon, sizeY, sizeX, number): button.isFlat() button.setStyleSheet('background: transparent') button.setIcon(QtGui.QIcon(icon)) button.setIconSize(QtCore.QSize(sizeY, sizeX)) button.clicked.connect(lambda *args: self.buttonType_on_click(number)) @pyqtSlot() # send the input to game def Editor_button_on_click(self): print(self.editor.toPlainText()) @pyqtSlot() #send the input to game def RecycleButton_on_click(self): is_valid, card_removed, card_added, count, win_id = self.game.recycle_card( self.recylePosition1[0], self.recylePosition1[1], self.recylePosition1[2], self.recylePosition1[3], self.type, self.positionX, 11 - self.positionY) # print(is_valid, card_removed, card_added, count, win_id) if is_valid: self.invalidLable.setText("") self.playerChange() # remove previous card self.tableWidget.setItem(11 - card_removed.seg[0].y, card_removed.seg[0].x, QTableWidgetItem("")) self.tableWidget.item( 11 - card_removed.seg[0].y, card_removed.seg[0].x, ).setBackground(QtGui.QColor(255, 255, 255)) self.tableWidget.setItem(11 - card_removed.seg[1].y, card_removed.seg[1].x, QTableWidgetItem("")) self.tableWidget.item( 11 - card_removed.seg[1].y, card_removed.seg[1].x, ).setBackground(QtGui.QColor(255, 255, 255)) new_dot0, new_color0 = self.recycleHelper(card_added.seg[0]) new_dot1, new_color1 = self.recycleHelper(card_added.seg[1]) self.tableWidget.clearSelection() # add new card self.tableWidget.setItem(11 - card_added.seg[0].y, card_added.seg[0].x, QTableWidgetItem(new_dot0)) self.tableWidget.item( 11 - card_added.seg[0].y, card_added.seg[0].x, ).setBackground( QtGui.QColor(new_color0[0], new_color0[1], new_color0[2])) self.tableWidget.setItem(11 - card_added.seg[1].y, card_added.seg[1].x, QTableWidgetItem(new_dot1)) self.tableWidget.item( 11 - card_added.seg[1].y, card_added.seg[1].x, ).setBackground( QtGui.QColor(new_color1[0], new_color1[1], new_color1[2])) else: self.invalidLable.setText("Invalid Move") if win_id == 1: self.textLable.setText("Player1 Win!") if win_id == 2: self.textLable.setText("Player2 Win!") if count == 60: self.textLable.setText("Draw!") def recycleHelper(self, seg): new_color = -1 new_dot = -1 if seg.color == Color.RED: new_color = [255, 0, 0] elif seg.color == Color.WHITE: new_color = [225, 225, 225] if seg.dot == Dot.SOLID: new_dot = " ●" elif seg.dot == Dot.EMPTY: new_dot = " O" return new_dot, new_color @pyqtSlot() # computer move def computerPlayerButton_on_click(self): print("computer move") if self.curPlayer == "Computer Player": card_removed, card_added, count, win_id = self.game.computer_move( str(self.choice), self.auto_input_alg, True) new_dot0, new_color0 = self.recycleHelper(card_added.seg[0]) new_dot1, new_color1 = self.recycleHelper(card_added.seg[1]) self.countLable.setText("count: " + str(count)) self.playerChange() self.playerLable.setText(self.curPlayer + '(' + str(self.choice) + ')') self.invalidLable.setText("") if count > 23: self.phase = 'Recyle Phase' self.tableWidget.clearSelection() if card_removed != None: # remove previous card self.tableWidget.setItem(11 - card_removed.seg[0].y, card_removed.seg[0].x, QTableWidgetItem("")) self.tableWidget.item( 11 - card_removed.seg[0].y, card_removed.seg[0].x, ).setBackground(QtGui.QColor(255, 255, 255)) self.tableWidget.setItem(11 - card_removed.seg[1].y, card_removed.seg[1].x, QTableWidgetItem("")) self.tableWidget.item( 11 - card_removed.seg[1].y, card_removed.seg[1].x, ).setBackground(QtGui.QColor(255, 255, 255)) # add new card self.tableWidget.setItem(11 - card_added.seg[0].y, card_added.seg[0].x, QTableWidgetItem(new_dot0)) self.tableWidget.item( 11 - card_added.seg[0].y, card_added.seg[0].x, ).setBackground( QtGui.QColor(new_color0[0], new_color0[1], new_color0[2])) self.tableWidget.setItem(11 - card_added.seg[1].y, card_added.seg[1].x, QTableWidgetItem(new_dot1)) self.tableWidget.item( 11 - card_added.seg[1].y, card_added.seg[1].x, ).setBackground( QtGui.QColor(new_color1[0], new_color1[1], new_color1[2])) if win_id == 1: self.textLable.setText("Player1 Win!") if win_id == 2: self.textLable.setText("Player2 Win!") if count == 60: self.textLable.setText("Draw!") else: self.invalidLable.setText("Invalid Player") @pyqtSlot() def buttonType_on_click(self, buttonNum): self.type = buttonNum ShowY = 12 - self.positionY ShowX = str(chr(65 + self.positionX)) if self.phase == 'Normal Phase': print(self.positionX, ShowY - 1, self.type) # auto mode Human player or manual mode if (self.mode == '1' and self.curPlayer == "Human Player") or self.mode == '2': is_valid, card, count, win_id = self.game.place_card( self.type, self.positionX, ShowY - 1) self.tableWidget.clearSelection() if is_valid: self.countLable.setText("count: " + str(count)) self.playerChange() self.playerLable.setText(self.curPlayer + '(' + str(self.choice) + ')') self.invalidLable.setText("") # paint the cells if buttonNum == 1: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY, self.positionX + 1, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) self.tableWidget.item(self.positionY, self.positionX + 1).setBackground( QtGui.QColor(225, 225, 225)) elif buttonNum == 2: self.tableWidget.setItem(self.positionY - 1, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) self.tableWidget.item(self.positionY - 1, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) elif buttonNum == 3: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" O")) self.tableWidget.setItem(self.positionY, self.positionX + 1, QTableWidgetItem(" ●")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) self.tableWidget.item(self.positionY, self.positionX + 1).setBackground( QtGui.QColor(255, 0, 0)) elif buttonNum == 4: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY - 1, self.positionX, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) self.tableWidget.item(self.positionY - 1, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) elif buttonNum == 5: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" O")) self.tableWidget.setItem(self.positionY, self.positionX + 1, QTableWidgetItem(" ●")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) self.tableWidget.item(self.positionY, self.positionX + 1).setBackground( QtGui.QColor(225, 225, 225)) elif buttonNum == 6: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY - 1, self.positionX, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY - 1, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) elif buttonNum == 7: self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY, self.positionX + 1, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) self.tableWidget.item(self.positionY, self.positionX + 1).setBackground( QtGui.QColor(255, 0, 0)) elif buttonNum == 8: self.tableWidget.setItem(self.positionY - 1, self.positionX, QTableWidgetItem(" ●")) self.tableWidget.setItem(self.positionY, self.positionX, QTableWidgetItem(" O")) self.tableWidget.item(self.positionY, self.positionX).setBackground( QtGui.QColor(255, 0, 0)) self.tableWidget.item(self.positionY - 1, self.positionX).setBackground( QtGui.QColor(225, 225, 225)) else: self.invalidLable.setText("Invalid Move") if count > 24: self.phase = 'Recyle Phase' self.textLable.setText(self.phase) if win_id == 1: self.textLable.setText("Player1 Win!") if win_id == 2: self.textLable.setText("Player2 Win!") if count == 60: self.textLable.setText("Draw!") else: self.invalidLable.setText("Invalid Player") @pyqtSlot() def playerChange(self): if self.curPlayer == "Player 1": self.curPlayer = "Player 2" elif self.curPlayer == "Player 2": self.curPlayer = "Player 1" elif self.curPlayer == "Human Player": self.curPlayer = "Computer Player" elif self.curPlayer == "Computer Player": self.curPlayer = "Human Player" if self.choice == Choice.DOT: self.choice = Choice.COLOR elif self.choice == Choice.COLOR: self.choice = Choice.DOT
class WheelDlg(ArtisanDialog): def __init__(self, parent=None, aw=None): super(WheelDlg, self).__init__(parent, aw) self.setAttribute( Qt.WA_DeleteOnClose, False) # overwrite the ArtisanDialog class default here!! rcParams['path.effects'] = [] self.setModal(True) self.setWindowTitle( QApplication.translate("Form Caption", "Wheel Graph Editor", None)) #table self.datatable = QTableWidget() self.createdatatable() #table for labels self.labeltable = QTableWidget() self.subdialogbuttons = QDialogButtonBox( QDialogButtonBox.Close | QDialogButtonBox.RestoreDefaults, Qt.Horizontal) self.setButtonTranslations( self.subdialogbuttons.button(QDialogButtonBox.RestoreDefaults), "Restore Defaults", QApplication.translate("Button", "Restore Defaults", None)) self.setButtonTranslations( self.subdialogbuttons.button(QDialogButtonBox.Close), "Close", QApplication.translate("Button", "Close", None)) self.subdialogbuttons.rejected.connect(self.closelabels) self.subdialogbuttons.button( QDialogButtonBox.RestoreDefaults).clicked.connect( self.resetlabelparents) self.labelwheelx = 0 #index of wheel being edited on labeltable # self.hierarchyButton = QPushButton(QApplication.translate("Button","Reverse Hierarchy",None)) # self.hierarchyButton.setToolTip(QApplication.translate("Tooltip","Sets graph hierarchy child->parent instead of parent->child",None)) # self.hierarchyButton.clicked.connect(self.aw.qmc.setWheelHierarchy) self.labeltable.setVisible(False) self.subdialogbuttons.setVisible(False) aspectlabel = QLabel(QApplication.translate("Label", "Ratio", None)) self.aspectSpinBox = QDoubleSpinBox() self.aspectSpinBox.setToolTip( QApplication.translate("Tooltip", "Aspect Ratio", None)) self.aspectSpinBox.setRange(0., 2.) self.aspectSpinBox.setSingleStep(.1) self.aspectSpinBox.setValue(self.aw.qmc.wheelaspect) self.aspectSpinBox.valueChanged.connect(self.setaspect) txtlabel = QLabel(QApplication.translate("Label", "Text", None)) txtButtonplus = QPushButton(QApplication.translate( "Button", "+", None)) txtButtonplus.setToolTip( QApplication.translate("Tooltip", "Increase size of text in all the graph", None)) txtButtonplus.clicked.connect(self.changetext1) txtButtonminus = QPushButton( QApplication.translate("Button", "-", None)) txtButtonminus.setToolTip( QApplication.translate("Tooltip", "Decrease size of text in all the graph", None)) txtButtonminus.clicked.connect(self.changetext0) edgelabel = QLabel(QApplication.translate("Label", "Edge", None)) self.edgeSpinBox = QSpinBox() self.edgeSpinBox.setToolTip( QApplication.translate("Tooltip", "Decorative edge beween wheels", None)) self.edgeSpinBox.setRange(0, 5) self.edgeSpinBox.setValue(int(self.aw.qmc.wheeledge * 100)) self.edgeSpinBox.valueChanged.connect(self.setedge) linewidthlabel = QLabel(QApplication.translate("Label", "Line", None)) self.linewidthSpinBox = QSpinBox() self.linewidthSpinBox.setToolTip( QApplication.translate("Tooltip", "Line thickness", None)) self.linewidthSpinBox.setRange(0, 20) self.linewidthSpinBox.setValue(self.aw.qmc.wheellinewidth) self.linewidthSpinBox.valueChanged.connect(self.setlinewidth) linecolor = QPushButton( QApplication.translate("Button", "Line Color", None)) linecolor.setToolTip( QApplication.translate("Tooltip", "Line color", None)) linecolor.clicked.connect(self.setlinecolor) textcolor = QPushButton( QApplication.translate("Button", "Text Color", None)) textcolor.setToolTip( QApplication.translate("Tooltip", "Text color", None)) textcolor.clicked.connect(self.settextcolor) colorlabel = QLabel( QApplication.translate("Label", "Color pattern", None)) self.colorSpinBox = QSpinBox() self.colorSpinBox.setToolTip( QApplication.translate("Tooltip", "Apply color pattern to whole graph", None)) self.colorSpinBox.setRange(0, 255) self.colorSpinBox.setValue(self.aw.qmc.wheelcolorpattern) self.colorSpinBox.setWrapping(True) self.colorSpinBox.valueChanged.connect(self.setcolorpattern) addButton = QPushButton(QApplication.translate("Button", "Add", None)) addButton.setToolTip( QApplication.translate("Tooltip", "Add new wheel", None)) addButton.clicked.connect(self.insertwheel) rotateLeftButton = QPushButton( QApplication.translate("Button", "<", None)) rotateLeftButton.setToolTip( QApplication.translate("Tooltip", "Rotate graph 1 degree counter clockwise", None)) rotateLeftButton.clicked.connect(self.rotatewheels1) rotateRightButton = QPushButton( QApplication.translate("Button", ">", None)) rotateRightButton.setToolTip( QApplication.translate("Tooltip", "Rotate graph 1 degree clockwise", None)) rotateRightButton.clicked.connect(self.rotatewheels0) self.main_buttons = QDialogButtonBox() saveButton = QPushButton( QApplication.translate("Button", "Save File", None)) saveButton.clicked.connect(self.fileSave) saveButton.setToolTip( QApplication.translate("Tooltip", "Save graph to a text file.wg", None)) self.main_buttons.addButton(saveButton, QDialogButtonBox.ActionRole) saveImgButton = QPushButton( QApplication.translate("Button", "Save Img", None)) saveImgButton.setToolTip( QApplication.translate( "Tooltip", "Save image using current graph size to a png format", None)) #saveImgButton.clicked.connect(self.aw.resizeImg_0_1) # save as PNG (raster) saveImgButton.clicked.connect( self.aw.saveVectorGraph_PDF) # save as PDF (vector) self.main_buttons.addButton(saveImgButton, QDialogButtonBox.ActionRole) openButton = self.main_buttons.addButton(QDialogButtonBox.Open) openButton.setToolTip( QApplication.translate("Tooltip", "open wheel graph file", None)) openButton.clicked.connect(self.loadWheel) viewModeButton = self.main_buttons.addButton(QDialogButtonBox.Close) viewModeButton.setToolTip( QApplication.translate("Tooltip", "Sets Wheel graph to view mode", None)) viewModeButton.clicked.connect(self.viewmode) if self.aw.locale not in self.aw.qtbase_locales: self.main_buttons.button(QDialogButtonBox.Close).setText( QApplication.translate("Button", "Close", None)) self.main_buttons.button(QDialogButtonBox.Open).setText( QApplication.translate("Button", "Open", None)) self.aw.qmc.drawWheel() label1layout = QVBoxLayout() label2layout = QHBoxLayout() label1layout.addWidget(self.labeltable) label2layout.addWidget(self.subdialogbuttons) label1layout.addLayout(label2layout) self.labelGroupLayout = QGroupBox( QApplication.translate("GroupBox", "Label Properties", None)) self.labelGroupLayout.setLayout(label1layout) self.labelGroupLayout.setVisible(False) buttonlayout = QHBoxLayout() buttonlayout.addWidget(self.main_buttons) configlayout = QHBoxLayout() configlayout.addWidget(colorlabel) configlayout.addWidget(self.colorSpinBox) configlayout.addWidget(aspectlabel) configlayout.addWidget(self.aspectSpinBox) configlayout.addWidget(edgelabel) configlayout.addWidget(self.edgeSpinBox) configlayout.addWidget(linewidthlabel) configlayout.addWidget(self.linewidthSpinBox) configlayout.addWidget(linecolor) configlayout.addWidget(textcolor) configlayout.addWidget(txtlabel) configlayout.addWidget(txtButtonplus) configlayout.addWidget(txtButtonminus) controlLayout = QHBoxLayout() controlLayout.addWidget(addButton) controlLayout.addWidget(rotateLeftButton) controlLayout.addWidget(rotateRightButton) # controlLayout.addWidget(self.hierarchyButton) mainlayout = QVBoxLayout() mainlayout.addWidget(self.datatable) mainlayout.addWidget(self.labelGroupLayout) mainlayout.addLayout(controlLayout) mainlayout.addLayout(configlayout) mainlayout.addLayout(buttonlayout) self.setLayout(mainlayout) def close(self): self.accept() #creates config table for wheel with index x @pyqtSlot(bool) def createlabeltable(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 3) if x is not None: self.createlabeltablex(x) def createlabeltablex(self, x): self.labelwheelx = x #wheel being edited self.labelGroupLayout.setVisible(True) self.labeltable.setVisible(True) self.subdialogbuttons.setVisible(True) nlabels = len(self.aw.qmc.wheelnames[x]) # self.labeltable.clear() # this crashes Ubuntu 16.04 self.labeltable.clearSelection( ) # this seems to work also for Ubuntu 16.04 if nlabels: self.labeltable.setRowCount(nlabels) self.labeltable.setColumnCount(5) self.labeltable.setHorizontalHeaderLabels([ QApplication.translate("Table", "Label", None), QApplication.translate("Table", "Parent", None), QApplication.translate("Table", "Width", None), QApplication.translate("Table", "Color", None), QApplication.translate("Table", "Opaqueness", None) ]) self.labeltable.setAlternatingRowColors(True) self.labeltable.setEditTriggers(QTableWidget.NoEditTriggers) self.labeltable.setSelectionBehavior(QTableWidget.SelectRows) self.labeltable.setSelectionMode(QTableWidget.SingleSelection) self.labeltable.setShowGrid(True) self.labeltable.verticalHeader().setSectionResizeMode(2) #populate table for i in range(nlabels): label = QTableWidgetItem(self.aw.qmc.wheelnames[x][i]) parentComboBox = QComboBox() if x > 0: items = self.aw.qmc.wheelnames[x - 1][:] items.insert(0, "") parentComboBox.addItems(items) if self.aw.qmc.wheellabelparent[x][i]: parentComboBox.setCurrentIndex( self.aw.qmc.wheellabelparent[x][i]) else: parentComboBox.addItems([]) parentComboBox.currentIndexChanged.connect(self.setwheelchild) labelwidthSpinBox = QDoubleSpinBox() labelwidthSpinBox.setRange(1., 100.) labelwidthSpinBox.setValue(self.aw.qmc.segmentlengths[x][i]) labelwidthSpinBox.setSuffix("%") labelwidthSpinBox.valueChanged.connect(self.setlabelwidth) colorButton = QPushButton("Set Color") colorButton.clicked.connect(self.setsegmentcolor) alphaSpinBox = QSpinBox() alphaSpinBox.setRange(0, 10) alphaSpinBox.setValue(int(self.aw.qmc.segmentsalpha[x][i] * 10)) alphaSpinBox.valueChanged.connect(self.setsegmentalpha) #add widgets to the table self.labeltable.setItem(i, 0, label) self.labeltable.setCellWidget(i, 1, parentComboBox) self.labeltable.setCellWidget(i, 2, labelwidthSpinBox) self.labeltable.setCellWidget(i, 3, colorButton) self.labeltable.setCellWidget(i, 4, alphaSpinBox) @pyqtSlot(bool) def setsegmentcolor(self, _): i = self.aw.findWidgetsRow(self.labeltable, self.sender(), 3) if i is not None: x = self.labelwheelx colorf = self.aw.colordialog(QColor(self.aw.qmc.wheelcolor[x][i])) if colorf.isValid(): colorname = str(colorf.name()) self.aw.qmc.wheelcolor[x][ i] = colorname #add new color to label self.createdatatable( ) #update main table with label names (label::color) self.aw.qmc.drawWheel() #sets a uniform color in wheel @pyqtSlot(bool) def setwheelcolor(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 8) if x is not None: colorf = self.aw.colordialog(QColor(self.aw.qmc.wheelcolor[x][0])) if colorf.isValid(): colorname = str(colorf.name()) for i in range(len(self.aw.qmc.wheelcolor[x])): self.aw.qmc.wheelcolor[x][i] = colorname self.createdatatable() self.aw.qmc.drawWheel() #sets color pattern (many colors) in wheel @pyqtSlot(int) def setwheelcolorpattern(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 9) if x is not None: wsb = self.datatable.cellWidget(x, 9) wpattern = wsb.value() wlen = len(self.aw.qmc.wheelcolor[x]) for i in range(wlen): color = QColor() color.setHsv((360 / wlen) * i * wpattern, 255, 255, 255) self.aw.qmc.wheelcolor[x][i] = str(color.name()) self.aw.qmc.drawWheel() #sets color pattern (many colors) for whole graph @pyqtSlot(int) def setcolorpattern(self, _): self.aw.qmc.wheelcolorpattern = self.colorSpinBox.value() if self.aw.qmc.wheelcolorpattern: for x in range(len(self.aw.qmc.wheelcolor)): wlen = len(self.aw.qmc.wheelcolor[x]) for i in range(wlen): color = QColor() color.setHsv( (360 / wlen) * i * self.aw.qmc.wheelcolorpattern, 255, 255, 255) self.aw.qmc.wheelcolor[x][i] = str(color.name()) self.aw.qmc.drawWheel() @pyqtSlot(int) def setsegmentalpha(self, z): u = self.aw.findWidgetsRow(self.labeltable, self.sender(), 4) if u is not None: x = self.labelwheelx self.aw.qmc.segmentsalpha[x][u] = float(z / 10.) self.aw.qmc.drawWheel() #rotate whole graph @pyqtSlot(bool) def rotatewheels1(self, _): for i in range(len(self.aw.qmc.startangle)): self.aw.qmc.startangle[i] += 1 self.aw.qmc.drawWheel() @pyqtSlot(bool) def rotatewheels0(self, _): for i in range(len(self.aw.qmc.startangle)): self.aw.qmc.startangle[i] -= 1 self.aw.qmc.drawWheel() #z= new width%, x= wheel number index, u = index of segment in the wheel @pyqtSlot(float) def setlabelwidth(self, z): u = self.aw.findWidgetsRow(self.labeltable, self.sender(), 2) if u is not None: x = self.labelwheelx newwidth = z oldwidth = self.aw.qmc.segmentlengths[x][u] diff = newwidth - oldwidth l = len(self.aw.qmc.segmentlengths[x]) for i in range(l): if i != u: if diff > 0: self.aw.qmc.segmentlengths[x][i] -= abs( float(diff)) / (l - 1) else: self.aw.qmc.segmentlengths[x][i] += abs( float(diff)) / (l - 1) self.aw.qmc.segmentlengths[x][u] = newwidth self.aw.qmc.drawWheel() #input: z = index of parent in previus wheel; x = wheel number; i = index of element in wheel @pyqtSlot(int) def setwheelchild(self, z): i = self.aw.findWidgetsRow(self.labeltable, self.sender(), 1) if i is not None: self.aw.qmc.setwheelchild(z, self.labelwheelx, i) self.aw.qmc.drawWheel() self.createdatatable() #update data table #deletes parent-child relation in a wheel. It obtains the wheel index by self.labelwheelx @pyqtSlot(bool) def resetlabelparents(self, _): x = self.labelwheelx nsegments = len(self.aw.qmc.wheellabelparent[x]) for i in range(nsegments): self.aw.qmc.wheellabelparent[x][i] = 0 self.aw.qmc.segmentlengths[x][i] = 100. / nsegments self.aw.qmc.drawWheel() self.createlabeltablex(x) @pyqtSlot(float) def setaspect(self, _): self.aw.qmc.wheelaspect = self.aspectSpinBox.value() self.aw.qmc.drawWheel() #adjust decorative edge between wheels @pyqtSlot(int) def setedge(self): self.aw.qmc.wheeledge = float(self.edgeSpinBox.value()) / 100. self.aw.qmc.drawWheel() #adjusts line thickness @pyqtSlot(int) def setlinewidth(self, _): self.aw.qmc.wheellinewidth = self.linewidthSpinBox.value() self.aw.qmc.drawWheel() #sets line color @pyqtSlot(bool) def setlinecolor(self, _): colorf = self.aw.colordialog(QColor(self.aw.qmc.wheellinecolor)) if colorf.isValid(): colorname = str(colorf.name()) #self.aw.qmc.wheellinealpha = colorf.alphaF() self.aw.qmc.wheellinecolor = colorname #add new color to label self.aw.qmc.drawWheel() #sets text color @pyqtSlot(bool) def settextcolor(self, _): colorf = self.aw.colordialog(QColor(self.aw.qmc.wheeltextcolor)) if colorf.isValid(): colorname = str(colorf.name()) #self.aw.qmc.wheeltextalpha = colorf.alphaF() self.aw.qmc.wheeltextcolor = colorname #add new color to label self.aw.qmc.drawWheel() #makes not visible the wheel config table @pyqtSlot() def closelabels(self): self.labelGroupLayout.setVisible(False) self.labeltable.setVisible(False) # self.labelCloseButton.setVisible(False) # self.labelResetButton.setVisible(False) self.subdialogbuttons.setVisible(False) #creates graph table def createdatatable(self): ndata = len(self.aw.qmc.wheelnames) # self.datatable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.datatable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.datatable.clearSelection( ) # this seems to work also for Ubuntu 16.04 self.datatable.setRowCount(ndata) self.datatable.setColumnCount(10) self.datatable.setHorizontalHeaderLabels([ QApplication.translate("Table", "Delete Wheel", None), QApplication.translate("Table", "Edit Labels", None), QApplication.translate("Table", "Update Labels", None), QApplication.translate("Table", "Properties", None), QApplication.translate("Table", "Radius", None), QApplication.translate("Table", "Starting angle", None), QApplication.translate("Table", "Projection", None), QApplication.translate("Table", "Text Size", None), QApplication.translate("Table", "Color", None), QApplication.translate("Table", "Color Pattern", None) ]) self.datatable.setAlternatingRowColors(True) self.datatable.setEditTriggers(QTableWidget.NoEditTriggers) self.datatable.setSelectionBehavior(QTableWidget.SelectRows) self.datatable.setSelectionMode(QTableWidget.SingleSelection) self.datatable.setShowGrid(True) self.datatable.verticalHeader().setSectionResizeMode(2) #populate table for i in range(ndata): delButton = QPushButton( QApplication.translate("Button", "Delete", None)) delButton.clicked.connect(self.popwheel) labelsedit = QLineEdit(str(",".join(self.aw.qmc.wheelnames[i]))) updateButton = QPushButton( QApplication.translate("Button", "Update", None)) updateButton.clicked.connect(self.updatelabels) setButton = QPushButton( QApplication.translate("Button", "Select", None)) setButton.clicked.connect(self.createlabeltable) widthSpinBox = QDoubleSpinBox() widthSpinBox.setRange(1., 100.) widthSpinBox.setValue(self.aw.qmc.wradii[i]) widthSpinBox.setSuffix("%") widthSpinBox.valueChanged.connect(self.setwidth) angleSpinBox = QSpinBox() angleSpinBox.setSuffix(QApplication.translate( "Label", " dg", None)) angleSpinBox.setRange(0, 359) angleSpinBox.setWrapping(True) angleSpinBox.setValue(self.aw.qmc.startangle[i]) angleSpinBox.valueChanged.connect(self.setangle) projectionComboBox = QComboBox() projectionComboBox.addItems([ QApplication.translate("ComboBox", "Flat", None), QApplication.translate("ComboBox", "Perpendicular", None), QApplication.translate("ComboBox", "Radial", None) ]) projectionComboBox.setCurrentIndex(self.aw.qmc.projection[i]) projectionComboBox.currentIndexChanged.connect(self.setprojection) txtSpinBox = QSpinBox() txtSpinBox.setRange(1, 30) txtSpinBox.setValue(self.aw.qmc.wheeltextsize[i]) txtSpinBox.valueChanged.connect(self.setTextsizeX) colorButton = QPushButton( QApplication.translate("Button", "Set Color", None)) colorButton.clicked.connect(self.setwheelcolor) colorSpinBox = QSpinBox() colorSpinBox.setRange(0, 255) colorSpinBox.setWrapping(True) colorSpinBox.valueChanged.connect(self.setwheelcolorpattern) #add widgets to the table self.datatable.setCellWidget(i, 0, delButton) self.datatable.setCellWidget(i, 1, labelsedit) self.datatable.setCellWidget(i, 2, updateButton) self.datatable.setCellWidget(i, 3, setButton) self.datatable.setCellWidget(i, 4, widthSpinBox) self.datatable.setCellWidget(i, 5, angleSpinBox) self.datatable.setCellWidget(i, 6, projectionComboBox) self.datatable.setCellWidget(i, 7, txtSpinBox) self.datatable.setCellWidget(i, 8, colorButton) self.datatable.setCellWidget(i, 9, colorSpinBox) #reads label edit box for wheel with index x, and updates labels @pyqtSlot(bool) def updatelabels(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 2) if x is not None: labelsedit = self.datatable.cellWidget(x, 1) text = str(labelsedit.text()) if "\\n" in text: #make multiple line text if "\n" found in label string parts = text.split("\\n") text = chr(10).join(parts) newwheellabels = text.strip().split(",") newnlabels = len(newwheellabels) oldnlabels = len(self.aw.qmc.wheelnames[x]) #adjust segments len and alpha for each wheel if number of labels changed if oldnlabels != newnlabels: self.aw.qmc.segmentlengths[x] = [100. / newnlabels ] * newnlabels self.aw.qmc.segmentsalpha[x] = [.3] * newnlabels self.aw.qmc.wheellabelparent[x] = [0] * newnlabels self.aw.qmc.wheelcolor[x] = [self.aw.qmc.wheelcolor[x][0] ] * newnlabels self.aw.qmc.wheelnames[x] = newwheellabels[:] self.aw.qmc.drawWheel() #sets radii for a wheel @pyqtSlot(float) def setwidth(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 4) if x is not None: widthSpinBox = self.datatable.cellWidget(x, 4) newwidth = widthSpinBox.value() oldwidth = self.aw.qmc.wradii[x] diff = newwidth - oldwidth l = len(self.aw.qmc.wradii) for i in range(l): if i != x: if diff > 0: self.aw.qmc.wradii[i] -= abs(float(diff)) / (l - 1) else: self.aw.qmc.wradii[i] += abs(float(diff)) / (l - 1) self.aw.qmc.wradii[x] = newwidth #Need 100.0% coverage. Correct for numerical floating point rounding errors: count = 0. for i in range(len(self.aw.qmc.wradii)): count += self.aw.qmc.wradii[i] diff = 100. - count if diff != 0.: if diff > 0.000: #if count smaller self.aw.qmc.wradii[x] += abs(diff) else: self.aw.qmc.wradii[x] -= abs(diff) self.aw.qmc.drawWheel() #sets starting angle (rotation) for a wheel with index x @pyqtSlot(int) def setangle(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 5) if x is not None: angleSpinBox = self.datatable.cellWidget(x, 5) self.aw.qmc.startangle[x] = angleSpinBox.value() self.aw.qmc.drawWheel() #sets text projection style for a wheel with index x @pyqtSlot(int) def setprojection(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 6) if x is not None: projectionComboBox = self.datatable.cellWidget(x, 6) self.aw.qmc.projection[x] = projectionComboBox.currentIndex() self.aw.qmc.drawWheel() #chages text size in wheel with index x @pyqtSlot(int) def setTextsizeX(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 7) if x is not None: txtSpinBox = self.datatable.cellWidget(x, 7) self.aw.qmc.wheeltextsize[x] = txtSpinBox.value() self.aw.qmc.drawWheel() #changes size of text in whole graph @pyqtSlot(bool) def changetext1(self, _): for i in range(len(self.aw.qmc.wheeltextsize)): self.aw.qmc.wheeltextsize[i] += 1 self.aw.qmc.drawWheel() @pyqtSlot(bool) def changetext0(self, _): for i in range(len(self.aw.qmc.wheeltextsize)): self.aw.qmc.wheeltextsize[i] -= 1 self.aw.qmc.drawWheel() #adds new top wheel @pyqtSlot(bool) def insertwheel(self, _): ndata = len(self.aw.qmc.wradii) if ndata: count = 0. for i in range(ndata): self.aw.qmc.wradii[i] = 100. / (ndata + 1) count += self.aw.qmc.wradii[i] self.aw.qmc.wradii.append(100. - count) else: self.aw.qmc.wradii.append(100.) #find number of labels of most outer wheel (last) if len(self.aw.qmc.wheelnames): nwheels = len(self.aw.qmc.wheelnames[-1]) else: #if no wheels nwheels = 3 wn, sl, sa, wlp, co = [], [], [], [], [] for i in range(nwheels + 1): wn.append("W%i %i" % (len(self.aw.qmc.wheelnames) + 1, i + 1)) sl.append(100. / (nwheels + 1)) sa.append(.3) wlp.append(0) color = QColor() color.setHsv((360 / (nwheels + 1)) * i, 255, 255, 255) co.append(str(color.name())) self.aw.qmc.wheelnames.append(wn) self.aw.qmc.segmentlengths.append(sl) self.aw.qmc.segmentsalpha.append(sa) self.aw.qmc.wheellabelparent.append(wlp) self.aw.qmc.startangle.append(0) self.aw.qmc.projection.append(2) self.aw.qmc.wheeltextsize.append(10) self.aw.qmc.wheelcolor.append(co) self.createdatatable() self.aw.qmc.drawWheel() #deletes wheel with index x @pyqtSlot(bool) def popwheel(self, _): x = self.aw.findWidgetsRow(self.datatable, self.sender(), 0) if x is not None: #correct raius of other wheels (to use 100% coverage) width = self.aw.qmc.wradii[x] l = len(self.aw.qmc.wradii) for i in range(l): if i != x: self.aw.qmc.wradii[i] += float(width) / (l - 1) self.aw.qmc.wheelnames.pop(x) self.aw.qmc.wradii.pop(x) self.aw.qmc.startangle.pop(x) self.aw.qmc.projection.pop(x) self.aw.qmc.wheeltextsize.pop(x) self.aw.qmc.segmentlengths.pop(x) self.aw.qmc.segmentsalpha.pop(x) self.aw.qmc.wheellabelparent.pop(x) self.aw.qmc.wheelcolor.pop(x) self.createdatatable() self.aw.qmc.drawWheel() @pyqtSlot(bool) def fileSave(self, _): try: filename = self.aw.ArtisanSaveFileDialog( msg=QApplication.translate("Message", "Save Wheel graph", None), ext="*.wg") if filename: #write self.aw.serialize(filename, self.aw.getWheelGraph()) self.aw.sendmessage( QApplication.translate("Message", "Wheel Graph saved", None)) except IOError as e: self.aw.qmc.adderror( (QApplication.translate("Error Message", "IO Error:", None) + " Wheel graph filesave(): {0}").format(str(e))) return @pyqtSlot(bool) def loadWheel(self, _): filename = self.aw.ArtisanOpenFileDialog(msg=QApplication.translate( "Message", "Open Wheel Graph", None), path=self.aw.getDefaultPath(), ext="*.wg") if filename: self.aw.loadWheel(filename) self.aw.wheelpath = filename self.createdatatable() self.aw.qmc.drawWheel() def closeEvent(self, _): self.viewmode(False) @pyqtSlot(bool) def viewmode(self, _): self.close() self.aw.qmc.connectWheel() self.aw.qmc.drawWheel()
class backgroundDlg(ArtisanResizeablDialog): def __init__(self, parent = None, aw = None, activeTab = 0): super(backgroundDlg,self).__init__(parent, aw) self.setWindowTitle(QApplication.translate("Form Caption","Profile Background", None)) self.setModal(True) settings = QSettings() if settings.contains("BackgroundGeometry"): self.restoreGeometry(settings.value("BackgroundGeometry")) #TAB 1 self.pathedit = QLineEdit(self.aw.qmc.backgroundpath) self.pathedit.setStyleSheet("background-color:'lightgrey';") self.pathedit.setReadOnly(True) self.pathedit.setFocusPolicy(Qt.NoFocus) self.filename = "" self.backgroundCheck = QCheckBox(QApplication.translate("CheckBox","Show", None)) self.backgroundDetails = QCheckBox(QApplication.translate("CheckBox","Annotations", None)) self.backgroundeventsflag = QCheckBox(QApplication.translate("CheckBox","Events", None)) self.backgroundDeltaETflag = QCheckBox() backgroundDeltaETflagLabel = QLabel(deltaLabelPrefix + QApplication.translate("Label","ET", None)) self.backgroundDeltaBTflag = QCheckBox() backgroundDeltaBTflagLabel = QLabel(deltaLabelPrefix + QApplication.translate("Label","BT", None)) self.backgroundETflag = QCheckBox(QApplication.translate("CheckBox","ET", None)) self.backgroundBTflag = QCheckBox(QApplication.translate("CheckBox","BT", None)) self.backgroundFullflag = QCheckBox(QApplication.translate("CheckBox","Show Full", None)) self.backgroundCheck.setChecked(self.aw.qmc.background) self.backgroundDetails.setChecked(self.aw.qmc.backgroundDetails) self.backgroundeventsflag.setChecked(self.aw.qmc.backgroundeventsflag) self.backgroundDeltaETflag.setChecked(self.aw.qmc.DeltaETBflag) self.backgroundDeltaBTflag.setChecked(self.aw.qmc.DeltaBTBflag) self.backgroundETflag.setChecked(self.aw.qmc.backgroundETcurve) self.backgroundBTflag.setChecked(self.aw.qmc.backgroundBTcurve) self.backgroundFullflag.setChecked(self.aw.qmc.backgroundShowFullflag) loadButton = QPushButton(QApplication.translate("Button","Load", None)) loadButton.setFocusPolicy(Qt.NoFocus) delButton = QPushButton(QApplication.translate("Button","Delete", None)) delButton.setFocusPolicy(Qt.NoFocus) # connect the ArtisanDialog standard OK/Cancel buttons self.dialogbuttons.accepted.connect(self.accept) self.dialogbuttons.removeButton(self.dialogbuttons.button(QDialogButtonBox.Cancel)) alignButton = QPushButton(QApplication.translate("Button","Align", None)) alignButton.setFocusPolicy(Qt.NoFocus) self.alignComboBox = QComboBox() alignnames = [ QApplication.translate("Label","CHARGE", None), QApplication.translate("Label","DRY", None), QApplication.translate("Label","FCs", None), QApplication.translate("Label","FCe", None), QApplication.translate("Label","SCs", None), QApplication.translate("Label","SCe", None), QApplication.translate("Label","DROP", None), QApplication.translate("Label","ALL", None), ] self.alignComboBox.addItems(alignnames) self.alignComboBox.setCurrentIndex(self.aw.qmc.alignEvent) self.alignComboBox.currentIndexChanged.connect(self.changeAlignEventidx) loadButton.clicked.connect(self.load) alignButton.clicked.connect(self.timealign) self.speedSpinBox = QSpinBox() self.speedSpinBox.setAlignment(Qt.AlignRight) self.speedSpinBox.setRange(1,90) self.speedSpinBox.setSingleStep(5) self.speedSpinBox.setValue(self.aw.qmc.backgroundmovespeed) curvenames = [""] # first entry is the empty one, no extra curve displayed for i in range(min(len(self.aw.qmc.extraname1B),len(self.aw.qmc.extraname2B),len(self.aw.qmc.extratimexB))): curvenames.append("B" + str(2*i+3) + ": " + self.aw.qmc.extraname1B[i]) curvenames.append("B" + str(2*i+4) + ": " + self.aw.qmc.extraname2B[i]) self.xtcurvelabel = QLabel(QApplication.translate("Label", "Extra 1",None)) self.xtcurveComboBox = QComboBox() self.xtcurveComboBox.setToolTip(QApplication.translate("Tooltip","For loaded backgrounds with extra devices only",None)) self.xtcurveComboBox.setMinimumWidth(120) self.xtcurveComboBox.addItems(curvenames) if self.aw.qmc.xtcurveidx < len(curvenames): self.xtcurveComboBox.setCurrentIndex(self.aw.qmc.xtcurveidx) self.xtcurveComboBox.currentIndexChanged.connect(self.changeXTcurveidx) self.ytcurvelabel = QLabel(QApplication.translate("Label", "Extra 2",None)) self.ytcurveComboBox = QComboBox() self.ytcurveComboBox.setToolTip(QApplication.translate("Tooltip","For loaded backgrounds with extra devices only",None)) self.ytcurveComboBox.setMinimumWidth(120) self.ytcurveComboBox.addItems(curvenames) if self.aw.qmc.ytcurveidx < len(curvenames): self.ytcurveComboBox.setCurrentIndex(self.aw.qmc.ytcurveidx) self.ytcurveComboBox.currentIndexChanged.connect(self.changeYTcurveidx) self.upButton = QPushButton(QApplication.translate("Button","Up",None)) self.upButton.setFocusPolicy(Qt.NoFocus) self.downButton = QPushButton(QApplication.translate("Button","Down",None)) self.downButton.setFocusPolicy(Qt.NoFocus) self.leftButton = QPushButton(QApplication.translate("Button","Left",None)) self.leftButton.setFocusPolicy(Qt.NoFocus) self.rightButton = QPushButton(QApplication.translate("Button","Right",None)) self.rightButton.setFocusPolicy(Qt.NoFocus) self.backgroundCheck.clicked.connect(self.readChecks) self.backgroundDetails.clicked.connect(self.readChecks) self.backgroundeventsflag.clicked.connect(self.readChecks) self.backgroundDeltaETflag.clicked.connect(self.readChecks) self.backgroundDeltaBTflag.clicked.connect(self.readChecks) self.backgroundETflag.clicked.connect(self.readChecks) self.backgroundBTflag.clicked.connect(self.readChecks) self.backgroundFullflag.clicked.connect(self.readChecks) delButton.clicked.connect(self.delete) self.upButton.clicked.connect(self.moveUp) self.downButton.clicked.connect(self.moveDown) self.leftButton.clicked.connect(self.moveLeft) self.rightButton.clicked.connect(self.moveRight) #TAB 2 EVENTS #table for showing events self.eventtable = QTableWidget() self.eventtable.setTabKeyNavigation(True) self.createEventTable() self.copyeventTableButton = QPushButton(QApplication.translate("Button", "Copy Table",None)) self.copyeventTableButton.setToolTip(QApplication.translate("Tooltip","Copy table to clipboard, OPTION or ALT click for tabular text",None)) self.copyeventTableButton.setFocusPolicy(Qt.NoFocus) self.copyeventTableButton.setMaximumSize(self.copyeventTableButton.sizeHint()) self.copyeventTableButton.setMinimumSize(self.copyeventTableButton.minimumSizeHint()) self.copyeventTableButton.clicked.connect(self.copyEventTabletoClipboard) #TAB 3 DATA #table for showing data self.datatable = QTableWidget() self.datatable.setTabKeyNavigation(True) self.createDataTable() self.copydataTableButton = QPushButton(QApplication.translate("Button", "Copy Table",None)) self.copydataTableButton.setToolTip(QApplication.translate("Tooltip","Copy table to clipboard, OPTION or ALT click for tabular text",None)) self.copydataTableButton.setFocusPolicy(Qt.NoFocus) self.copydataTableButton.setMaximumSize(self.copydataTableButton.sizeHint()) self.copydataTableButton.setMinimumSize(self.copydataTableButton.minimumSizeHint()) self.copydataTableButton.clicked.connect(self.copyDataTabletoClipboard) #TAB 4 self.replayComboBox = QComboBox() replayVariants = [ QApplication.translate("Label","by time", None), QApplication.translate("Label","by BT", None), QApplication.translate("Label","by ET", None), ] self.replayComboBox.addItems(replayVariants) self.replayComboBox.setCurrentIndex(self.aw.qmc.replayType) self.replayComboBox.currentIndexChanged.connect(self.changeReplayTypeidx) self.backgroundReproduce = QCheckBox(QApplication.translate("CheckBox","Playback Aid",None)) self.backgroundReproduce.setChecked(self.aw.qmc.backgroundReproduce) self.backgroundReproduce.setFocusPolicy(Qt.NoFocus) self.backgroundReproduce.stateChanged.connect(self.setreproduce) self.backgroundReproduceBeep = QCheckBox(QApplication.translate("CheckBox","Beep",None)) self.backgroundReproduceBeep.setChecked(self.aw.qmc.backgroundReproduce) self.backgroundReproduceBeep.setFocusPolicy(Qt.NoFocus) self.backgroundReproduceBeep.stateChanged.connect(self.setreproduceBeep) self.backgroundPlaybackEvents = QCheckBox(QApplication.translate("CheckBox","Playback Events",None)) self.backgroundPlaybackEvents.setChecked(self.aw.qmc.backgroundPlaybackEvents) self.backgroundPlaybackEvents.setFocusPolicy(Qt.NoFocus) self.backgroundPlaybackEvents.stateChanged.connect(self.setplaybackevent) self.backgroundPlaybackDROP = QCheckBox(QApplication.translate("CheckBox","Playback DROP",None)) self.backgroundPlaybackDROP.setChecked(self.aw.qmc.backgroundPlaybackDROP) self.backgroundPlaybackDROP.setFocusPolicy(Qt.NoFocus) self.backgroundPlaybackDROP.stateChanged.connect(self.setplaybackdrop) etimelabel =QLabel(QApplication.translate("Label", "Text Warning",None)) etimeunit =QLabel(QApplication.translate("Label", "sec",None)) self.etimeSpinBox = QSpinBox() self.etimeSpinBox.setRange(1,60) self.etimeSpinBox.setValue(self.aw.qmc.detectBackgroundEventTime) self.etimeSpinBox.valueChanged.connect(self.setreproduce) #LAYOUT MANAGERS movelayout = QGridLayout() movelayout.addWidget(self.upButton,0,1) movelayout.addWidget(self.leftButton,1,0) movelayout.addWidget(self.speedSpinBox,1,1) movelayout.addWidget(self.rightButton,1,2) movelayout.addWidget(self.downButton,2,1) movelayout.setSpacing(20) checkslayout1 = QHBoxLayout() checkslayout1.addStretch() checkslayout1.addWidget(self.backgroundCheck) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDetails) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundeventsflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundETflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundBTflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDeltaETflag) checkslayout1.addSpacing(3) checkslayout1.addWidget(backgroundDeltaETflagLabel) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDeltaBTflag) checkslayout1.addSpacing(3) checkslayout1.addWidget(backgroundDeltaBTflagLabel) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundFullflag) checkslayout1.addStretch() layout = QGridLayout() layoutBoxedH = QHBoxLayout() layoutBoxedH.addStretch() layoutBoxedH.addLayout(movelayout) layoutBoxedH.addLayout(layout) layoutBoxedH.addStretch() layoutBoxed = QVBoxLayout() layoutBoxed.addStretch() layoutBoxed.addLayout(checkslayout1) layoutBoxed.addStretch() layoutBoxed.addLayout(layoutBoxedH) layoutBoxed.addStretch() alignButtonBoxed = QHBoxLayout() alignButtonBoxed.addWidget(self.xtcurvelabel) alignButtonBoxed.addWidget(self.xtcurveComboBox) alignButtonBoxed.addSpacing(10) alignButtonBoxed.addWidget(self.ytcurvelabel) alignButtonBoxed.addWidget(self.ytcurveComboBox) alignButtonBoxed.addStretch() alignButtonBoxed.addWidget(alignButton) alignButtonBoxed.addWidget(self.alignComboBox) tab4content = QHBoxLayout() tab4content.addWidget(self.backgroundReproduce) tab4content.addSpacing(10) tab4content.addWidget(self.backgroundReproduceBeep) tab4content.addSpacing(10) tab4content.addWidget(etimelabel) tab4content.addWidget(self.etimeSpinBox) tab4content.addWidget(etimeunit) tab4content.addSpacing(20) tab4content.addStretch() tab4content.addWidget(self.backgroundPlaybackEvents) tab4content.addSpacing(10) tab4content.addWidget(self.backgroundPlaybackDROP) tab4content.addSpacing(10) tab4content.addWidget(self.replayComboBox) tab1layout = QVBoxLayout() tab1layout.addLayout(layoutBoxed) # tab1layout.addStretch() tab1layout.addLayout(alignButtonBoxed) tab1layout.addLayout(tab4content) tab1layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom eventbuttonLayout = QHBoxLayout() eventbuttonLayout.addWidget(self.copyeventTableButton) eventbuttonLayout.addStretch() tab2layout = QVBoxLayout() tab2layout.addWidget(self.eventtable) tab2layout.addLayout(eventbuttonLayout) tab2layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom databuttonLayout = QHBoxLayout() databuttonLayout.addWidget(self.copydataTableButton) databuttonLayout.addStretch() tab3layout = QVBoxLayout() tab3layout.addWidget(self.datatable) tab3layout.addLayout(databuttonLayout) tab3layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom #tab layout tab1layout.setSpacing(5) self.TabWidget = QTabWidget() C1Widget = QWidget() C1Widget.setLayout(tab1layout) self.TabWidget.addTab(C1Widget,QApplication.translate("Tab","Config",None)) C2Widget = QWidget() C2Widget.setLayout(tab2layout) self.TabWidget.addTab(C2Widget,QApplication.translate("Tab","Events",None)) C3Widget = QWidget() C3Widget.setLayout(tab3layout) self.TabWidget.addTab(C3Widget,QApplication.translate("Tab","Data",None)) buttonLayout = QHBoxLayout() buttonLayout.addWidget(loadButton) buttonLayout.addWidget(delButton) buttonLayout.addStretch() buttonLayout.addWidget(self.dialogbuttons) mainLayout = QVBoxLayout() mainLayout.addWidget(self.TabWidget) mainLayout.addWidget(self.pathedit) mainLayout.addLayout(buttonLayout) mainLayout.setContentsMargins(5, 10, 5, 5) # left, top, right, bottom self.setLayout(mainLayout) if platform.system() == 'Windows': self.dialogbuttons.button(QDialogButtonBox.Ok) else: self.dialogbuttons.button(QDialogButtonBox.Ok).setFocus() self.TabWidget.setCurrentIndex(activeTab) @pyqtSlot(bool) def timealign(self,_): self.aw.qmc.timealign() #keyboard presses. There must not be widgets (pushbuttons, comboboxes, etc) in focus in order to work def keyPressEvent(self,event): if event.matches(QKeySequence.Copy): if self.TabWidget.currentIndex() == 2: # datatable self.aw.copy_cells_to_clipboard(self.datatable) self.aw.sendmessage(QApplication.translate("Message","Data table copied to clipboard",None)) else: super(backgroundDlg,self).keyPressEvent(event) @pyqtSlot() def accept(self): self.aw.qmc.backgroundmovespeed = self.speedSpinBox.value() self.close() def closeEvent(self,_): settings = QSettings() #save window geometry settings.setValue("BackgroundGeometry",self.saveGeometry()) self.aw.backgroundDlg_activeTab = self.TabWidget.currentIndex() def getColorIdx(self,c): try: return self.defaultcolorsmapped.index(c) except Exception: try: return self.colors.index(c) + 5 except Exception: return 0 @pyqtSlot(int) def setplaybackevent(self,_): s = None if self.backgroundPlaybackEvents.isChecked(): self.aw.qmc.backgroundPlaybackEvents = True msg = QApplication.translate("Message","Playback Events set ON",None) else: self.aw.qmc.backgroundPlaybackEvents = False msg = QApplication.translate("StatusBar","Playback Events set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) @pyqtSlot(int) def setplaybackdrop(self,_): s = None if self.backgroundPlaybackDROP.isChecked(): self.aw.qmc.backgroundPlaybackDROP = True msg = QApplication.translate("Message","Playback DROP set ON",None) else: self.aw.qmc.backgroundPlaybackDROP = False msg = QApplication.translate("StatusBar","Playback DROP set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) @pyqtSlot(int) def setreproduceBeep(self,_): if self.backgroundReproduceBeep.isChecked(): self.aw.qmc.backgroundReproduceBeep = True else: self.aw.qmc.backgroundReproduceBeep = False @pyqtSlot(int) def setreproduce(self,_): self.aw.qmc.detectBackgroundEventTime = self.etimeSpinBox.value() s = None if self.backgroundReproduce.isChecked(): self.aw.qmc.backgroundReproduce = True msg = QApplication.translate("Message","Playback Aid set ON at {0} secs",None).format(str(self.aw.qmc.detectBackgroundEventTime)) else: self.aw.qmc.backgroundReproduce = False msg = QApplication.translate("StatusBar","Playback Aid set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) def adjustcolor(self,curve): curve = str(curve).lower() etcolor = str(self.metcolorComboBox.currentText()).lower() btcolor = str(self.btcolorComboBox.currentText()).lower() deltabtcolor = str(self.deltabtcolorComboBox.currentText()).lower() deltaetcolor = str(self.deltaetcolorComboBox.currentText()).lower() xtcolor = str(self.xtcolorComboBox.currentText()).lower() defaults = ["et","bt","deltaet","deltabt"] if curve == "et": if etcolor in defaults: self.aw.qmc.backgroundmetcolor = self.aw.qmc.palette[etcolor] else: self.aw.qmc.backgroundmetcolor = etcolor elif curve == "bt": if btcolor in defaults: self.aw.qmc.backgroundbtcolor = self.aw.qmc.palette[btcolor] else: self.aw.qmc.backgroundbtcolor = btcolor elif curve == "deltaet": if deltaetcolor in defaults: self.aw.qmc.backgrounddeltaetcolor = self.aw.qmc.palette[deltaetcolor] else: self.aw.qmc.backgrounddeltaetcolor = deltaetcolor elif curve == "deltabt": if deltabtcolor in defaults: self.aw.qmc.backgrounddeltabtcolor = self.aw.qmc.palette[deltabtcolor] else: self.aw.qmc.backgrounddeltabtcolor = deltabtcolor elif curve == "xt": if xtcolor in defaults: self.aw.qmc.backgroundxtcolor = self.aw.qmc.palette[xtcolor] else: self.aw.qmc.backgroundxtcolor = xtcolor self.aw.qmc.redraw(recomputeAllDeltas=False) @pyqtSlot(bool) def delete(self,_): self.pathedit.setText("") # we should not overwrite the users app settings here, right: # but we have to deactivate the show flag self.backgroundCheck.setChecked(False) self.aw.qmc.background = False self.aw.qmc.backgroundprofile = None self.xtcurveComboBox.blockSignals(True) self.xtcurveComboBox.clear() self.aw.deleteBackground() self.eventtable.clear() self.createEventTable() self.createDataTable() self.aw.qmc.resetlinecountcaches() self.xtcurveComboBox.blockSignals(False) self.aw.qmc.redraw(recomputeAllDeltas=False) @pyqtSlot(bool) def moveUp(self,_): self.upButton.setDisabled(True) self.move("up") self.upButton.setDisabled(False) @pyqtSlot(bool) def moveDown(self,_): self.downButton.setDisabled(True) self.move("down") self.downButton.setDisabled(False) @pyqtSlot(bool) def moveLeft(self,_): self.leftButton.setDisabled(True) self.move("left") self.leftButton.setDisabled(False) @pyqtSlot(bool) def moveRight(self,_): self.rightButton.setDisabled(True) self.move("right") self.rightButton.setDisabled(False) def move(self,m): step = self.speedSpinBox.value() self.aw.qmc.movebackground(m,step) self.createEventTable() self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False) def readChecks(self): self.aw.qmc.background = bool(self.backgroundCheck.isChecked()) self.aw.qmc.backgroundDetails = bool(self.backgroundDetails.isChecked()) self.aw.qmc.backgroundeventsflag = bool(self.backgroundeventsflag.isChecked()) self.aw.qmc.DeltaETBflag = bool(self.backgroundDeltaETflag.isChecked()) self.aw.qmc.DeltaBTBflag = bool(self.backgroundDeltaBTflag.isChecked()) self.aw.qmc.backgroundETcurve = bool(self.backgroundETflag.isChecked()) self.aw.qmc.backgroundBTcurve = bool(self.backgroundBTflag.isChecked()) self.aw.qmc.backgroundShowFullflag = bool(self.backgroundFullflag.isChecked()) self.aw.qmc.redraw(recomputeAllDeltas=True) @pyqtSlot(int) def changeAlignEventidx(self,i): self.aw.qmc.alignEvent = i @pyqtSlot(int) def changeReplayTypeidx(self,i): self.aw.qmc.replayType = i @pyqtSlot(int) def changeXTcurveidx(self,i): self.aw.qmc.xtcurveidx = i self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False,smooth=True) @pyqtSlot(int) def changeYTcurveidx(self,i): self.aw.qmc.ytcurveidx = i self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False,smooth=True) @pyqtSlot(bool) def load(self,_): self.filename = self.aw.ArtisanOpenFileDialog(msg=QApplication.translate("Message","Load Background",None),ext_alt=".alog") if len(self.filename) == 0: return self.aw.sendmessage(QApplication.translate("Message","Reading background profile...",None)) self.aw.qmc.resetlinecountcaches() self.aw.loadbackground(self.filename) # reset XT curve popup curvenames = [""] # first entry is the empty one (no extra curve displayed) for i in range(min(len(self.aw.qmc.extraname1B),len(self.aw.qmc.extraname2B),len(self.aw.qmc.extratimexB))): curvenames.append("B" + str(2*i+3) + ": " + self.aw.qmc.extraname1B[i]) curvenames.append("B" + str(2*i+4) + ": " + self.aw.qmc.extraname2B[i]) self.xtcurveComboBox.blockSignals(True) self.xtcurveComboBox.clear() self.xtcurveComboBox.addItems(curvenames) if self.aw.qmc.xtcurveidx < len(curvenames): self.xtcurveComboBox.setCurrentIndex(self.aw.qmc.xtcurveidx) self.xtcurveComboBox.blockSignals(False) self.ytcurveComboBox.blockSignals(True) self.ytcurveComboBox.clear() self.ytcurveComboBox.addItems(curvenames) if self.aw.qmc.ytcurveidx < len(curvenames): self.ytcurveComboBox.setCurrentIndex(self.aw.qmc.ytcurveidx) self.ytcurveComboBox.blockSignals(False) self.pathedit.setText(self.filename) self.backgroundCheck.setChecked(True) self.aw.qmc.timealign(redraw=False) self.readChecks() self.createEventTable() self.createDataTable() def createEventTable(self): ndata = len(self.aw.qmc.backgroundEvents) # self.eventtable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.eventtable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.eventtable.clearSelection() # this seems to work also for Ubuntu 16.04 self.eventtable.setRowCount(ndata) self.eventtable.setColumnCount(6) self.eventtable.setHorizontalHeaderLabels([QApplication.translate("Table","Time",None), QApplication.translate("Table", "ET", None), QApplication.translate("Table", "BT", None), QApplication.translate("Table","Description",None), QApplication.translate("Table","Type",None), QApplication.translate("Table","Value",None)]) self.eventtable.setAlternatingRowColors(True) self.eventtable.setEditTriggers(QTableWidget.NoEditTriggers) self.eventtable.setSelectionBehavior(QTableWidget.SelectRows) self.eventtable.setSelectionMode(QTableWidget.ExtendedSelection) self.eventtable.setShowGrid(True) self.eventtable.verticalHeader().setSectionResizeMode(2) if self.aw.qmc.timeindex[0] != -1: start = self.aw.qmc.timex[self.aw.qmc.timeindex[0]] else: start = 0 for i in range(ndata): timez = QTableWidgetItem(stringfromseconds(self.aw.qmc.timeB[self.aw.qmc.backgroundEvents[i]]-start)) timez.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) if self.aw.qmc.LCDdecimalplaces: fmtstr = "%.1f" else: fmtstr = "%.0f" etline = QTableWidgetItem(fmtstr%(self.aw.qmc.temp1B[self.aw.qmc.backgroundEvents[i]]) + self.aw.qmc.mode) etline.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) btline = QTableWidgetItem(fmtstr%(self.aw.qmc.temp2B[self.aw.qmc.backgroundEvents[i]]) + self.aw.qmc.mode) btline.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) description = QTableWidgetItem(self.aw.qmc.backgroundEStrings[i]) etype = QTableWidgetItem(self.aw.qmc.Betypesf(self.aw.qmc.backgroundEtypes[i])) evalue = QTableWidgetItem(self.aw.qmc.eventsvalues(self.aw.qmc.backgroundEvalues[i])) evalue.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) #add widgets to the table self.eventtable.setItem(i,0,timez) self.eventtable.setItem(i,1,etline) self.eventtable.setItem(i,2,btline) self.eventtable.setItem(i,3,description) self.eventtable.setItem(i,4,etype) self.eventtable.setItem(i,5,evalue) # improve width of Time column self.eventtable.setColumnWidth(1,175) header = self.eventtable.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(1, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.Fixed) header.setSectionResizeMode(3, QHeaderView.Stretch) header.setSectionResizeMode(4, QHeaderView.Fixed) header.setSectionResizeMode(5, QHeaderView.Fixed) self.eventtable.resizeColumnsToContents() self.eventtable.setColumnWidth(1,65) self.eventtable.setColumnWidth(2,65) def createDataTable(self): try: #### lock shared resources ##### self.aw.qmc.samplingsemaphore.acquire(1) ndata = len(self.aw.qmc.timeB) self.datatable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.datatable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.datatable.clearSelection() # this seems to work also for Ubuntu 16.04 if self.aw.qmc.timeindexB[0] != -1 and len(self.aw.qmc.timeB) > self.aw.qmc.timeindexB[0]: start = self.aw.qmc.timeB[self.aw.qmc.timeindexB[0]] else: start = 0 self.datatable.setRowCount(ndata) headers = [QApplication.translate("Table","Time",None), QApplication.translate("Table","ET",None), QApplication.translate("Table","BT",None), deltaLabelUTF8 + QApplication.translate("Table","ET",None), deltaLabelUTF8 + QApplication.translate("Table","BT",None)] xtcurve = False # no XT curve if self.aw.qmc.xtcurveidx > 0: # 3rd background curve set? idx3 = self.aw.qmc.xtcurveidx - 1 n3 = idx3 // 2 if len(self.aw.qmc.temp1BX) > n3 and len(self.aw.qmc.extratimexB) > n3: xtcurve = True if self.aw.qmc.xtcurveidx % 2: headers.append(self.aw.qmc.extraname1B[n3]) else: headers.append(self.aw.qmc.extraname2B[n3]) ytcurve = False # no YT curve if self.aw.qmc.ytcurveidx > 0: # 4th background curve set? idx4 = self.aw.qmc.ytcurveidx - 1 n4 = idx4 // 2 if len(self.aw.qmc.temp1BX) > n4 and len(self.aw.qmc.extratimexB) > n4: ytcurve = True if self.aw.qmc.ytcurveidx % 2: headers.append(self.aw.qmc.extraname1B[n4]) else: headers.append(self.aw.qmc.extraname2B[n4]) headers.append("") # dummy column that stretches self.datatable.setColumnCount(len(headers)) self.datatable.setHorizontalHeaderLabels(headers) self.datatable.setAlternatingRowColors(True) self.datatable.setEditTriggers(QTableWidget.NoEditTriggers) self.datatable.setSelectionBehavior(QTableWidget.SelectRows) self.datatable.setSelectionMode(QTableWidget.ExtendedSelection) # QTableWidget.SingleSelection, ContiguousSelection, MultiSelection self.datatable.setShowGrid(True) self.datatable.verticalHeader().setSectionResizeMode(2) for i in range(ndata): Rtime = QTableWidgetItem(stringfromseconds(self.aw.qmc.timeB[i]-start)) Rtime.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if self.aw.qmc.LCDdecimalplaces: fmtstr = "%.1f" else: fmtstr = "%.0f" ET = QTableWidgetItem(fmtstr%self.aw.qmc.temp1B[i]) BT = QTableWidgetItem(fmtstr%self.aw.qmc.temp2B[i]) ET.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) BT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if i: d = (self.aw.qmc.timeB[i]-self.aw.qmc.timeB[i-1]) if d == 0: dET = 0. dBT = 0. else: dET = (60*(self.aw.qmc.temp1B[i]-self.aw.qmc.temp1B[i-1])/d) dBT = (60*(self.aw.qmc.temp2B[i]-self.aw.qmc.temp2B[i-1])/d) deltaET = QTableWidgetItem("%.1f"%dET) deltaBT = QTableWidgetItem("%.1f"%dBT) else: deltaET = QTableWidgetItem("--") deltaBT = QTableWidgetItem("--") deltaET.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) deltaBT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) self.datatable.setItem(i,0,Rtime) if i: #identify by color and add notation if i == self.aw.qmc.timeindexB[0] != -1: self.datatable.item(i,0).setBackground(QColor('#f07800')) text = QApplication.translate("Table", "CHARGE",None) elif i == self.aw.qmc.timeindexB[1]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "DRY END",None) elif i == self.aw.qmc.timeindexB[2]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "FC START",None) elif i == self.aw.qmc.timeindexB[3]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "FC END",None) elif i == self.aw.qmc.timeindexB[4]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "SC START",None) elif i == self.aw.qmc.timeindexB[5]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "SC END",None) elif i == self.aw.qmc.timeindexB[6]: self.datatable.item(i,0).setBackground(QColor('#f07800')) text = QApplication.translate("Table", "DROP",None) elif i == self.aw.qmc.timeindexB[7]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "COOL",None) elif i in self.aw.qmc.backgroundEvents: self.datatable.item(i,0).setBackground(QColor('yellow')) index = self.aw.qmc.backgroundEvents.index(i) text = QApplication.translate("Table", "#{0} {1}{2}",None).format(str(index+1),self.aw.qmc.Betypesf(self.aw.qmc.backgroundEtypes[index])[0],self.aw.qmc.eventsvalues(self.aw.qmc.backgroundEvalues[index])) else: text = "" Rtime.setText(text + " " + Rtime.text()) self.datatable.setItem(i,1,ET) self.datatable.setItem(i,2,BT) self.datatable.setItem(i,3,deltaET) self.datatable.setItem(i,4,deltaBT) if xtcurve and len(self.aw.qmc.temp1BX[n3]) > i: # an XT column is availble, fill it with data if self.aw.qmc.xtcurveidx % 2: XT = QTableWidgetItem("%.0f"%self.aw.qmc.temp1BX[n3][i]) else: XT = QTableWidgetItem("%.0f"%self.aw.qmc.temp2BX[n3][i]) XT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) self.datatable.setItem(i,5,XT) if ytcurve and len(self.aw.qmc.temp1BX[n4]) > i: # an YT column is availble, fill it with data if self.aw.qmc.ytcurveidx % 2: YT = QTableWidgetItem("%.0f"%self.aw.qmc.temp1BX[n4][i]) else: YT = QTableWidgetItem("%.0f"%self.aw.qmc.temp2BX[n4][i]) YT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if xtcurve: self.datatable.setItem(i,6,YT) else: self.datatable.setItem(i,5,YT) header = self.datatable.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(1, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.Fixed) header.setSectionResizeMode(3, QHeaderView.Fixed) header.setSectionResizeMode(4, QHeaderView.Fixed) if (xtcurve and not ytcurve) or (ytcurve and not xtcurve): header.setSectionResizeMode(5, QHeaderView.Fixed) header.setSectionResizeMode(6, QHeaderView.Stretch) elif xtcurve and ytcurve: header.setSectionResizeMode(5, QHeaderView.Fixed) header.setSectionResizeMode(6, QHeaderView.Fixed) header.setSectionResizeMode(7, QHeaderView.Stretch) else: header.setSectionResizeMode(5, QHeaderView.Stretch) self.datatable.resizeColumnsToContents() finally: if self.aw.qmc.samplingsemaphore.available() < 1: self.aw.qmc.samplingsemaphore.release(1) @pyqtSlot(bool) def copyDataTabletoClipboard(self,_=False): self.datatable.selectAll() self.aw.copy_cells_to_clipboard(self.datatable,adjustment=7) self.datatable.clearSelection() self.aw.sendmessage(QApplication.translate("Message","Data table copied to clipboard",None)) @pyqtSlot(bool) def copyEventTabletoClipboard(self,_=False): self.aw.copy_cells_to_clipboard(self.eventtable,adjustment=0) self.aw.sendmessage(QApplication.translate("Message","Event table copied to clipboard",None))
class Widgets(QWidget): def __init__(self, parent=None): super(Widgets, self).__init__(parent) self.initUI() def initUI(self): fuenteSiacle = self.font() fuenteSiacle.setBold(True) fuenteSiacle.setPointSize(12) # ================= FRAME PANEL DE CONTROL =================== paletaBotones = self.palette() paletaBotones.setColor(QPalette.Background, QColor("#2EFEC8")) frameBotones = QFrame() frameBotones.setFrameStyle(QFrame.NoFrame) frameBotones.setAutoFillBackground(True) frameBotones.setPalette(paletaBotones) frameBotones.setFixedWidth(220) # ============================================================ paletaPanel = self.palette() paletaPanel.setBrush(QPalette.Background, QBrush(QColor(255, 90, 0), Qt.SolidPattern)) paletaPanel.setColor(QPalette.Foreground, Qt.white) labelSiacle = QLabel("PANEL DE CONTROL", frameBotones) labelSiacle.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) labelSiacle.setFont(fuenteSiacle) labelSiacle.setAutoFillBackground(True) labelSiacle.setPalette(paletaPanel) labelSiacle.setFixedSize(220, 46) labelSiacle.move(0, 0) # ============================================================ tamanioIcono = QSize(30, 30) botonNuevo = Boton(frameBotones) botonNuevo.setText(" Nuevo") botonNuevo.setToolTip("Nuevo cliente") botonNuevo.setCursor(Qt.PointingHandCursor) botonNuevo.move(-2, 45) botonActualizar = Boton(frameBotones) botonActualizar.setText(" Actualizar") botonActualizar.setToolTip("Actualizar cliente") botonActualizar.setCursor(Qt.PointingHandCursor) botonActualizar.move(-2, 82) botonEliminar = Boton(frameBotones) botonEliminar.setText(" Eliminar") botonEliminar.setToolTip("Eliminar cliente") botonEliminar.setCursor(Qt.PointingHandCursor) botonEliminar.move(-2, 119) botonLimpiar = Boton(frameBotones) botonLimpiar.setText(" Limpiar tabla") botonLimpiar.setToolTip("Limpiar tabla") botonLimpiar.setCursor(Qt.PointingHandCursor) botonLimpiar.move(-2, 156) # ============================================================ paletaSuscribete = self.palette() paletaSuscribete.setBrush(QPalette.Background, QBrush(QColor(135, 206, 250), Qt.SolidPattern)) fuenteSuscribete = self.font() fuenteSuscribete.setBold(True) fuenteSuscribete.setFamily("Arial") fuenteSuscribete.setPointSize(11) labelSuscribete = QLabel("<a href='https://www.youtube.com/c/AndresNiñoPython?" "sub_confirmation=1'>SUSCRIBETE.</a>", frameBotones) labelSuscribete.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) labelSuscribete.setOpenExternalLinks(True) labelSuscribete.setFont(fuenteSuscribete) labelSuscribete.setAutoFillBackground(True) labelSuscribete.setPalette(paletaSuscribete) labelSuscribete.setFixedSize(220, 46) labelSuscribete.move(0, 210) # ============ FRAME BIENVENIDO - CONFIGURACIÓN ============== paletaFrame = self.palette() paletaFrame.setColor(QPalette.Background, QColor("blue")) frameBienvenido = QFrame() frameBienvenido.setFrameStyle(QFrame.NoFrame) frameBienvenido.setAutoFillBackground(True) frameBienvenido.setPalette(paletaFrame) frameBienvenido.setFixedHeight(46) # ============================================================ paletaTitulo = self.palette() paletaTitulo.setColor(QPalette.Foreground, Qt.yellow) labelBienvenido = QLabel("BIENVENIDO A SIACLE") labelBienvenido.setAlignment(Qt.AlignCenter) labelBienvenido.setFont(fuenteSiacle) labelBienvenido.setPalette(paletaTitulo) botonConfiguracion = QPushButton() botonConfiguracion.setIcon(QIcon("Imagenes/configuracion.png")) botonConfiguracion.setIconSize(QSize(24, 24)) botonConfiguracion.setToolTip("Configurar Siacle") botonConfiguracion.setCursor(Qt.PointingHandCursor) botonConfiguracion.setFixedWidth(36) disenioFrame = QHBoxLayout() disenioFrame.addWidget(labelBienvenido, Qt.AlignCenter) disenioFrame.addStretch() disenioFrame.addWidget(botonConfiguracion) disenioFrame.setContentsMargins(0, 0, 5, 0) frameBienvenido.setLayout(disenioFrame) # ============================================================ self.buscarLineEdit = QLineEdit() self.buscarLineEdit.setObjectName("Enter") self.buscarLineEdit.setPlaceholderText("Nombre del cliente") self.buscarLineEdit.setMinimumSize(200, 26) botonBuscar = QPushButton("Buscar") botonBuscar.setObjectName("Buscar") botonBuscar.setCursor(Qt.PointingHandCursor) botonBuscar.setMinimumSize(60, 26) separadorTodos = QFrame() separadorTodos.setFrameShape(QFrame.VLine) separadorTodos.setFrameShadow(QFrame.Raised) separadorTodos.setFixedSize(1, 26) botonTodos = QPushButton("Todos") botonTodos.setObjectName("Todos") botonTodos.setCursor(Qt.PointingHandCursor) botonTodos.setMinimumSize(60, 26) nombreColumnas = ("Id", "Nombre", "Apellido", "Sexo", "Fecha de nacimiento", "País", "Teléfono o celular") menuMostrarOcultar = QMenu() for indice, columna in enumerate(nombreColumnas, start=0): accion = QAction(columna, menuMostrarOcultar) accion.setCheckable(True) accion.setChecked(True) accion.setData(indice) menuMostrarOcultar.addAction(accion) botonMostrarOcultar = QPushButton("Motrar/ocultar columnas") botonMostrarOcultar.setCursor(Qt.PointingHandCursor) botonMostrarOcultar.setMenu(menuMostrarOcultar) botonMostrarOcultar.setMinimumSize(180, 26) disenioBuscar = QHBoxLayout() disenioBuscar.setSpacing(10) disenioBuscar.addWidget(self.buscarLineEdit) disenioBuscar.addWidget(botonBuscar) disenioBuscar.addWidget(separadorTodos) disenioBuscar.addWidget(botonTodos) disenioBuscar.addWidget(botonMostrarOcultar) # =================== WIDGET QTableWidget =================== self.tabla = QTableWidget() # Deshabilitar edición self.tabla.setEditTriggers(QAbstractItemView.NoEditTriggers) # Deshabilitar el comportamiento de arrastrar y soltar self.tabla.setDragDropOverwriteMode(False) # Seleccionar toda la fila self.tabla.setSelectionBehavior(QAbstractItemView.SelectRows) # Seleccionar una fila a la vez self.tabla.setSelectionMode(QAbstractItemView.SingleSelection) # Especifica dónde deben aparecer los puntos suspensivos "..." cuando se muestran # textos que no encajan self.tabla.setTextElideMode(Qt.ElideRight)# Qt.ElideNone # Establecer el ajuste de palabras del texto self.tabla.setWordWrap(False) # Deshabilitar clasificación self.tabla.setSortingEnabled(False) # Establecer el número de columnas self.tabla.setColumnCount(7) # Establecer el número de filas self.tabla.setRowCount(0) # Alineación del texto del encabezado self.tabla.horizontalHeader().setDefaultAlignment(Qt.AlignHCenter|Qt.AlignVCenter| Qt.AlignCenter) # Deshabilitar resaltado del texto del encabezado al seleccionar una fila self.tabla.horizontalHeader().setHighlightSections(False) # Hacer que la última sección visible del encabezado ocupa todo el espacio disponible self.tabla.horizontalHeader().setStretchLastSection(True) # Ocultar encabezado vertical self.tabla.verticalHeader().setVisible(False) # Dibujar el fondo usando colores alternados self.tabla.setAlternatingRowColors(True) # Establecer altura de las filas self.tabla.verticalHeader().setDefaultSectionSize(20) # self.tabla.verticalHeader().setHighlightSections(True) # Establecer las etiquetas de encabezado horizontal usando etiquetas self.tabla.setHorizontalHeaderLabels(nombreColumnas) # Menú contextual self.tabla.setContextMenuPolicy(Qt.CustomContextMenu) self.tabla.customContextMenuRequested.connect(self.menuContextual) # Establecer ancho de las columnas for indice, ancho in enumerate((80, 240, 240, 140, 150, 130), start=0): self.tabla.setColumnWidth(indice, ancho) # ============================================================ disenioBuscarTabla = QVBoxLayout() disenioBuscarTabla.addLayout(disenioBuscar) disenioBuscarTabla.addWidget(self.tabla) disenioBuscarTabla.setSpacing(8) disenioBuscarTabla.setContentsMargins(10, 10, 10, 0) # ===================== LAYOUT DERECHO ======================= disenioDerecho = QVBoxLayout() disenioDerecho.addWidget(frameBienvenido) disenioDerecho.addLayout(disenioBuscarTabla) disenioDerecho.setContentsMargins(0, 0, 0, 0) # ====================== LAYOUT FINAL ====================== disenioFinal = QGridLayout() disenioFinal.addWidget(frameBotones, 0, 0) disenioFinal.addLayout(disenioDerecho, 0, 1) disenioFinal.setSpacing(0) disenioFinal.setContentsMargins(0, 0, 0, 0) self.setLayout(disenioFinal) # ========= GUARDAR INFORMACIÓN EN EL PORTAPAPELES ========= self.copiarInformacion = QApplication.clipboard() # ======================== EVENTOS ========================= botonNuevo.clicked.connect(self.Nuevo) botonActualizar.clicked.connect(self.Actualizar) botonEliminar.clicked.connect(self.Eliminar) botonLimpiar.clicked.connect(self.limpiarTabla) self.buscarLineEdit.returnPressed.connect(self.Buscar) botonBuscar.clicked.connect(self.Buscar) botonTodos.clicked.connect(self.Buscar) botonConfiguracion.clicked.connect(lambda: Configuracion(self).exec_()) self.tabla.itemDoubleClicked.connect(self.Suscribete) menuMostrarOcultar.triggered.connect(self.mostrarOcultar) # ======================= FUNCIONES ============================ def Nuevo(self): nuevoCliente(self).exec_() def Actualizar(self): fila = self.tabla.selectedItems() if fila: indice = fila[0].row() datos = [self.tabla.item(indice, i).text() for i in range(7)] actualizarCliente(indice, datos, self).exec_() else: QMessageBox.critical(self, "Actualizar cliente", "Seleccione un cliente. ", QMessageBox.Ok) def Eliminar(self): fila = self.tabla.selectedItems() if fila: eliminar = QMessageBox(self) eliminar.setWindowTitle("Eliminar cliente") eliminar.setIcon(QMessageBox.Question) eliminar.setText("¿Esta seguro que desea eliminar el cliente? ") botonSi = eliminar.addButton("Si", QMessageBox.YesRole) botonCancelar = eliminar.addButton("Cancelar", QMessageBox.NoRole) eliminar.exec_() if eliminar.clickedButton() == botonSi: indiceFila = fila[0].row() idCliente = self.tabla.item(indiceFila, 0).text() if QFile.exists("DB_SIACLE/DB_SIACLE.db"): conexion = sqlite3.connect("DB_SIACLE/DB_SIACLE.db") cursor = conexion.cursor() try: cursor.execute("DELETE FROM CLIENTES WHERE ID = ?", (idCliente,)) conexion.commit() conexion.close() self.tabla.removeRow(indiceFila) self.tabla.clearSelection() QMessageBox.information(self, "Eliminar cliente", "Cliente eliminado." " ", QMessageBox.Ok) except: conexion.close() QMessageBox.critical(self, "Eliminar cliente", "Error desconocido. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Buscar clientes", "No se encontro la base de " "datos. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Eliminar cliente", "Seleccione un cliente. ", QMessageBox.Ok) def Suscribete(self, celda): QMessageBox.warning(self, "Suscribirse", "Hola, l@ invito a que se suscriba al " "canal.\nPor cierto hiciste doble clic sobre esta " "celda: {}. ".format(celda.text()), QMessageBox.Ok) def Buscar(self): widget = self.sender().objectName() if widget in ("Enter", "Buscar"): cliente = " ".join(self.buscarLineEdit.text().split()).lower() if cliente: sql = "SELECT * FROM CLIENTES WHERE NOMBRE LIKE ?", ("%"+cliente+"%",) else: self.buscarLineEdit.setFocus() return else: self.buscarLineEdit.clear() sql = "SELECT * FROM CLIENTES" if QFile.exists("DB_SIACLE/DB_SIACLE.db"): conexion = sqlite3.connect("DB_SIACLE/DB_SIACLE.db") cursor = conexion.cursor() try: if widget in ("Enter", "Buscar"): cursor.execute(sql[0], sql[1]) else: cursor.execute(sql) datosDevueltos = cursor.fetchall() conexion.close() self.tabla.clearContents() self.tabla.setRowCount(0) if datosDevueltos: fila = 0 for datos in datosDevueltos: self.tabla.setRowCount(fila + 1) idDato = QTableWidgetItem(str(datos[0])) idDato.setTextAlignment(Qt.AlignCenter) self.tabla.setItem(fila, 0, idDato) self.tabla.setItem(fila, 1, QTableWidgetItem(datos[1])) self.tabla.setItem(fila, 2, QTableWidgetItem(datos[2])) self.tabla.setItem(fila, 3, QTableWidgetItem(datos[3])) self.tabla.setItem(fila, 4, QTableWidgetItem(datos[4])) self.tabla.setItem(fila, 5, QTableWidgetItem(datos[5])) self.tabla.setItem(fila, 6, QTableWidgetItem(datos[6])) fila += 1 else: QMessageBox.information(self, "Buscar cliente", "No se encontro " "información. ", QMessageBox.Ok) except: conexion.close() QMessageBox.critical(self, "Buscar clientes", "Error desconocido. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Buscar clientes", "No se encontro la base de datos. ", QMessageBox.Ok) self.buscarLineEdit.setFocus() def mostrarOcultar(self, accion): columna = accion.data() if accion.isChecked(): self.tabla.setColumnHidden(columna, False) else: self.tabla.setColumnHidden(columna, True) def limpiarTabla(self): self.tabla.clearContents() self.tabla.setRowCount(0) def menuContextual(self, posicion): indices = self.tabla.selectedIndexes() if indices: menu = QMenu() itemsGrupo = QActionGroup(self) itemsGrupo.setExclusive(True) menu.addAction(QAction("Copiar todo", itemsGrupo)) columnas = [self.tabla.horizontalHeaderItem(columna).text() for columna in range(self.tabla.columnCount()) if not self.tabla.isColumnHidden(columna)] copiarIndividual = menu.addMenu("Copiar individual") for indice, item in enumerate(columnas, start=0): accion = QAction(item, itemsGrupo) accion.setData(indice) copiarIndividual.addAction(accion) itemsGrupo.triggered.connect(self.copiarTableWidgetItem) menu.exec(self.tabla.viewport().mapToGlobal(posicion)) def copiarTableWidgetItem(self, accion): filaSeleccionada = [dato.text() for dato in self.tabla.selectedItems()] if accion.text() == "Copiar todo": filaSeleccionada = tuple(filaSeleccionada) else: filaSeleccionada = filaSeleccionada[accion.data()] self.copiarInformacion.clear(mode = QClipboard.Clipboard) self.copiarInformacion.setText(str(filaSeleccionada), QClipboard.Clipboard)
class App(QWidget): """Display window for the main game and start the timer. Display options to: 1. Select letters on the table 2. Pause/Resume the game 3. Quit the game Attributes: wordBank: A string of the words to find in the word search. wordBankSplit: A list of strings of words to find in the word search. wordSelected: A string to hold the current word selected. xVisited: A list of integers to hold the current row values of the letters selected. yVisited: A list of integers to hold the current column values of the letters selected. inRow: An integer to determine if the letters selected are in consecutive fashion. progressValue: An integer to keep track of the words found. wordsCompleted: A list of strings of the words found. timeFlag: A time flag to keep track of the timer if the game has been paused or resumed. """ def __init__(self): """Initiate initUI.""" super().__init__() self.wordBank = "" self.wordBankSplit = [] self.wordSelected = "" self.xVisited = [] self.yVisited = [] self.inRow = 0 self.progressValue = 0 self.wordsCompleted = [] self.timeFlag = 2 self.initUI() def initUI(self): """Initiate UI elements.""" title = 'Word Search Mania' self.setWindowTitle(title) self.wordBankBox = QTextEdit() self.tableWidget = QTableWidget() self.progress = QProgressBar() self.timer = PyQt5.QtCore.QTimer() self.createTable() self.createWordBank() self.createProgressBar() self.createTimer() self.mouseTracking() wordBankTitle = QLabel() wordBankTitle.setText(" Word Bank") font = QFont() font.setBold(True) wordBankTitle.setFont(font) buttonClear = QPushButton('Clear', self) buttonClear.setToolTip('This clears your word selection.') buttonClear.clicked.connect(self.onClickClear) buttonQuit = QPushButton('Quit', self) buttonQuit.setToolTip( 'This will buttonQuit your game. You will loose all progress.') buttonQuit.clicked.connect(self.onClickQuit) self.buttonPause = QPushButton('Pause') self.buttonPause.setToolTip('This pauses the game.') self.buttonPause.clicked.connect(self.onClickPause) vBox = QVBoxLayout() vBox.addWidget(wordBankTitle) vBox.addWidget(self.wordBankBox) vBox.addWidget(buttonClear) vBox.addWidget(self.buttonPause) vBox.addWidget(buttonQuit) self.grid = QGridLayout() self.grid.addLayout(vBox, 0, 1) self.grid.addWidget(self.tableWidget, 0, 0) self.grid.addWidget(self.progress, 1, 0) self.grid.addWidget(self.LCD, 1, 1) self.setLayout(self.grid) self.tableWidget.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContents) self.show() def createTable(self): """Generate the word search table.""" generateAll = False global wordBoxChecked global rowBoxChecked global columnBoxChecked global diagonalBoxChecked if wordBoxChecked: f = open('custom_word_bank.txt', "r") wordBoxChecked = False else: f = open('words_alpha.txt', "r") generateAll = True wordFileContent = f.readlines() wordFileContent = [x.strip() for x in wordFileContent] self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget.setRowCount(nElements) self.tableWidget.setColumnCount(nElements) # Populate table with random letters for y in range(0, nElements): for x in range(0, nElements): self.tableWidget.setItem( x, y, QTableWidgetItem(random.choice(string.ascii_uppercase))) self.tableWidget.setColumnWidth(x, 20) self.tableWidget.setRowHeight(y, 20) # Implements words across rows def generateRow(self): col = 0 row = 0 lastColPosition = 0 wordDuplicate = False while row < nElements: while col < nElements: col = random.randint(lastColPosition, nElements) word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] while len(word) < 3: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] for x in self.wordBank.split(): if x == word: wordDuplicate = True if not wordDuplicate: if nElements - col > len(word): lastColPosition = len(word) + col self.wordBank += word + "\n" for x in word: self.tableWidget.setItem( row, col, QTableWidgetItem(x)) col += 1 col = nElements wordDuplicate = False row += 3 col = 0 lastColPosition = 0 # Implements words down each column def generateCol(self): col = 0 row = 0 lastRowPosition = 0 decide = 0 wordDuplicate = False while col < nElements: while row < nElements: row = random.randint(lastRowPosition, nElements) word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] while len(word) < 3: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] for x in self.wordBank.split(): if x == word: wordDuplicate = True if not wordDuplicate: if nElements - row > len(word): for k in range(row, row + len(word)): if self.tableWidget.item(k, col).text().islower(): decide += 1 if decide == 0: lastRowPosition = len(word) + row self.wordBank += word + "\n" for y in word: self.tableWidget.setItem( row, col, QTableWidgetItem(y)) row += 1 decide = 0 wordDuplicate = False col += 3 row = 0 lastRowPosition = 0 # Implements words down each diagonal in forward def generateForwardDiag(self): col = 0 row = 0 wordCount = 0 decide = 0 wordDuplicate = False while row < nElements: while col < nElements: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] while len(word) < 3: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] for x in self.wordBank.split(): if x == word: wordDuplicate = True if not wordDuplicate: tempRow = row tempCol = col while tempRow < nElements and tempCol < nElements and wordCount < len( word): if self.tableWidget.item(tempRow, tempCol).text().islower(): decide += 1 tempRow += 1 tempCol += 1 wordCount += 1 tempRow = row tempCol = col if decide == 0 and ( len(word) + tempCol) < nElements and ( len(word) + tempRow) < nElements: self.wordBank += word + "\n" for y in word: self.tableWidget.setItem( tempRow, tempCol, QTableWidgetItem(y)) tempCol += 1 tempRow += 1 decide = 0 wordCount = 0 col += 1 wordDuplicate = False row += 1 col = 0 # Implements words down each diagonal in backward def generateBackwardDiag(self): col = nElements - 1 row = 0 wordCount = 0 decide = 0 wordDuplicate = False while row < nElements: while col >= 0: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] while len(word) < 3: word = wordFileContent[random.randint( 0, len(wordFileContent) - 1)] for x in self.wordBank.split(): if x == word: wordDuplicate = True if not wordDuplicate: tempRow = row tempCol = col while tempRow < nElements and tempCol >= 0 and wordCount < len( word): if self.tableWidget.item(tempRow, tempCol).text().islower(): decide += 1 tempRow += 1 tempCol -= 1 wordCount += 1 tempRow = row tempCol = col if decide == 0 and (tempCol - len(word)) > 0 and ( len(word) + tempRow) < nElements: self.wordBank += word + "\n" for y in word: self.tableWidget.setItem( tempRow, tempCol, QTableWidgetItem(y)) tempRow += 1 tempCol -= 1 decide = 0 wordCount = 0 col -= 1 wordDuplicate = False row += 1 col = nElements - 1 if generateAll: generateRow(self) generateCol(self) generateForwardDiag(self) generateBackwardDiag(self) else: if rowBoxChecked: generateRow(self) rowBoxChecked = False if columnBoxChecked: generateCol(self) columnBoxChecked = False if diagonalBoxChecked: generateForwardDiag(self) generateBackwardDiag(self) diagonalBoxChecked = False for y in range(0, nElements): for x in range(0, nElements): letter = self.tableWidget.item(x, y).text().lower() self.tableWidget.setItem(x, y, QTableWidgetItem(letter)) self.tableWidget.item(x, y).setTextAlignment( PyQt5.QtCore.Qt.AlignCenter) self.tableWidget.horizontalHeader().hide() self.tableWidget.verticalHeader().hide() self.tableWidget.setShowGrid(False) self.tableWidget.clicked.connect(self.onClickLetter) def createWordBank(self): """Generate a word bank of the words to be found.""" self.wordBankSplit = self.wordBank.split() self.wordBankSplit.sort() for x in self.wordBankSplit: self.wordBankBox.append(x) self.wordBankBox.setReadOnly(True) self.wordBankBox.setMaximumWidth(120) font = QFont() font.setFamily('Arial') self.wordBankBox.setFont(font) self.wordBankBox.moveCursor(QTextCursor.Start) def strikeWord(self, word): """Strike word with a line if the word is found.""" newWord = "" for x in word: newWord += x + '\u0336' self.wordBankSplit = [ newWord if i == word else i for i in self.wordBankSplit ] self.wordBankBox.setText("") for x in self.wordBankSplit: self.wordBankBox.append(x) self.wordBankBox.show() self.wordBankBox.moveCursor(QTextCursor.Start) def mouseTracking(self): """Track mouse movement of the table.""" self.currentHover = [0, 0] self.tableWidget.setMouseTracking(True) self.tableWidget.cellEntered.connect(self.cellHover) def cellHover(self, row, column): """Highlight letter if mouse is hovering over it.""" item = self.tableWidget.item(row, column) oldItem = self.tableWidget.item(self.currentHover[0], self.currentHover[1]) mouseTracker1 = True mouseTracker2 = True for x in range(len(self.xVisited)): if self.xVisited[x] == row and self.yVisited[x] == column: mouseTracker1 = False if self.currentHover[0] == self.xVisited[x] and self.currentHover[ 1] == self.yVisited[x]: mouseTracker2 = False if mouseTracker1: if self.currentHover != [row, column]: if item.text().islower(): item.setBackground(QBrush(QColor('yellow'))) if oldItem.text().islower() and mouseTracker2: oldItem.setBackground(QBrush(QColor('white'))) elif mouseTracker2: oldItem.setBackground(QBrush(QColor('white'))) self.currentHover = [row, column] def onClickLetter(self): """Highlight letters on selection and highlight word green if found on click.""" self.wordSelected = "" wordBankSplitOriginal = self.wordBank.split() selectionTracker = True selectionCorrectness = 0 word = "" listX = [] listY = [] for currentQTableWidgetItem in self.tableWidget.selectedItems(): if self.tableWidget.item( currentQTableWidgetItem.row(), currentQTableWidgetItem.column()).text().isupper(): letter = self.tableWidget.item( currentQTableWidgetItem.row(), currentQTableWidgetItem.column()).text().lower() self.tableWidget.setItem(currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), QTableWidgetItem(letter)) self.tableWidget.clearSelection() else: for currentQTableWidgetItem in self.tableWidget.selectedItems( ): for x in range(0, len(self.xVisited)): if currentQTableWidgetItem.row() == self.xVisited[x] and currentQTableWidgetItem.column() == \ self.yVisited[x]: selectionTracker = False if selectionTracker: letter = self.tableWidget.item( currentQTableWidgetItem.row(), currentQTableWidgetItem.column()).text().upper() self.tableWidget.setItem( currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), QTableWidgetItem(letter)) for currentQTableWidgetItem in self.tableWidget.selectedItems( ): if selectionTracker: self.tableWidget.item( currentQTableWidgetItem.row(), currentQTableWidgetItem.column()).setBackground( QColor(216, 191, 216)) for currentQTableWidgetItem in self.tableWidget.selectedItems( ): if selectionTracker: self.tableWidget.item( currentQTableWidgetItem.row(), currentQTableWidgetItem.column()).setTextAlignment( PyQt5.QtCore.Qt.AlignCenter) self.tableWidget.clearSelection() for x in range(0, nElements): for y in range(0, nElements): if self.tableWidget.item(x, y).text().isupper(): self.wordSelected += self.tableWidget.item(x, y).text() listX.append(x) listY.append(y) for x in wordBankSplitOriginal: if x == self.wordSelected.lower(): selectionCorrectness += 1 word = x if selectionCorrectness == 1: # Makes sure the word is in a row for i in range(1, len(listY)): if listY[i - 1] == listY[i] - 1 and listX[i - 1] == listX[i]: self.inRow += 1 if self.inRow == len(listY) - 1: selectionCorrectness += 1 self.inRow = 0 if selectionCorrectness == 1: # Makes sure the word is in a single column for i in range(1, len(listY)): if listX[i - 1] == listX[i] - 1 and listY[i - 1] == listY[i]: self.inRow += 1 if self.inRow == len(listY) - 1: selectionCorrectness += 1 self.inRow = 0 if selectionCorrectness == 1: # Makes sure the word is in a forward diagonal for i in range(1, len(listY)): if listX[i - 1] == listX[i] - 1 and listY[i - 1] == listY[i] - 1: self.inRow += 1 if self.inRow == len(listY) - 1: selectionCorrectness += 1 self.inRow = 0 if selectionCorrectness == 1: # Makes sure the word is in a backward diagonal for i in range(1, len(listY)): if listX[i - 1] == listX[i] - 1 and listY[i - 1] == listY[i] + 1: self.inRow += 1 if self.inRow == len(listY) - 1: selectionCorrectness += 1 self.inRow = 0 if selectionCorrectness == 2: wordIndex = self.wordSelected.find(word) self.progressValue += 1 self.setProgressBar() self.strikeWord(word) self.wordsCompleted.append(word) for i in range(wordIndex, wordIndex + len(word)): letterI = self.tableWidget.item(listX[i], listY[i]).text().lower() self.tableWidget.setItem(listX[i], listY[i], QTableWidgetItem(letterI)) for i in range(wordIndex, wordIndex + len(word)): self.tableWidget.item(listX[i], listY[i]).setBackground( QColor(144, 238, 144)) self.xVisited.append(listX[i]) self.yVisited.append(listY[i]) for i in range(wordIndex, wordIndex + len(word)): self.tableWidget.item(listX[i], listY[i]).setTextAlignment( PyQt5.QtCore.Qt.AlignCenter) def onClickClear(self): """Clear word selection on button click.""" self.wordSelected = "" for x in range(0, nElements): for y in range(0, nElements): if self.tableWidget.item(x, y).text().isupper(): letterI = self.tableWidget.item(x, y).text().lower() self.tableWidget.setItem(x, y, QTableWidgetItem(letterI)) self.tableWidget.item(x, y).setTextAlignment( PyQt5.QtCore.Qt.AlignCenter) def onClickQuit(self): """Display option to quit the app on button click.""" quitMessage = QMessageBox() quitMessage = QMessageBox.question( self, "Quit", "Are you sure you would like to buttonQuit?", QMessageBox.No | QMessageBox.Yes) if quitMessage == QMessageBox.Yes: sys.exit() else: pass def createProgressBar(self): """Generate progress bar of with the progress of the words found until completion.""" self.progress.setRange(0, len(self.wordBank.split())) self.progress.setToolTip("Shows your word completion progress.") def setProgressBar(self): """Set value for the progress bar.""" self.progress.setValue(self.progressValue) def createTimer(self): """Generate a timer.""" self.timer.timeout.connect(self.Time) self.timer.start(1000) self.time = PyQt5.QtCore.QTime(0, 0, 0) self.LCD = QLCDNumber() self.LCD.display(self.time.toString("hh:mm:ss")) self.LCD.setSegmentStyle(QLCDNumber.Flat) def Time(self): """Increment timer by a second.""" self.time = self.time.addSecs(1) self.LCD.display(self.time.toString("hh:mm:ss")) if len(self.wordsCompleted) == len(self.wordBankSplit): self.timer.stop() self.endTime = self.time.toString("hh:mm:ss") self.addHighScore() self.close() self.openHighscoreMenu = HighScoreMenu() self.openHighscoreMenu.show() def onClickPause(self): """Pause and resume the game on button click.""" if self.timeFlag % 2 == 0: self.timer.stop() self.timeFlag += 1 self.tableWidget.hide() self.buttonPause.setText("Unpause") else: self.timer.start() self.timeFlag += 1 self.tableWidget.show() self.tableWidget.clearSelection() self.buttonPause.setText("Pause") def addHighScore(self): """Save highScore to WS_Highscores text file.""" with open("highscores.txt", "a") as highscoreFile: if 10 <= nElements <= 19: highscoreFile.write("Easy\n") elif 20 <= nElements <= 29: highscoreFile.write("Medium\n") else: highscoreFile.write("Hard\n") highscoreFile.write(str(self.endTime) + "\n")
class BrowserTags(QDialog): def __init__(self, parent=None): super(BrowserTags, self).__init__(parent) # self.setWindowFlags(Qt.FramelessWindowHint) #|Qt.WindowStaysOnTopHint) #|Qt.WindowTitleHint) self.flag = False self.resize(1024, 768) self.setWindowTitle('Etiquetas') masterLayout = QVBoxLayout(self) self.tag_list = [] self.tag_id = -1 deselect_tags_Btn = QPushButton('Des-seleciona') deselect_tags_Btn.clicked.connect(self.deselect_click) clean_tranf_Btn = QPushButton('Limpa Transferidas') clean_tranf_Btn.clicked.connect(self.clean_transfer) add_tag_Btn = QPushButton('Adiciona Tag') add_tag_Btn.clicked.connect(self.add_new_tag) masterLayout.addLayout( qc.addHLayout( [deselect_tags_Btn, clean_tranf_Btn, add_tag_Btn, True])) self.searchEdit = QLineEdit() self.toto = () self.searchEdit.textChanged.connect(self.tag_search_changed) masterLayout.addWidget(self.searchEdit) self.grid = QTableWidget() self.grid.setEditTriggers(QTableWidget.NoEditTriggers) self.grid.verticalHeader().setDefaultSectionSize(20) self.grid.verticalHeader().setVisible(False) self.grid.itemDoubleClicked.connect(self.transfer_click) self.lastTags_grid = QTableWidget() self.lastTags_grid.setEditTriggers(QTableWidget.NoEditTriggers) self.lastTags_grid.verticalHeader().setDefaultSectionSize(20) self.lastTags_grid.verticalHeader().setVisible(False) self.lastTags_grid.itemDoubleClicked.connect(self.transfer_last_click) self.current_tags = QTextBrowser() self.current_tags.setMaximumHeight(60) self.current_tags.setMinimumHeight(60) masterLayout.addLayout(qc.addHLayout([self.grid, self.lastTags_grid])) masterLayout.addWidget(self.current_tags) exit_btn = QPushButton('Sair') exit_btn.clicked.connect(self.exit_click) valid_btn = QPushButton('Substitui') valid_btn.clicked.connect(self.valid_click) add_btn = QPushButton('Adiciona') add_btn.clicked.connect(self.add_click) masterLayout.addLayout(qc.addHLayout([valid_btn, add_btn, exit_btn])) self.tag_refresh() self.last_tag_refresh() def deselect_click(self): self.grid.clearSelection() def clean_transfer(self): self.current_tags.clear() def add_new_tag(self): b, ok = QInputDialog.getText(self, self.tr("Nova Etiqueta"), self.tr("Nova Etiqueta"), QLineEdit.Normal) if ok and not b == '': if self.current_tags.toPlainText() == '': self.current_tags.setText(b) else: self.current_tags.setText(self.current_tags.toPlainText() + ',' + b) else: print('do nothing') def transfer_click(self): a = self.grid.item(self.grid.currentRow(), 1).text() gl.last_tags.insert(0, self.grid.item(self.grid.currentRow(), 1).text()) self.tag_list = self.current_tags.toPlainText().split(',') self.tag_list.append(a) xl = '' for n in self.tag_list: xl += '<font color="blue"><strong>' + n + ',</font>' self.current_tags.setHtml(xl) self.searchEdit.clear() self.searchEdit.setFocus() def transfer_last_click(self): a = self.lastTags_grid.item(self.lastTags_grid.currentRow(), 0).text() self.tag_list = self.current_tags.toPlainText().split(',') self.tag_list.append(a) xl = '' for n in self.tag_list: xl += '<font color="blue"><strong>' + n + ',</font>' self.current_tags.setHtml(xl) def valid_click(self): self.tag_id = 0 self.tag_list = self.current_tags.toPlainText() self.tag_list = self.tag_list.replace(',,', ',') self.tag_list = self.tag_list.rstrip(',') self.tag_list = self.tag_list.lstrip(',') self.flag = True self.close() def add_click(self): self.tag_id = 1 self.tag_list = self.current_tags.toPlainText() self.tag_list = self.tag_list.replace(',,', ',') self.tag_list = self.tag_list.rstrip(',') self.tag_list = self.tag_list.lstrip(',') self.flag = True save_last_tags_params() self.close() def tag_refresh(self): self.c_grid = 10 # sql = '''select ta_id, ta_name from tags order by ta_name''' sql = '''select ta_id,ta_name from tags where tag_key is null order by ta_name''' dataset = dbmain.query_many(sql) ex_grid.ex_grid_update(self.grid, { 0: ['ID', 'i'], 1: ['Nome', 's'] }, dataset, hidden=0) self.grid.horizontalHeader().setVisible(False) self.grid.setColumnWidth(0, 80) self.grid.setColumnWidth(1, 350) def last_tag_refresh(self): self.c_grid = 10 lin = 0 self.lastTags_grid.setColumnCount(1) self.lastTags_grid.setRowCount(len(gl.last_tags)) for n in gl.last_tags: item = QTableWidgetItem() item.setText(n) self.lastTags_grid.setItem(lin, 0, item) lin += 1 self.lastTags_grid.horizontalHeader().setVisible(False) self.lastTags_grid.setColumnWidth(0, 450) def tag_search_changed(self, text): if len(text) > 3: search = '\'%%' + text + '%%\'' # print 'search ',text sql = '''select ta_id, ta_name from tags where unaccent(ta_name) like unaccent(''' + search + ''') order by ta_name''' dataset = dbmain.query_many(sql) ex_grid.ex_grid_update(self.grid, { 0: ['ID', 'i'], 1: ['Nome', 's'] }, dataset, hidden=0) self.grid.horizontalHeader().setVisible(False) self.grid.setColumnWidth(0, 80) self.grid.setColumnWidth(1, 350) def exit_click(self): self.tag_list = '' self.tag_id = -1 self.close()
class MainWindow(QMainWindow): """Главное окно программы. Управляет интерфейсом и событиями.""" def __init__(self, core): """Инициализация главного окна. core - ядро программы, содержащее логику.""" super().__init__() self.core = core self.core.logged.connect(self.onLogged) self.initUI() self.command() def initUI(self): """Инициализировать графический интерфейс.""" self.textEdit = QTextEdit() self.actionFileStart = QAction('&Start', self) self.actionFileStart.triggered.connect(self.onActionFileStartTriggered) self.actionFileStop = QAction('&Stop', self) self.actionFileStop.triggered.connect(self.onActionFileStopTriggered) self.actionFileStop.setDisabled(True) actionFileSave = QAction('&Save config', self) actionFileSave.triggered.connect(self.core.save) actionFileDraw = QAction('&Save chart', self) actionFileDraw.triggered.connect(self.core.draw) actionFileLogClear = QAction('&Clear', self) actionFileLogClear.triggered.connect(self.textEdit.clear) actionFileExit = QAction('&Exit', self) actionFileExit.triggered.connect(self.close) self.actionViewInfo = QAction('&Extended', self) self.actionViewInfo.setCheckable(True) self.actionViewInfo.setChecked(True) self.actionViewInfo.changed.connect(self.changeRow) actionConfigPeriod = QAction('&Period...', self) actionConfigPeriod.triggered.connect(self.actionConfigPeriodTriggered) actionConfigSensorsAdd = QAction('&Add...', self) actionConfigSensorsAdd.triggered.connect( self.actionConfigSensorsAddTriggered) actionConfigSensorsDel = QAction('&Delete...', self) actionConfigSensorsDel.triggered.connect( self.actionConfigSensorsDelTriggered) actionConfigPathAddresses = QAction('&Adressess...', self) actionConfigPathAddresses.triggered.connect( self.actionConfigPathAddressesTriggered) actionConfigPathSensors = QAction('&Sensors...', self) actionConfigPathSensors.triggered.connect( self.actionConfigPathSensorsTriggered) actionConfigPathData = QAction('&Data...', self) actionConfigPathData.triggered.connect( self.actionConfigPathDataTriggered) actionHelpHelp = QAction('&Help...', self) actionHelpHelp.triggered.connect(self.actionHelpHelpTriggered) actionHelpAbout = QAction('&About...', self) actionHelpAbout.triggered.connect(self.onAbout) menuFile = self.menuBar().addMenu('&File') menuFile.addAction(self.actionFileStart) menuFile.addAction(self.actionFileStop) menuFile.addSeparator() menuFileOpen = menuFile.addMenu('&Open') menuFileSave = menuFile.addMenu('&Save') menuFileSave.addAction(actionFileSave) menuFileSave.addAction(actionFileDraw) menuFile.addSeparator() menuFile.addAction(actionFileLogClear) menuFile.addSeparator() menuFile.addAction(actionFileExit) menuView = self.menuBar().addMenu('&View') menuView.addAction(self.actionViewInfo) menuConfig = self.menuBar().addMenu('&Settings') menuConfig.addAction(actionConfigPeriod) menuConfigSensors = menuConfig.addMenu('&Sensors') menuConfigSensors.addAction(actionConfigSensorsAdd) menuConfigSensors.addAction(actionConfigSensorsDel) menuConfigPath = menuConfig.addMenu('&Path') menuConfigPath.addAction(actionConfigPathAddresses) menuConfigPath.addAction(actionConfigPathSensors) menuConfigPath.addAction(actionConfigPathData) menuHelp = self.menuBar().addMenu('&Help') menuHelp.addAction(actionHelpHelp) menuHelp.addAction(actionHelpAbout) self.textEditList = deque(maxlen=100) self.textEdit.setVerticalScrollBarPolicy(2) self.textEdit.setToolTip("Action log.") self.textEdit.setReadOnly(True) self.isItemSave = False self.table = QTableWidget(0, 3) self.table.setToolTip("Temperature sensors.") self.table.setFixedWidth(342) self.table.setVerticalScrollBarPolicy(1) self.table.setHorizontalScrollBarPolicy(1) self.table.setColumnWidth(0, 50) self.table.setColumnWidth(1, 150) self.table.setColumnWidth(2, 120) self.table.setAlternatingRowColors(True) self.table.setSelectionMode(QTableWidget.SingleSelection) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setHorizontalHeaderLabels(['Val', 'Name', 'Address']) self.table.verticalHeader().setFixedWidth(20) self.table.verticalHeader().setDefaultAlignment(Qt.AlignRight) self.table.setEditTriggers(self.table.NoEditTriggers) grid = QGridLayout() grid.setSpacing(3) grid.setContentsMargins(3, 3, 3, 3) grid.addWidget(self.textEdit, 0, 0, 1, 1) grid.addWidget(self.table, 0, 1, 1, 1) widget = QWidget() widget.setLayout(grid) self.setCentralWidget(widget) self.setGeometry(300, 200, 600, 400) self.setWindowFlags(Qt.MSWindowsFixedSizeDialogHint) self.statusBar().setSizeGripEnabled(False) self.setWindowTitle('Observer') self.show() self.core.dataAdded.connect(self.onDataAdded) self.statusBar().showMessage('Application is runnig.') def command(self): """Обработка командой строки при запуске.""" if len(sys.argv) > 1: if sys.argv[1] == '-s': if len(sys.argv) > 2: if sys.argv[2].isdigit(): self.core.period = sys.argv[2] self.actionFileStart.trigger() def closeEvent(self, event): """Событие закрытия программы.""" self.core.stop() super().closeEvent(event) def onActionFileStartTriggered(self): """Действие нажатия Файл -> Старт. Запускает мониторинг.""" self.actionFileStop.setEnabled(True) self.actionFileStart.setDisabled(True) self.core.start() def onActionFileStopTriggered(self): """Действие нажатия Файл -> Стоп. Остановить мониторинг.""" self.actionFileStart.setEnabled(True) self.actionFileStop.setDisabled(True) self.core.stop() def actionConfigSensorsAddTriggered(self): """Действие нажатия Настройки -> Сенсоры -> Добавить. Открыть файл с настройками датчиков.""" if os.path.exists(self.core.pathSensors): answer = QMessageBox.question( self, 'Add sensors', 'Open file "{}/{}" to add sensors in external editor?'.format( self.core.pathSensors, 'temperature')) if answer == QMessageBox.Yes: try: os.startfile('{}\\{}'.format(self.core.pathSensors, 'temperature')) except Exception: QMessageBox.warning( self, 'Add sensors', 'File "{}/{}" don`t open!'.format( self.core.pathSensors, 'temperature')) else: QMessageBox.warning( self, 'Add sensors', '"{}" path does not exist'.format(self.core.pathSensors)) def actionConfigSensorsDelTriggered(self): """Действие нажатия Настройки -> Сенсоры -> Удалить. Открыть файл с настройками датчиков.""" if os.path.exists(self.core.pathSensors): answer = QMessageBox.question( self, 'Delete sensors', 'Open "{}/{}" to delete sensors in external editor?'.format( self.core.pathSensors, 'temperature')) if answer == QMessageBox.Yes: try: os.startfile('{}\\{}'.format(self.core.pathSensors, 'temperature')) except Exception: QMessageBox.warning( self, 'Delete sensors', 'File "{}/{}" don`t open!'.format( self.core.pathSensors, 'temperature')) else: QMessageBox.warning( self, 'Delete sensors', '"{}" path does not exist'.format(self.core.pathSensors)) def actionConfigPeriodTriggered(self): """Действие нажатия Настройки -> Период. Открыть окно настройки периода опроса датчиков.""" period, ok = QInputDialog.getInt(self, 'Request period', 'Enter request period:', int(self.core.period), 10, 3600) if ok: self.core.period = str(period) self.core.configSave() def actionConfigPathAddressesTriggered(self): """Действие нажатия Настройки -> Путь -> Адреса. Открывает окно выбора пути к файлу настройки адресов.""" path = QFileDialog.getOpenFileName(self, 'Addresses path', self.core.pathAddresses, '') if path[0] != '': self.core.pathAddresses = path[0] self.core.configSave() def actionConfigPathSensorsTriggered(self): """Действие нажатия Настройки -> Путь -> Датчики. Открывает окно выбора пути к файлу настройки датчиков.""" path = QFileDialog.getExistingDirectory(self, 'Sensors directory', self.core.pathSensors) if path != '': self.core.pathSensors = path self.core.configSave() def actionConfigPathDataTriggered(self): """Действие нажатия Настройки -> Путь -> Данные. Открывает окно выбора пути к папке, содержащей данные.""" path = QFileDialog.getExistingDirectory(self, 'Data directory', self.core.pathData) if path != '': self.core.pathData = path self.core.configSave() def changeRow(self): """Изменение отображения информации в колонках.""" if self.actionViewInfo.isChecked(): self.table.setColumnHidden(1, False) self.table.setColumnHidden(2, False) self.table.setFixedWidth(self.table.width() + 270) self.setFixedWidth(self.width() + 270) else: self.table.setColumnHidden(1, True) self.table.setColumnHidden(2, True) self.table.setFixedWidth(self.table.width() - 270) self.setFixedWidth(self.width() - 270) def checkItemSave(self, item): """Проверка изменения ячейки имени датчика.""" if item.column() == 1: self.isItemSave = True else: self.isItemSave = False def itemSave(self, item): """Сохранение имени в списке.""" if self.isItemSave: if item.column() == 1: if item.tableWidget() == self.table: self.isItemSave = False self.statusBar().showMessage(item.text() + " save") temp = self.table.item(item.row(), 2) key = temp.text() self.core.sensors[key].name = item.text() self.table.clearSelection() def onLogged(self, log, modes): """Событие логирования. log - передаваемое сообщение; modes - содержит один или несколько режимов отображения лога: l - вывод в текстовое поле; s - вывод в статус бар; f - запись в файл.""" if 'l' in modes: self.textEditList.appendleft(log) self.textEdit.clear() self.textEdit.setText('\n'.join(self.textEditList)) if 's' in modes: self.statusBar().showMessage(log) if 'f' in modes: directory = '{0}\\{1}\\{2}\\{3}'.format( self.core.pathData, self.core.currentDate.strftime('%Y'), self.core.currentDate.strftime('%m'), self.core.currentDate.strftime('%d')) os.makedirs(directory, 0o777, True) with open( '{0}\\{1}.log'.format( directory, self.core.currentDate.strftime('%Y.%m.%d')), 'a') as file: file.write(log + '\n') def onDataAdded(self): """Событие добавления данных.""" self.table.setRowCount(len(self.core.sensors)) if self.table.rowCount() <= 20: self.table.setMinimumHeight(25 + self.table.rowCount() * 23) i = 0 for key in self.core.sensors.keys(): if self.core.sensors[key].value is not None: self.table.setRowHeight(i, 23) self.table.setItem( i, 0, QTableWidgetItem(self.core.sensors[key].value)) self.table.setItem( i, 1, QTableWidgetItem(self.core.sensors[key].name)) self.table.setItem(i, 2, QTableWidgetItem(key)) i += 1 self.table.setRowCount(i) self.table.sortItems(1) def onAbout(self): """Действие нажатия Помощь -> О программе.""" text = '<h2>{0}</h2>'\ '<p>Client program for monitoring the condition<br>'\ 'of the premises of the Kharkov Radar.<br>'\ 'Source code is available on '\ '<a href="https://github.com/StanislavMain/ObserverClient">'\ 'GitHub</a>.</p><p><b>Stanislav Hnatiuk</b><br>'\ 'Institute of Ionosphere\n<br>Ukraine, Kharkiv.<br><br>'\ 'Contacts with me:<br>'\ '<a href="https://t.me/stanmain">Telegram</a><br>'\ '<a href="https://github.com/StanislavMain">GitHub</a><br>'\ '<br><b>All rights reserved.</b></p>'.format(self.windowTitle()) QMessageBox.about(self, 'About the program', text) def actionHelpHelpTriggered(self): """Действие нажатия Помощь -> Помощь.""" text = '<p><b>Console parametrs</b><br>'\ 'usage: main [options]<br>'\ 'where options have next key:<br>'\ '-s [seconds]: start monitoring with a period of [seconds]</p>'\ '<p><b>Code and name</b><br>'\ 'Write your address and sensors in the appropriate files:<br>'\ '{}<br>{}/*</p>'\ '<p><b>Data</b><br>'\ 'Data is located in "{}"<br>'\ '"/Year/Month/Day/Y.M.D.csv" : data file<br>'\ '"/Year/Month/Day/Y.M.D.png" : chart file<br>'\ '"/Year/Month/Day/Y.M.D.log" : log file<br>'\ '</p>'.format( self.core.pathAddresses, self.core.pathSensors, self.core.pathData ) QMessageBox.information(self, 'Help', text)
class Widgets(QWidget): def __init__(self, parent=None): super(Widgets, self).__init__(parent) self.initUI() def initUI(self): fuenteSiacle = self.font() fuenteSiacle.setBold(True) fuenteSiacle.setPointSize(12) paletaBotones = self.palette() paletaBotones.setColor(QPalette.Background, QColor("#2EFEC8")) frameBotones = QFrame() frameBotones.setFrameStyle(QFrame.NoFrame) frameBotones.setAutoFillBackground(True) frameBotones.setPalette(paletaBotones) frameBotones.setFixedWidth(220) paletaPanel = self.palette() paletaPanel.setBrush(QPalette.Background, QBrush(QColor(255, 90, 0), Qt.SolidPattern)) paletaPanel.setColor(QPalette.Foreground, Qt.white) labelSiacle = QLabel("ПАНЕЛЬ УПРАВЛЕНИЯ", frameBotones) labelSiacle.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) labelSiacle.setFont(fuenteSiacle) labelSiacle.setAutoFillBackground(True) labelSiacle.setPalette(paletaPanel) labelSiacle.setFixedSize(220, 46) labelSiacle.move(0, 0) # ============================================================ tamanioIcono = QSize(30, 30) botonNuevo = Boton(frameBotones) botonNuevo.setText(" Новый") botonNuevo.setToolTip("Новый клиент") botonNuevo.setCursor(Qt.PointingHandCursor) botonNuevo.move(-2, 45) botonActualizar = Boton(frameBotones) botonActualizar.setText(" Обновление") botonActualizar.setToolTip("Обновление клиента ") botonActualizar.setCursor(Qt.PointingHandCursor) botonActualizar.move(-2, 82) botonEliminar = Boton(frameBotones) botonEliminar.setText(" Удалить") botonEliminar.setToolTip("Удалить клиента") botonEliminar.setCursor(Qt.PointingHandCursor) botonEliminar.move(-2, 119) botonLimpiar = Boton(frameBotones) botonLimpiar.setText(" Закрыть") botonLimpiar.setToolTip("Закрыть бд") botonLimpiar.setCursor(Qt.PointingHandCursor) botonLimpiar.move(-2, 156) # ============================================================ paletaSuscribete = self.palette() paletaSuscribete.setBrush(QPalette.Background, QBrush(QColor(135, 206, 250), Qt.SolidPattern)) fuenteSuscribete = self.font() fuenteSuscribete.setBold(True) fuenteSuscribete.setFamily("Arial") fuenteSuscribete.setPointSize(11) labelSuscribete = QLabel("<a href='https://t.me/yarosla_0v'>АВТОР</a>", frameBotones) labelSuscribete.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) labelSuscribete.setOpenExternalLinks(True) labelSuscribete.setFont(fuenteSuscribete) labelSuscribete.setAutoFillBackground(True) labelSuscribete.setPalette(paletaSuscribete) labelSuscribete.setFixedSize(220, 46) labelSuscribete.move(0, 210) paletaFrame = self.palette() paletaFrame.setColor(QPalette.Background, QColor("blue")) frameBienvenido = QFrame() frameBienvenido.setFrameStyle(QFrame.NoFrame) frameBienvenido.setAutoFillBackground(True) frameBienvenido.setPalette(paletaFrame) frameBienvenido.setFixedHeight(46) # ============================================================ paletaTitulo = self.palette() paletaTitulo.setColor(QPalette.Foreground, Qt.yellow) labelBienvenido = QLabel("Клиенты") labelBienvenido.setAlignment(Qt.AlignCenter) labelBienvenido.setFont(fuenteSiacle) labelBienvenido.setPalette(paletaTitulo) botonConfiguracion = QPushButton() botonConfiguracion.setIcon(QIcon("Imagenes/configuracion.png")) botonConfiguracion.setIconSize(QSize(24, 24)) botonConfiguracion.setToolTip("Конфигурация") botonConfiguracion.setCursor(Qt.PointingHandCursor) botonConfiguracion.setFixedWidth(36) disenioFrame = QHBoxLayout() disenioFrame.addWidget(labelBienvenido, Qt.AlignCenter) disenioFrame.addStretch() disenioFrame.addWidget(botonConfiguracion) disenioFrame.setContentsMargins(0, 0, 5, 0) frameBienvenido.setLayout(disenioFrame) # ============================================================ self.buscarLineEdit = QLineEdit() self.buscarLineEdit.setObjectName("Enter") self.buscarLineEdit.setPlaceholderText("Имя клента") self.buscarLineEdit.setMinimumSize(200, 26) botonBuscar = QPushButton("Поиск") botonBuscar.setObjectName("Поиск") botonBuscar.setCursor(Qt.PointingHandCursor) botonBuscar.setMinimumSize(60, 26) separadorTodos = QFrame() separadorTodos.setFrameShape(QFrame.VLine) separadorTodos.setFrameShadow(QFrame.Raised) separadorTodos.setFixedSize(1, 26) botonTodos = QPushButton("Все записи") botonTodos.setObjectName("Все записи") botonTodos.setCursor(Qt.PointingHandCursor) botonTodos.setMinimumSize(60, 26) nombreColumnas = ("Id", "Имя", "Фамилия", "Пол", "Дата рождения", "Страна", "Телефон") menuMostrarOcultar = QMenu() for indice, columna in enumerate(nombreColumnas, start=0): accion = QAction(columna, menuMostrarOcultar) accion.setCheckable(True) accion.setChecked(True) accion.setData(indice) menuMostrarOcultar.addAction(accion) botonMostrarOcultar = QPushButton("Скрыть столбцы") botonMostrarOcultar.setCursor(Qt.PointingHandCursor) botonMostrarOcultar.setMenu(menuMostrarOcultar) botonMostrarOcultar.setMinimumSize(180, 26) disenioBuscar = QHBoxLayout() disenioBuscar.setSpacing(10) disenioBuscar.addWidget(self.buscarLineEdit) disenioBuscar.addWidget(botonBuscar) disenioBuscar.addWidget(separadorTodos) disenioBuscar.addWidget(botonTodos) disenioBuscar.addWidget(botonMostrarOcultar) self.tabla = QTableWidget() self.tabla.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tabla.setDragDropOverwriteMode(False) self.tabla.setSelectionBehavior(QAbstractItemView.SelectRows) self.tabla.setSelectionMode(QAbstractItemView.SingleSelection) self.tabla.setTextElideMode(Qt.ElideRight) # Qt.ElideNone self.tabla.setWordWrap(False) self.tabla.setSortingEnabled(False) self.tabla.setColumnCount(7) self.tabla.setRowCount(0) self.tabla.horizontalHeader().setDefaultAlignment(Qt.AlignHCenter | Qt.AlignVCenter | Qt.AlignCenter) self.tabla.horizontalHeader().setHighlightSections(False) self.tabla.horizontalHeader().setStretchLastSection(True) self.tabla.verticalHeader().setVisible(False) self.tabla.setAlternatingRowColors(True) self.tabla.verticalHeader().setDefaultSectionSize(20) # self.tabla.verticalHeader().setHighlightSections(True) self.tabla.setHorizontalHeaderLabels(nombreColumnas) self.tabla.setContextMenuPolicy(Qt.CustomContextMenu) self.tabla.customContextMenuRequested.connect(self.menuContextual) for indice, ancho in enumerate((80, 240, 240, 140, 150, 130), start=0): self.tabla.setColumnWidth(indice, ancho) # ============================================================ disenioBuscarTabla = QVBoxLayout() disenioBuscarTabla.addLayout(disenioBuscar) disenioBuscarTabla.addWidget(self.tabla) disenioBuscarTabla.setSpacing(8) disenioBuscarTabla.setContentsMargins(10, 10, 10, 0) disenioDerecho = QVBoxLayout() disenioDerecho.addWidget(frameBienvenido) disenioDerecho.addLayout(disenioBuscarTabla) disenioDerecho.setContentsMargins(0, 0, 0, 0) disenioFinal = QGridLayout() disenioFinal.addWidget(frameBotones, 0, 0) disenioFinal.addLayout(disenioDerecho, 0, 1) disenioFinal.setSpacing(0) disenioFinal.setContentsMargins(0, 0, 0, 0) self.setLayout(disenioFinal) self.copiarInformacion = QApplication.clipboard() botonNuevo.clicked.connect(self.Nuevo) botonActualizar.clicked.connect(self.Actualizar) botonEliminar.clicked.connect(self.Eliminar) botonLimpiar.clicked.connect(self.limpiarTabla) self.buscarLineEdit.returnPressed.connect(self.Buscar) botonBuscar.clicked.connect(self.Buscar) botonTodos.clicked.connect(self.Buscar) botonConfiguracion.clicked.connect(lambda: Configuracion(self).exec_()) self.tabla.itemDoubleClicked.connect(self.Suscribete) menuMostrarOcultar.triggered.connect(self.mostrarOcultar) def Nuevo(self): nuevoCliente(self).exec_() def Actualizar(self): fila = self.tabla.selectedItems() if fila: indice = fila[0].row() datos = [self.tabla.item(indice, i).text() for i in range(7)] actualizarCliente(indice, datos, self).exec_() else: QMessageBox.critical(self, "Обновление клиента", "Выберите клиента. ", QMessageBox.Ok) def Eliminar(self): fila = self.tabla.selectedItems() if fila: eliminar = QMessageBox(self) eliminar.setWindowTitle("Удалить клиента") eliminar.setIcon(QMessageBox.Question) eliminar.setText("Вы уверены, что хотите удалить клиента? ") botonSi = eliminar.addButton("Да", QMessageBox.YesRole) botonCancelar = eliminar.addButton("Отмена", QMessageBox.NoRole) eliminar.exec_() if eliminar.clickedButton() == botonSi: indiceFila = fila[0].row() idCliente = self.tabla.item(indiceFila, 0).text() if QFile.exists("DB_SIACLE/DB_SIACLE.db"): conexion = sqlite3.connect("DB_SIACLE/DB_SIACLE.db") cursor = conexion.cursor() try: cursor.execute("DELETE FROM CLIENTES WHERE ID = ?", (idCliente,)) conexion.commit() conexion.close() self.tabla.removeRow(indiceFila) self.tabla.clearSelection() QMessageBox.information(self, "Удалить клиента.", "Клиент удален." " ", QMessageBox.Ok) except: conexion.close() QMessageBox.critical(self, "Удалить клиента.", "Неизвестная ошибка. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Поиск клиентов", "База не найдена. " "datos. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Удалить клиента ", "Выберите клиента. ", QMessageBox.Ok) def Suscribete(self, celda): QMessageBox.warning(self, "", " " "\n " " ".format(celda.text()), QMessageBox.Ok) def Buscar(self): widget = self.sender().objectName() if widget in ("Enter", "Поиск"): cliente = " ".join(self.buscarLineEdit.text().split()).lower() if cliente: sql = "SELECT * FROM CLIENTES WHERE NOMBRE LIKE ?", ("%" + cliente + "%",) else: self.buscarLineEdit.setFocus() return else: self.buscarLineEdit.clear() sql = "SELECT * FROM CLIENTES" if QFile.exists("DB_SIACLE/DB_SIACLE.db"): conexion = sqlite3.connect("DB_SIACLE/DB_SIACLE.db") cursor = conexion.cursor() try: if widget in ("Enter", "Поиск"): cursor.execute(sql[0], sql[1]) else: cursor.execute(sql) datosDevueltos = cursor.fetchall() conexion.close() self.tabla.clearContents() self.tabla.setRowCount(0) if datosDevueltos: fila = 0 for datos in datosDevueltos: self.tabla.setRowCount(fila + 1) idDato = QTableWidgetItem(str(datos[0])) idDato.setTextAlignment(Qt.AlignCenter) self.tabla.setItem(fila, 0, idDato) self.tabla.setItem(fila, 1, QTableWidgetItem(datos[1])) self.tabla.setItem(fila, 2, QTableWidgetItem(datos[2])) self.tabla.setItem(fila, 3, QTableWidgetItem(datos[3])) self.tabla.setItem(fila, 4, QTableWidgetItem(datos[4])) self.tabla.setItem(fila, 5, QTableWidgetItem(datos[5])) self.tabla.setItem(fila, 6, QTableWidgetItem(datos[6])) fila += 1 else: QMessageBox.information(self, "Поиск клиента ", "Не найдено " "информации. ", QMessageBox.Ok) except: conexion.close() QMessageBox.critical(self, "Поиск клиента", "Неизвестная ошибка. ", QMessageBox.Ok) else: QMessageBox.critical(self, "Buscar clientes", "База данных не найдена. ", QMessageBox.Ok) self.buscarLineEdit.setFocus() def mostrarOcultar(self, accion): columna = accion.data() if accion.isChecked(): self.tabla.setColumnHidden(columna, False) else: self.tabla.setColumnHidden(columna, True) def limpiarTabla(self): self.tabla.clearContents() self.tabla.setRowCount(0) def menuContextual(self, posicion): indices = self.tabla.selectedIndexes() if indices: menu = QMenu() itemsGrupo = QActionGroup(self) itemsGrupo.setExclusive(True) menu.addAction(QAction("Скопировать все ", itemsGrupo)) columnas = [self.tabla.horizontalHeaderItem(columna).text() for columna in range(self.tabla.columnCount()) if not self.tabla.isColumnHidden(columna)] copiarIndividual = menu.addMenu("Копировать поле") for indice, item in enumerate(columnas, start=0): accion = QAction(item, itemsGrupo) accion.setData(indice) copiarIndividual.addAction(accion) itemsGrupo.triggered.connect(self.copiarTableWidgetItem) menu.exec(self.tabla.viewport().mapToGlobal(posicion)) def copiarTableWidgetItem(self, accion): filaSeleccionada = [dato.text() for dato in self.tabla.selectedItems()] if accion.text() == "Скопировать все": filaSeleccionada = tuple(filaSeleccionada) else: filaSeleccionada = filaSeleccionada[accion.data()] self.copiarInformacion.clear(mode=QClipboard.Clipboard) self.copiarInformacion.setText(str(filaSeleccionada), QClipboard.Clipboard)
class App(QDialog): def __init__(self): super().__init__() self.title = 'SEQUENTIAL' self.left = 100 self.top = 100 self.width = 320 self.height = 320 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, 1500, 1500) global inpSize # cardinal variables d'entrées dans notre cas rf0 rf1 pr1 pr2 pr3 global outpSize # cardinal variables de sorties pr1 pr2 pr3 global inlabels # labels des variables global outlabels # global pblties # 2^(inpsize) self.textboxou = QLineEdit(self) self.textboxou.setPlaceholderText("Out") self.textboxou.move(442, 600) self.textboxou.resize(30, 30) self.textboxin = QLineEdit(self) self.textboxin.setText("") self.textboxin.setPlaceholderText("In") self.textboxin.move(410, 600) self.textboxin.resize(30, 30) inlabels = [] outlabels = [] global horiz self.buttonSet = QPushButton('OK', self) self.buttonSet.move(410, 630) self.buttonSet.resize(60, 40) self.buttonSet.clicked.connect(self.on_lickvarou) self.show() def on_lickvarou(self): global inpSize global outpSize global inlabels global outlabels global pblties inlabels = [] outlabels = [] outpSize = int(self.textboxou.text()) inpSize = int(self.textboxin.text()) self.textboxou.hide() self.textboxin.hide() self.buttonSet.hide() horiz = [] pblties = 2**inpSize ii = 0 while (ii < inpSize): inlabels.append("in" + str(ii)) horiz.append("in" + str(ii)) ii = ii + 1 ii = 0 while (ii < outpSize): outlabels.append("out" + str(ii)) horiz.append("out" + str(ii)) ii = ii + 1 self.datatable = QTableWidget(parent=self) self.datatable.setColumnCount(inpSize + outpSize) self.datatable.setRowCount(pblties) self.datatable.setHorizontalHeaderLabels(horiz) self.datatable.show() QTableWidget.resize(self.datatable, 1250, 600) self.textbox = QTextEdit(self) self.textbox.setText("") self.textbox.move(900, 630) self.textbox.resize(280, 40) self.textbox.show() lik = list(itertools.product([0, 1], repeat=inpSize)) i = 0 while (i < pblties): j = 0 while (j < (inpSize + outpSize)): if (j > inpSize - 1): self.datatable.setItem(i, j, QTableWidgetItem('x')) else: self.datatable.setItem( i, j, QTableWidgetItem(str( lik.__getitem__(i).__getitem__(j)))) j = j + 1 i = i + 1 buttonEnv = QPushButton('ENVOYER', self) buttonEnv.setToolTip('This is an example button') buttonEnv.move(700, 650) buttonEnv.clicked.connect(self.on_lick) buttonEnv.show() def on_lick(self): global inpSize global outpSize global inlabels global outlabels global pblties print("hala") self.datatable.clearSelection() self.datatable.selectAll() strsin = ["" for x in range(pblties)] strsout = ["" for x in range(pblties)] z = 0 for currentQTableWidgetItem in self.datatable.selectedItems(): if (currentQTableWidgetItem.column() > inpSize - 1): strsout[currentQTableWidgetItem.row( )] = strsout[currentQTableWidgetItem.row( )] + currentQTableWidgetItem.text() else: strsin[currentQTableWidgetItem.row( )] = strsin[currentQTableWidgetItem.row( )] + currentQTableWidgetItem.text() k = 0 t = logicmin.TT(inpSize, outpSize) while (k < pblties): t.add(strsin[k], strsout[k]) k = k + 1 sols = t.solve() self.textbox.setText( sols.printN(xnames=inlabels, ynames=outlabels, info=False))
class MainWindow(QMainWindow): ''' A class used to represent main window Attributes ---------- mydatabase : mydatabase instance of the class mydatabase table : QTableWidget table for displaying data from database Methods ------- initUI Initializes user interface loaddb(grid_layout) Loads database and display it in table createdb(grid_layout,colnumber,valuelist) Creates new database and display it in table displaytable(grid_layout,colnumber,rownumber,data) Displays data in table savefile Saves data in csv file requesteditrow(grid_layout) Asks user to input data for editing cell requaestdelrow(grid_layout) Asks user to input data for deleting row requestaddrow(grid_layout) Asks user to input data for adding row askforsearch Asks user to input data for finding row openfilename Asks user to select file for opening savefilepath Asks user to select file for saving requestcolnumber(grid_layout) Asks user to input data for creating new database showabout Shows message with information about developer on_cell_item_clicked(item) Allows user to edit cell by double clicking on cell's item showmessage(title,text) Shows message box with given title and text ''' def __init__(self): super().__init__() self.table = QTableWidget() # Создаём таблицу self.table.itemDoubleClicked.connect(self.on_cell_item_clicked) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.initUI() def initUI(self): '''Initializes user interface ''' central_widget = QWidget(self) # Создаём центральный виджет self.setMinimumWidth(500) self.setMinimumHeight(500) self.setCentralWidget(central_widget) # Устанавливаем центральный виджет grid_layout = QGridLayout() # Создаём QGridLayout central_widget.setLayout(grid_layout) # Устанавливаем данное размещение в центральный виджет exitAction = QAction('Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit from application') exitAction.setIcon(QIcon('exit.svg')) exitAction.triggered.connect(self.close) loadAction = QAction('Load',self) loadAction.setShortcut('Ctrl+O') loadAction.setStatusTip('Load database from cvs file') loadAction.setIcon(QIcon('load.svg')) loadAction.triggered.connect(lambda: self.loaddb(grid_layout)) saveAction = QAction('Save', self) saveAction.setShortcut('Ctrl+S') saveAction.setStatusTip('Save database into cvs file') saveAction.setIcon(QIcon('save.svg')) saveAction.triggered.connect(self.savefile) createAction = QAction('Create', self) createAction.setShortcut('Ctrl+N') createAction.setStatusTip('Create new database') createAction.setIcon(QIcon("new.svg")) createAction.triggered.connect(lambda: self.requestcolnumber(grid_layout)) deleterowAction = QAction('Delete row',self) deleterowAction.setStatusTip('Delete row from database') deleterowAction.setIcon(QIcon('delete.svg')) deleterowAction.triggered.connect(lambda: self.requaestdelrow(grid_layout)) addrowAction = QAction('Add row', self) addrowAction.setStatusTip('Add row to database') addrowAction.setIcon(QIcon('add.svg')) addrowAction.triggered.connect(lambda: self.requestaddrow(grid_layout)) editrowAction = QAction('Edit row',self) editrowAction.setStatusTip('Edit row in database') editrowAction.setIcon(QIcon('edit.svg')) editrowAction.triggered.connect(lambda: self.requesteditrow(grid_layout)) searchAction = QAction('Find row',self) searchAction.setShortcut('Ctrl+F') searchAction.setStatusTip('Find row in database') searchAction.setIcon(QIcon('find.svg')) searchAction.triggered.connect(self.askforsearch) aboutAction = QAction('About us', self) aboutAction.setStatusTip('About us') aboutAction.setIcon(QIcon('about.svg')) aboutAction.triggered.connect(self.showabout) self.statusBar() menubar = self.menuBar() fileMenu = menubar.addMenu('&File') editMenu = menubar.addMenu('&Edit') moreMenu = menubar.addMenu('&More') helpMenu = menubar.addMenu('&Help') fileMenu.addAction(loadAction) fileMenu.addAction(saveAction) fileMenu.addAction(createAction) fileMenu.addAction(exitAction) editMenu.addAction(deleterowAction) editMenu.addAction(addrowAction) editMenu.addAction(editrowAction) moreMenu.addAction(searchAction) helpMenu.addAction(aboutAction) self.setWindowTitle('База данных') # название окна self.setWindowIcon(QIcon('icon.jpg')) self.show() def loaddb(self,grid_layout): '''Loads database and display it in table Parameters ---------- :param grid_layout: QGridLayout ''' filename = self.openfilename() if os.path.exists(filename): self.mydatabase = mydatabase(filename) rownumber,colnumber,data = self.mydatabase.readdatafromcsv(filename) self.displaytable(grid_layout,colnumber,rownumber,data) def createdb(self,grid_layout,colnumber,valuelist): ''' Creates new database and display it in table Parameters ---------- :param grid_layout: QGridLayout :param colnumber: number of columns :param valuelist: list of values ''' self.mydatabase = mydatabase('') colnumber=colnumber+1 newdb = self.mydatabase.create(colnumber,valuelist[0:colnumber]) self.mydatabase.add(valuelist[colnumber:len(valuelist)]) rownumber = 2 data=[valuelist[0:colnumber]] data.append(valuelist[colnumber:len(valuelist)]) self.displaytable(grid_layout,colnumber,rownumber,data) def displaytable(self,grid_layout,colnumber,rownumber,data): '''Displays data in table Parameters ---------- :param grid_layout: QGridLayout :param colnumber: number of columns :param rownumber: number of rows :param data: data ''' self.table.setColumnCount(colnumber) self.table.setRowCount(rownumber-1) if type(data[0]) == list: headerlist=data[0] self.table.setHorizontalHeaderLabels(headerlist) data=data[1:] for i in range(rownumber-1): for j in range(colnumber): # print('i, j, data[i][j]',i,j,data[i][j]) self.table.setItem(i, j, QTableWidgetItem(str(data[i][j]))) else: headerlist = ['столбец '+str(i) for i in range(1,colnumber+1)] self.table.setHorizontalHeaderLabels(headerlist) for j in range(colnumber): self.table.setItem(0, j, QTableWidgetItem(str(data[j]))) grid_layout.addWidget(self.table, 0, 0) # Добавляем таблицу в сетку def savefile(self): ''' Saves data in csv file ''' savepath = self.savefilepath() print('savepath',savepath) if len(savepath) < 4: print('length',len(savepath)) savepath=savepath+".csv" else: if savepath[-4:] !=".csv": print('1',savepath[-4:]) savepath=savepath+".csv" print('2',savepath) try: self.mydatabase.dumpdb(savepath) except Exception as e: pass def requesteditrow(self,grid_layout): ''' Asks user to input data for editing cell Parameters ---------- :param grid_layout: QGridLayout ''' try: row, ok = QInputDialog.getInt(self, 'Редактировать ячейку', 'Введите номер строки \n(Вы также можете редактировать ячейку дважды кликнув по ней)',min=1,max=len(self.mydatabase.db.keys())-1) col, ok = QInputDialog.getInt(self, 'Редактировать ячейку', 'Введите номер столбца',min=1,max=len(self.mydatabase.db[0])-1) value, ok = QInputDialog.getText(self, 'Редактировать ячейку', 'Введите значение ячейки') is_successful = self.mydatabase.set(row,col,value) if is_successful == True: colnumber = len(self.mydatabase.db[0]) rownumber = len(self.mydatabase.db.keys()) data = [] for key in self.mydatabase.db: data.append(self.mydatabase.db[key]) print('data',data) self.displaytable(grid_layout,colnumber,rownumber,data) except Exception as e: self.showmessage("Внимание","Чтобы редактировать строку, нужно сначала загрузить из файла или создать базу данных") def requaestdelrow(self,grid_layout): ''' Asks user to input data for deleting row Parameters ---------- :param grid_layout: QGridLayout ''' try: # key, ok = QInputDialog.getInt(self, 'Удалить строку', 'Введите номер строки, которую хотите удалить',min=1,max=len(self.mydatabase.db.keys())) key = int(self.table.selectedItems()[0].text()) if len(self.table.selectedItems()) == len(self.mydatabase.db[0]): is_successful = self.mydatabase.delete(key+1) print('key',key,'database',self.mydatabase.db) if is_successful == True: colnumber = len(self.mydatabase.db[0]) rownumber = len(self.mydatabase.db.keys()) data = [] for key in self.mydatabase.db: data.append(self.mydatabase.db[key]) self.displaytable(grid_layout,colnumber,rownumber,data) else: self.showmessage("Внимание","Вы пытаетесь удалить несуществующую строку") else: self.showmessage("Внимание","Вы выделили столбец") except IndexError: self.showmessage("Внимание","Чтобы удалить строку, сначала выделите ее в таблице") except Exception as e: self.showmessage("Внимание","Чтобы удалить строку, нужно сначала загрузить из файла или создать базу данных") def requestaddrow(self,grid_layout): ''' Asks user to input data for adding row Parameters ---------- :param grid_layout: QGridLayout ''' try: colnumber = len(self.mydatabase.db[0]) valuelist=[str(len(self.mydatabase.db.keys())-1)] ok = True for i in range(colnumber-1): value='' while value == '' and ok == True: value, ok = QInputDialog.getText(self, 'Создание БД', 'Введите значение для '+str(i+1)+'-го поля:') if value: valuelist.append(value) self.mydatabase.add(valuelist) #добавили строку в БД rownumber = len(self.mydatabase.db.keys()) data = [] for key in self.mydatabase.db: data.append(self.mydatabase.db[key]) self.displaytable(grid_layout,colnumber,rownumber,data) except IndexError: self.showmessage("Внимание","Вы заполнили не все поля") except Exception as e: self.showmessage("Внимание","Чтобы добавить строку, нужно сначала загрузить из файла или создать базу данных") def askforsearch(self): ''' Asks user to input data for finding row ''' try: colnumber = self.table.currentColumn() print('colnumber',colnumber,len(self.table.selectedItems()),len(self.mydatabase.db.keys())-1) print('db',self.mydatabase.db[1][colnumber]) if len(self.table.selectedItems()) == len(self.mydatabase.db.keys())-1: value, ok = QInputDialog.getText(self, 'Поиск по столбцу', 'Введите значение, которое нужно найти: ') if value: keylist = self.mydatabase.search(colnumber,value) self.table.clearSelection() for key in keylist: for i in range(len(self.mydatabase.db[0])-1): self.table.item(key-1,i).setSelected(True) print('keys',keylist) else: self.showmessage("Внимание","Выделите столбец") except Exception as e: self.showmessage("Внимание","Чтобы выполнить поиск, сначала создайте или загрузите базу данных") def openfilename(self): ''' Asks user to select file for opening ''' path = os.getcwd() fname = QFileDialog.getOpenFileName(self, 'Open file',path,"*.csv")[0] print(fname) return fname def savefilepath(self): '''Asks user to select file for saving ''' path = os.getcwd() path = QFileDialog.getSaveFileName(self,'Save file',path,'*.csv')[0] print('path',path) return path def requestcolnumber(self,grid_layout): '''Asks user to input data for creating new database Parameters ---------- :param grid_layout: QGridLayout ''' colnumber, ok = QInputDialog.getInt(self, 'Создание БД', 'Введите число полей', min = 1) if ok == True: valuelist=[''] ok = True for i in range(colnumber): value='' while value == '' and ok == True: value, ok = QInputDialog.getText(self, 'Создание БД', 'Введите название для '+str(i+1)+'-го поля:') if value: valuelist.append(value) valuelist.append(0) for i in range(colnumber): value='' while value == '' and ok == True: value, ok = QInputDialog.getText(self, 'Создание БД', 'Введите значение для '+str(i+1)+'-го поля:') if value: valuelist.append(value) if ok == True: self.createdb(grid_layout,colnumber,valuelist) def showabout(self): ''' Shows message with information about developer ''' self.showmessage("О разработчике","Студент Роман Забаровский") def on_cell_item_clicked(self, item): ''' Allows user to edit cell by double clicking on cell's item Parameters ---------- :param item: QTableWidget::item ''' print(item, item.column()) if item.column() != 0: new_text, ok = QInputDialog.getText(self, 'Редактирование ячейки', 'Введите новое значение поля', text=item.text()) row=item.row()+1 is_successful = self.mydatabase.set(row,item.column(),new_text) print('is_successful',is_successful, 'db',self.mydatabase.db[row]) if ok: item.setText(new_text) def showmessage(self,title,text): ''' Shows message box with given title and text Parameters ---------- :param title: the title of message box :param text: the text in message box ''' msg = QMessageBox() msg.setWindowTitle(title) msg.setText(text) x = msg.exec_()