class CameraWindow(PyDialog): """defines the CameraWindow class""" def __init__(self, data, win_parent=None): """ +--------+ | Camera | +--------+---------------+ | Camera Name | | +-------------------+ | | | | | | | | | | | | | | | | | | | | | | +-------------------+ | | | | Name xxx Save | | Delete Set | | | | Apply OK Cancel | +--------+---------------+ """ PyDialog.__init__(self, data, win_parent) self.setWindowTitle('Camera Views') #self.setWindowIcon(view_icon) self._default_name = 'Camera' self.out_data['clicked_ok'] = False self.cameras = deepcopy(data['cameras']) self.names = sorted(self.cameras.keys()) self.name = QLabel("Name:") self.name_edit = QLineEdit(str(self._default_name)) self.delete_button = QPushButton("Delete") self.set_button = QPushButton("Set") self.save_button = QPushButton("Save") # closing self.apply_button = QPushButton("Apply") self.close_button = QPushButton("Close") self.cancel_button = QPushButton("Cancel") self.table = QTableWidget() names_text = [] for name in self.names: name_text = QTableWidgetItem(str(name)) names_text.append(name_text) self.create_layout(names_text) self.set_connections() def create_layout(self, names_text): nrows = len(self.names) table = self.table table.setRowCount(nrows) table.setColumnCount(1) headers = ['Camera Name'] table.setHorizontalHeaderLabels(headers) header = table.horizontalHeader() header.setStretchLastSection(True) for iname, name_text in enumerate(names_text): # row, col, value table.setItem(iname, 0, name_text) table.resizeRowsToContents() ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.close_button) ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) grid.addWidget(self.save_button, irow, 2) irow += 1 grid.addWidget(self.delete_button, irow, 0) grid.addWidget(self.set_button, irow, 1) irow += 1 vbox = QVBoxLayout() vbox.addWidget(self.table) vbox.addLayout(grid) vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): """creates the actions for the menu""" #if qt_version == 4: #self.connect(self.ok_button, QtCore.SIGNAL('clicked()'), self.on_ok) self.set_button.clicked.connect(self.on_set) self.save_button.clicked.connect(self.on_save) self.delete_button.clicked.connect(self.on_delete) self.apply_button.clicked.connect(self.on_apply) self.close_button.clicked.connect(self.on_close) self.cancel_button.clicked.connect(self.on_cancel) def on_set(self): objs = self.table.selectedIndexes() if len(objs) == 1: obj = objs[0] irow = obj.row() name = self.names[irow] self.set_camera(name) return True return False def on_save(self): name = str(self.name_edit.text()).strip() if name in self.cameras: return irow = self.nrows if len(name): self.table.insertRow(irow) name_text = QTableWidgetItem(str(name)) self.table.setItem(irow, 0, name_text) self.name_edit.setText('') self.save_camera(name) def set_camera(self, name): camera_data = self.cameras[name] if self.win_parent is None: return self.win_parent.on_set_camera_data(camera_data) def save_camera(self, name): self.names.append(name) if self.win_parent is None: self.cameras[name] = None return self.cameras[name] = self.win_parent.get_camera_data() @property def nrows(self): return self.table.rowCount() def on_delete(self): irows = [] for obj in self.table.selectedIndexes(): irow = obj.row() irows.append(irow) irows.sort() for irow in reversed(irows): self.table.removeRow(irow) name = self.names.pop(irow) del self.cameras[name] #print(' removing irow=%s name=%r' % (irow, name)) def closeEvent(self, event): event.accept() def on_apply(self): passed = self.on_set() #if passed: # self.win_parent.create_plane(self.out_data) return passed def on_close(self): self.out_data['clicked_ok'] = True self.out_data['cameras'] = self.cameras self.close() def on_ok(self): passed = self.on_apply() if passed: name = str(self.name_edit.text()).strip() self.out_data['name'] = name self.out_data['cameras'] = self.cameras self.out_data['clicked_ok'] = True self.close() #self.destroy() def on_cancel(self): self.close()
class LogViewerDialog(DialogBase): """Logger widget.""" def __init__( self, parent=None, log_folder=LOG_FOLDER, log_filename=LOG_FILENAME, ): """ Logger widget. Parameters ---------- log_folder: str Folder where logs are located log_filename: str Basic name for the rotating log files. """ super(LogViewerDialog, self).__init__(parent=parent) self._data = None self._columns = ['level', 'time', 'module', 'method', 'message'] self._headers = [c.capitalize() for c in self._columns] self._log_filename = log_filename self._log_folder = log_folder # Widgets self.label = QLabel('Select log file:') self.combobox = ComboBoxBase() self.table_logs = QTableWidget(self) self.button_copy = ButtonPrimary('Copy') self.text_search = LineEditSearch() # Widget setup self.table_logs.setAttribute(Qt.WA_LayoutUsesWidgetRect, True) horizontal_header = self.table_logs.horizontalHeader() vertical_header = self.table_logs.verticalHeader() horizontal_header.setStretchLastSection(True) horizontal_header.setSectionResizeMode(QHeaderView.Fixed) vertical_header.setSectionResizeMode(QHeaderView.Fixed) self.table_logs.setSelectionBehavior(QTableWidget.SelectRows) self.table_logs.setEditTriggers(QTableWidget.NoEditTriggers) self.setWindowTitle('Log Viewer') self.setMinimumWidth(800) self.setMinimumHeight(500) self.text_search.setPlaceholderText("Search...") # Layouts top_layout = QHBoxLayout() top_layout.addWidget(self.label) top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.combobox) top_layout.addStretch() top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.text_search) top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.button_copy) layout = QVBoxLayout() layout.addLayout(top_layout) layout.addWidget(SpacerVertical()) layout.addWidget(self.table_logs) self.setLayout(layout) # Signals self.combobox.currentIndexChanged.connect(self.update_text) self.button_copy.clicked.connect(self.copy_item) self.text_search.textChanged.connect(self.filter_text) # Setup() self.setup() self.update_style_sheet() def update_style_sheet(self, style_sheet=None): """Update custom CSS stylesheet.""" if style_sheet is None: style_sheet = load_style_sheet() self.setStyleSheet(style_sheet) def setup(self): """Setup widget content.""" self.combobox.clear() paths = log_files( log_folder=self._log_folder, log_filename=self._log_filename, ) files = [os.path.basename(p) for p in paths] self.combobox.addItems(files) def filter_text(self): """Search for text in the selected log file.""" search = self.text_search.text().lower() for i, data in enumerate(self._data): if any(search in str(d).lower() for d in data.values()): self.table_logs.showRow(i) else: self.table_logs.hideRow(i) def row_data(self, row): """Give the current row data concatenated with spaces.""" data = {} if self._data: length = len(self._data) if 0 >= row < length: data = self._data[row] return data def update_text(self, index): """Update logs based on combobox selection.""" path = os.path.join(self._log_folder, self.combobox.currentText()) self._data = load_log(path) self.table_logs.clear() self.table_logs.setSortingEnabled(False) self.table_logs.setRowCount(len(self._data)) self.table_logs.setColumnCount(len(self._columns)) self.table_logs.setHorizontalHeaderLabels(self._headers) for row, data in enumerate(self._data): for col, col_key in enumerate(self._columns): item = QTableWidgetItem(data.get(col_key, '')) self.table_logs.setItem(row, col, item) for c in [0, 2, 3]: self.table_logs.resizeColumnToContents(c) self.table_logs.resizeRowsToContents() self.table_logs.setSortingEnabled(True) self.table_logs.scrollToBottom() self.table_logs.scrollToTop() self.table_logs.sortByColumn(1, Qt.AscendingOrder) # Make sure there is always a selected row self.table_logs.setCurrentCell(0, 0) def copy_item(self): """Copy selected item to clipboard in markdown format.""" app = QApplication.instance() items = self.table_logs.selectedIndexes() if items: rows = set(sorted(i.row() for i in items)) if self._data: all_data = [self._data[row] for row in rows] dump = json.dumps(all_data, sort_keys=True, indent=4) app.clipboard().setText('```json\n' + dump + '\n```')
class EventsDialog(QDialog): def __init__(self, parent, pos, desc): super().__init__(parent) self.setWindowTitle("Edit Events") self.table = QTableWidget(len(pos), 2) for row, (p, d) in enumerate(zip(pos, desc)): self.table.setItem(row, 0, IntTableWidgetItem(p)) self.table.setItem(row, 1, IntTableWidgetItem(d)) self.table.setHorizontalHeaderLabels(["Position", "Type"]) self.table.horizontalHeader().setStretchLastSection(True) self.table.verticalHeader().setVisible(False) self.table.setShowGrid(False) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSortingEnabled(True) self.table.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.table) hbox = QHBoxLayout() self.add_button = QPushButton("+") self.remove_button = QPushButton("-") buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) hbox.addWidget(self.add_button) hbox.addWidget(self.remove_button) hbox.addStretch() hbox.addWidget(buttonbox) vbox.addLayout(hbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) self.table.itemSelectionChanged.connect(self.toggle_buttons) self.remove_button.clicked.connect(self.remove_event) self.add_button.clicked.connect(self.add_event) self.toggle_buttons() self.resize(300, 500) @Slot() def toggle_buttons(self): """Toggle + and - buttons.""" n_items = len(self.table.selectedItems()) if n_items == 2: # one row (2 items) selected self.add_button.setEnabled(True) self.remove_button.setEnabled(True) elif n_items > 2: # more than one row selected self.add_button.setEnabled(False) self.remove_button.setEnabled(True) else: # no rows selected self.add_button.setEnabled(False) self.remove_button.setEnabled(False) def add_event(self): current_row = self.table.selectedIndexes()[0].row() pos = int(self.table.item(current_row, 0).data(Qt.DisplayRole)) self.table.setSortingEnabled(False) self.table.insertRow(current_row) self.table.setItem(current_row, 0, IntTableWidgetItem(pos)) self.table.setItem(current_row, 1, IntTableWidgetItem(0)) self.table.setSortingEnabled(True) def remove_event(self): rows = {index.row() for index in self.table.selectedIndexes()} self.table.clearSelection() for row in sorted(rows, reverse=True): self.table.removeRow(row)
class LevelsPresetDialog(QDialog): # name of the current preset; whether to set this preset as default; dict of Levels levels_changed = Signal(str, bool, dict) def __init__(self, parent, preset_name, levels): super().__init__(parent) self.preset_name = preset_name self.levels = deepcopy(levels) self.setupUi() self.update_output() def setupUi(self): self.resize(480, 340) self.vbox = QVBoxLayout(self) self.presetLabel = QLabel(self) self.table = QTableWidget(0, 4, self) self.setAsDefaultCheckbox = QCheckBox("Set as default preset", self) self.vbox.addWidget(self.presetLabel) self.vbox.addWidget(self.table) self.vbox.addWidget(self.setAsDefaultCheckbox) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setHorizontalHeaderLabels( ["Show", "Level name", "Preview", "Preview (dark)"]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.horizontalHeader().setSectionsClickable(False) self.table.horizontalHeader().setSectionsMovable(False) self.table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.table.verticalHeader().setVisible(False) self.table.doubleClicked.connect(self.open_level_edit_dialog) self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self.open_menu) buttons = QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons, self) self.vbox.addWidget(self.buttonBox) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.resetButton = self.buttonBox.button(QDialogButtonBox.Reset) self.resetButton.clicked.connect(self.reset) def update_output(self): self.presetLabel.setText("Preset: {}".format(self.preset_name)) self.setAsDefaultCheckbox.setChecked( CONFIG['default_levels_preset'] == self.preset_name) self.table.clearContents() self.table.setRowCount(len(self.levels)) for i, levelname in enumerate(self.levels): level = self.levels[levelname] checkbox = self.get_level_show_checkbox(level) nameItem = QTableWidgetItem(level.levelname) preview, previewDark = self.get_preview_items(level) self.table.setCellWidget(i, 0, checkbox) self.table.setItem(i, 1, nameItem) self.table.setItem(i, 2, preview) self.table.setItem(i, 3, previewDark) def get_level_show_checkbox(self, level): checkbox_widget = QWidget(self.table) checkbox_widget.setStyleSheet("QWidget { background-color:none;}") checkbox = QCheckBox() checkbox.setStyleSheet( "QCheckBox::indicator { width: 15px; height: 15px;}") checkbox.setChecked(level.enabled) checkbox_layout = QHBoxLayout() checkbox_layout.setAlignment(Qt.AlignCenter) checkbox_layout.setContentsMargins(0, 0, 0, 0) checkbox_layout.addWidget(checkbox) checkbox_widget.setLayout(checkbox_layout) return checkbox_widget def get_preview_items(self, level): previewItem = QTableWidgetItem("Log message") previewItem.setBackground(QBrush(level.bg, Qt.SolidPattern)) previewItem.setForeground(QBrush(level.fg, Qt.SolidPattern)) previewItemDark = QTableWidgetItem("Log message") previewItemDark.setBackground(QBrush(level.bgDark, Qt.SolidPattern)) previewItemDark.setForeground(QBrush(level.fgDark, Qt.SolidPattern)) font = QFont(CONFIG.logger_table_font, CONFIG.logger_table_font_size) fontDark = QFont(font) if 'bold' in level.styles: font.setBold(True) if 'italic' in level.styles: font.setItalic(True) if 'underline' in level.styles: font.setUnderline(True) if 'bold' in level.stylesDark: fontDark.setBold(True) if 'italic' in level.stylesDark: fontDark.setItalic(True) if 'underline' in level.stylesDark: fontDark.setUnderline(True) previewItem.setFont(font) previewItemDark.setFont(fontDark) return previewItem, previewItemDark def open_level_edit_dialog(self, index): levelname = self.table.item(index.row(), 1).data(Qt.DisplayRole) level = self.levels[levelname] d = LevelEditDialog(self, level) d.setWindowModality(Qt.NonModal) d.setWindowTitle('Level editor') d.level_changed.connect(self.update_output) d.open() def open_menu(self, position): menu = QMenu(self) preset_menu = menu.addMenu('Presets') preset_menu.addAction('New preset', self.new_preset_dialog) preset_menu.addSeparator() preset_names = CONFIG.get_levels_presets() if len(preset_names) == 0: action = preset_menu.addAction('No presets') action.setEnabled(False) else: delete_menu = menu.addMenu('Delete preset') for name in preset_names: preset_menu.addAction(name, partial(self.load_preset, name)) delete_menu.addAction(name, partial(self.delete_preset, name)) menu.addSeparator() menu.addAction('New level...', self.create_new_level_dialog) if len(self.table.selectedIndexes()) > 0: menu.addAction('Delete selected', self.delete_selected) menu.popup(self.table.viewport().mapToGlobal(position)) def load_preset(self, name): new_levels = CONFIG.load_levels_preset(name) if not new_levels: return self.levels = new_levels self.preset_name = name self.update_output() def delete_preset(self, name): CONFIG.delete_levels_preset(name) if name == self.preset_name: self.reset() def delete_selected(self): selected = self.table.selectionModel().selectedRows() for index in selected: item = self.table.item(index.row(), 1) del self.levels[item.text()] self.update_output() def new_preset_dialog(self): d = QInputDialog(self) d.setLabelText('Enter the new name for the new preset:') d.setWindowTitle('Create new preset') d.textValueSelected.connect(self.create_new_preset) d.open() def create_new_preset(self, name): if name in CONFIG.get_levels_presets(): show_warning_dialog( self, "Preset creation error", 'Preset named "{}" already exists.'.format(name)) return if len(name.strip()) == 0: show_warning_dialog( self, "Preset creation error", 'This preset name is not allowed.'.format(name)) return self.preset_name = name self.update_output() CONFIG.save_levels_preset(name, self.levels) def create_new_level_dialog(self): d = LevelEditDialog(self, creating_new_level=True, level_names=self.levels.keys()) d.setWindowModality(Qt.NonModal) d.setWindowTitle('Level editor') d.level_changed.connect(self.level_changed) d.open() def level_changed(self, level): if level.levelname in self.levels: self.levels.copy_from(level) else: self.levels[level.levelname] = level self.update_output() def accept(self): for i, _ in enumerate(self.levels): checkbox = self.table.cellWidget(i, 0).children()[1] levelname = self.table.item(i, 1).text() self.levels[levelname].enabled = checkbox.isChecked() self.levels_changed.emit(self.preset_name, self.setAsDefaultCheckbox.isChecked(), self.levels) self.done(0) def reject(self): self.done(0) def reset(self): for levelname, level in self.levels.items(): level.copy_from(get_default_level(levelname)) self.update_output()