class GanttConfigure(QDialog): def __init__(self, sim, start, end): QDialog.__init__(self) #self.setCaption("Gantt configuration") self.layout = QVBoxLayout(self) # self._slider = QxtSpanSliderWidget( # sim.observe_window[0] // sim.cycles_per_ms, # min(sim.now(), sim.observe_window[1]) // sim.cycles_per_ms, # self) self._slider = QxtSpanSliderWidget( 0, min(sim.now(), sim.duration) // sim.cycles_per_ms, self) self._slider.setSpan(start, end) self.layout.addWidget(self._slider) self._list_elements = QListWidget(self) for processor in sim.processors: item = QListWidgetItem(processor.name, self._list_elements) item.setData(Qt.UserRole, processor) self._list_elements.addItem(item) for task in sim.task_list: item = QListWidgetItem(task.name, self._list_elements) item.setData(Qt.UserRole, task) self._list_elements.addItem(item) #self._list_elements.setDragDropMode(QListWidget.InternalMove) for row in range(0, self._list_elements.count()): self._list_elements.item(row).setCheckState(Qt.Checked) self.layout.addWidget(self._list_elements) buttons = QWidget(self) buttons_layout = QHBoxLayout() buttons.setLayout(buttons_layout) buttons_layout.addStretch() ok_button = QPushButton("Ok") cancel_button = QPushButton("Cancel") ok_button.clicked.connect(self.accept) cancel_button.clicked.connect(self.reject) buttons_layout.addWidget(ok_button) buttons_layout.addWidget(cancel_button) self.layout.addWidget(buttons) def get_start_date(self): return self._slider.lowerValue def get_end_date(self): return self._slider.upperValue def get_selected_items(self): res = [] for row in range(0, self._list_elements.count()): if self._list_elements.item(row).checkState() == Qt.Checked: data = self._list_elements.item(row).data(Qt.UserRole) res.append(data) return res
class MetricsWindowTab(QWidget): def __init__(self, parent=None): super().__init__(parent) self.name = self.tr("Metrics Window") self.inputTextLabel = QLabel(self.tr("Default text:"), self) self.inputTextList = QListWidget(self) self.inputTextList.setDragDropMode(QAbstractItemView.InternalMove) self.addItemButton = QPushButton("+", self) self.addItemButton.clicked.connect(self.addItem) self.removeItemButton = QPushButton("−", self) self.removeItemButton.clicked.connect(self.removeItem) layout = QGridLayout(self) l = 0 layout.addWidget(self.inputTextLabel, l, 0, 1, 3) l += 1 layout.addWidget(self.inputTextList, l, 0, 1, 3) l += 1 layout.addWidget(self.addItemButton, l, 0) layout.addWidget(self.removeItemButton, l, 1) self.setLayout(layout) self.readSettings() def addItem(self): item = QListWidgetItem(self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.inputTextList.setCurrentItem(item) self.inputTextList.editItem(item) self.removeItemButton.setEnabled(True) def removeItem(self): i = self.inputTextList.currentRow() self.inputTextList.takeItem(i) if not self.inputTextList.count(): self.removeItemButton.setEnabled(False) def readSettings(self): self.inputTextList.clear() entries = settings.metricsWindowComboBoxItems() for entry in entries: item = QListWidgetItem(entry, self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) if not len(entries): self.removeItemButton.setEnabled(False) def writeSettings(self): entries = [] for i in range(self.inputTextList.count()): item = self.inputTextList.item(i) entries.append(item.text()) settings.setMetricsWindowComboBoxItems(entries)
class PatchesPage(QtWidgets.QWizardPage): def __init__(self, Wizard, parent=None): super(PatchesPage, self).__init__(parent) self.base = Wizard.base self.setTitle(self.tr("Patches page")) self.setSubTitle(self.tr("Select patches")) self.addButton = QPushButton("+") self.removeButton = QPushButton("-") patchesLabel = QLabel("Patches") self.listPatches = QListWidget() self.addButton.setMaximumWidth(68) self.addButton.setMaximumHeight(60) self.addButton.clicked.connect(self.openPatchesPageFileDialog) self.removeButton.setMaximumWidth(68) self.removeButton.setMaximumHeight(60) self.removeButton.clicked.connect(self.removeItemFromListWidget) grid = QGridLayout() grid.addWidget(patchesLabel, 0, 0) grid.addWidget(self.addButton, 0, 1,) grid.addWidget(self.removeButton, 0, 2) grid.addWidget(self.listPatches, 1, 0, 1, 0) self.setLayout(grid) def removeItemFromListWidget(self): self.item = self.listPatches.takeItem(self.listPatches.currentRow()) self.item = None def openPatchesPageFileDialog(self): brows = QFileDialog() self.getPath = brows.getOpenFileName(self, "Choose patches", "/home", "All files (*)") self.newPath = self.getPath[0] self.listPatches.addItem(self.newPath) def validatePage(self): self.itemsCount = self.listPatches.count() self.pathes = [] for i in range(0, self.itemsCount): self.pathes.append(self.listPatches.item(i).text()) self.base.apply_patches(self.pathes) self.base.run_patched_sources_analysis() return True def nextId(self): return Wizard.PageScripts
class JavaScriptExceptionsPanel(SettingsPanel): def __init__(self, parent=None): super(JavaScriptExceptionsPanel, self).__init__(parent) domainEntryRow = custom_widgets.LineEditRow(tr("Add domain:"), self) self.domainEntry = domainEntryRow.lineEdit self.domainEntry.returnPressed.connect(self.addDomain) self.layout().addWidget(domainEntryRow) self.addDomainButton = QPushButton(tr("Add")) self.addDomainButton.clicked.connect(self.addDomain) domainEntryRow.layout().addWidget(self.addDomainButton) self.domainList = QListWidget(self) self.layout().addWidget(self.domainList) self.removeDomainButton = QPushButton(tr("Remove")) self.removeDomainButton.clicked.connect(lambda: self.removeDomain(True)) self.layout().addWidget(self.removeDomainButton) self.removeAction = QAction(self) self.removeAction.setShortcut("Del") self.removeAction.triggered.connect(self.removeDomain) self.addAction(self.removeAction) def removeDomain(self, forceFocus=False): if self.domainList.hasFocus() or forceFocus: self.domainList.takeItem(self.domainList.row(self.domainList.currentItem())) def addDomain(self): self.domainList.addItem(self.domainEntry.text()) self.domainEntry.clear() def loadSettings(self): settings.js_exceptions = settings.setting_to_list("content/JavaScriptExceptions") self.domainList.clear() for f in settings.js_exceptions: self.domainList.addItem(f) def saveSettings(self): settings.js_exceptions = [self.domainList.item(f).text() for f in range(0, self.domainList.count())] settings.settings.setValue("content/JavaScriptExceptions", settings.js_exceptions) settings.settings.sync()
class AdremoverSettingsPanel(SettingsPanel): def __init__(self, parent=None): super(AdremoverSettingsPanel, self).__init__(parent) filterEntryRow = custom_widgets.LineEditRow(tr("Add filter:"), self) self.filterEntry = filterEntryRow.lineEdit self.filterEntry.returnPressed.connect(self.addFilter) self.layout().addWidget(filterEntryRow) self.addFilterButton = QPushButton(tr("Add")) self.addFilterButton.clicked.connect(self.addFilter) filterEntryRow.layout().addWidget(self.addFilterButton) # Ad Remover filter list. self.filterList = QListWidget(self) self.layout().addWidget(self.filterList) self.removeFilterButton = QPushButton(tr("Remove")) self.removeFilterButton.clicked.connect(lambda: self.removeFilter(True)) self.layout().addWidget(self.removeFilterButton) self.removeAction = QAction(self) self.removeAction.setShortcut("Del") self.removeAction.triggered.connect(self.removeFilter) self.addAction(self.removeAction) def removeFilter(self, forceFocus=False): if self.filterList.hasFocus() or forceFocus: self.filterList.takeItem(self.filterList.row(self.filterList.currentItem())) def addFilter(self): self.filterList.addItem(self.filterEntry.text()) self.filterEntry.clear() def loadSettings(self): settings.load_adremover_filters() self.filterList.clear() for f in settings.adremover_filters: self.filterList.addItem(f) def saveSettings(self): settings.adremover_filters = [self.filterList.item(f).text() for f in range(0, self.filterList.count())] settings.save_adremover_filters()
class SelectedLinesWidget(QWidget): """ Widget to show and enable lines to be selected inp : LineList Input LineList init_select : str or list of indices str -- 'All' """ def __init__(self, inp, parent=None, init_select=None, plot_widget=None): """ """ super(SelectedLinesWidget, self).__init__(parent) self.parent = parent # Line list Table if isinstance(inp, LineList): self.lines = inp._data self.llst = inp elif isinstance(inp, Table): raise ValueError('SelectedLineWidget: DEPRECATED') else: raise ValueError('SelectedLineWidget: Wrong type of input') self.plot_widget = plot_widget # Create the line list line_label = QLabel('Lines:') self.lines_widget = QListWidget(self) self.lines_widget.setSelectionMode(QAbstractItemView.MultiSelection) # Initialize list self.item_flg = 0 self.init_list() # Initial selection if init_select is None: self.selected = [0] elif init_select == 'All': self.selected = [] for ii in range(self.lines_widget.count()): self.lines_widget.item(ii).setSelected(True) self.selected.append(ii) else: self.selected = init_select if len(self.selected) == 0: self.selected = [0] for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem(self.lines_widget.item( self.selected[0])) # Events self.lines_widget.itemSelectionChanged.connect(self.on_item_change) # Layout vbox = QVBoxLayout() vbox.addWidget(line_label) vbox.addWidget(self.lines_widget) self.setLayout(vbox) def init_list(self): nlin = len(self.lines['wrest']) for ii in range(nlin): self.lines_widget.addItem('{:s} :: {:.3f} :: {}'.format( self.lines['name'][ii], self.lines['wrest'][ii], self.lines['f'][ii])) def on_item_change(self): #,item): # For big changes if self.item_flg == 1: return all_items = [ self.lines_widget.item(ii) for ii in range(self.lines_widget.count()) ] sel_items = self.lines_widget.selectedItems() self.selected = [all_items.index(isel) for isel in sel_items] self.selected.sort() #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() # Update llist try: self.plot_widget.llist['show_line'] = self.selected except AttributeError: if self.parent is not None: self.parent.updated_slines(self.selected) return else: self.plot_widget.on_draw() def on_list_change(self, llist): # Clear if not isinstance(llist, LineList): raise ValueError('Expecting LineList!!') self.item_flg = 1 self.lines = llist._data self.llst = llist self.lines_widget.clear() # Initialize self.init_list() # Set selected for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem(self.lines_widget.item( self.selected[0])) self.item_flg = 0
class Scheduler: def showDialog(self, currentCard=None): if currentCard: self.did = currentCard.did elif mw._selectedDeck(): self.did = mw._selectedDeck()['id'] else: return if not self._getCardInfo(self.did): showInfo('Please select an Incremental Reading deck.') return dialog = QDialog(mw) layout = QVBoxLayout() self.cardListWidget = QListWidget() self.cardListWidget.setAlternatingRowColors(True) self.cardListWidget.setSelectionMode( QAbstractItemView.ExtendedSelection) self.cardListWidget.setWordWrap(True) self.cardListWidget.itemDoubleClicked.connect(lambda: self.showBrowser( self.cardListWidget.currentItem().data(Qt.UserRole)['nid'])) self._updateListItems() upButton = QPushButton('Up') upButton.clicked.connect(self._moveUp) downButton = QPushButton('Down') downButton.clicked.connect(self._moveDown) topButton = QPushButton('Top') topButton.clicked.connect(self._moveToTop) bottomButton = QPushButton('Bottom') bottomButton.clicked.connect(self._moveToBottom) randomizeButton = QPushButton('Randomize') randomizeButton.clicked.connect(self._randomize) controlsLayout = QHBoxLayout() controlsLayout.addWidget(topButton) controlsLayout.addWidget(upButton) controlsLayout.addWidget(downButton) controlsLayout.addWidget(bottomButton) controlsLayout.addStretch() controlsLayout.addWidget(randomizeButton) buttonBox = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Save) buttonBox.accepted.connect(dialog.accept) buttonBox.rejected.connect(dialog.reject) buttonBox.setOrientation(Qt.Horizontal) layout.addLayout(controlsLayout) layout.addWidget(self.cardListWidget) layout.addWidget(buttonBox) dialog.setLayout(layout) dialog.setWindowModality(Qt.WindowModal) dialog.resize(500, 500) choice = dialog.exec_() if choice == 1: cids = [] for i in range(self.cardListWidget.count()): card = self.cardListWidget.item(i).data(Qt.UserRole) cids.append(card['id']) self.reorder(cids) def _updateListItems(self): cardInfo = self._getCardInfo(self.did) self.cardListWidget.clear() posWidth = len(str(len(cardInfo) + 1)) for i, card in enumerate(cardInfo, start=1): if self.settings['prioEnabled']: info = card['priority'] else: info = str(i).zfill(posWidth) title = sub('\s+', ' ', stripHTML(card['title'])) text = self.settings['organizerFormat'].format(info=info, title=title) item = QListWidgetItem(text) item.setData(Qt.UserRole, card) self.cardListWidget.addItem(item) def _moveToTop(self): selected = self._getSelected() if not selected: showInfo('Please select one or several items.') return selected.reverse() for item in selected: self.cardListWidget.takeItem(self.cardListWidget.row(item)) self.cardListWidget.insertItem(0, item) item.setSelected(True) self.cardListWidget.scrollToTop() def _moveUp(self): selected = self._getSelected() if not selected: showInfo('Please select one or several items.') return if self.cardListWidget.row(selected[0]) == 0: return for item in selected: row = self.cardListWidget.row(item) self.cardListWidget.takeItem(row) self.cardListWidget.insertItem(row - 1, item) item.setSelected(True) self.cardListWidget.scrollToItem(item) def _moveDown(self): selected = self._getSelected() if not selected: showInfo('Please select one or several items.') return selected.reverse() if (self.cardListWidget.row( selected[0]) == self.cardListWidget.count() - 1): return for item in selected: row = self.cardListWidget.row(item) self.cardListWidget.takeItem(row) self.cardListWidget.insertItem(row + 1, item) item.setSelected(True) self.cardListWidget.scrollToItem(item) def _moveToBottom(self): selected = self._getSelected() if not selected: showInfo('Please select one or several items.') return for item in selected: self.cardListWidget.takeItem(self.cardListWidget.row(item)) self.cardListWidget.insertItem(self.cardListWidget.count(), item) item.setSelected(True) self.cardListWidget.scrollToBottom() def _getSelected(self): return [ self.cardListWidget.item(i) for i in range(self.cardListWidget.count()) if self.cardListWidget.item(i).isSelected() ] def _randomize(self): allItems = [ self.cardListWidget.takeItem(0) for _ in range(self.cardListWidget.count()) ] if self.settings['prioEnabled']: maxPrio = len(self.settings['priorities']) - 1 for item in allItems: priority = item.data(Qt.UserRole)['priority'] if priority != '': item.contNewPos = gauss(maxPrio - int(priority), maxPrio / 20) else: item.contNewPos = float('inf') allItems.sort(key=lambda item: item.contNewPos) else: shuffle(allItems) for item in allItems: self.cardListWidget.addItem(item) def answer(self, card, ease): if self.settings['prioEnabled']: # reposition the card at the end of the organizer cardCount = len(self._getCardInfo(card.did)) self.reposition(card, cardCount) return if ease == SCHEDULE_EXTRACT: value = self.settings['extractValue'] randomize = self.settings['extractRandom'] method = self.settings['extractMethod'] elif ease == SCHEDULE_SOON: value = self.settings['soonValue'] randomize = self.settings['soonRandom'] method = self.settings['soonMethod'] elif ease == SCHEDULE_LATER: value = self.settings['laterValue'] randomize = self.settings['laterRandom'] method = self.settings['laterMethod'] elif ease == SCHEDULE_CUSTOM: self.reposition(card, 1) self.showDialog(card) return if method == 'percent': totalCards = len([c['id'] for c in self._getCardInfo(card.did)]) newPos = totalCards * (value / 100) elif method == 'count': newPos = value if randomize: newPos = gauss(newPos, newPos / 10) newPos = max(1, round(newPos)) self.reposition(card, newPos) if ease != SCHEDULE_EXTRACT: tooltip('Card moved to position {}'.format(newPos)) def reposition(self, card, newPos): cids = [c['id'] for c in self._getCardInfo(card.did)] mw.col.sched.forgetCards(cids) cids.remove(card.id) newOrder = cids[:newPos - 1] + [card.id] + cids[newPos - 1:] mw.col.sched.sortCards(newOrder) def reorder(self, cids): mw.col.sched.forgetCards(cids) mw.col.sched.sortCards(cids) def _getCardInfo(self, did): cardInfo = [] for cid, nid in mw.col.db.execute( 'select id, nid from cards where did = ?', did): note = mw.col.getNote(nid) if note.model()['name'] == self.settings['modelName']: if self.settings['prioEnabled']: prio = note[self.settings['prioField']] else: prio = None cardInfo.append({ 'id': cid, 'nid': nid, 'title': note[self.settings['titleField']], 'priority': prio }) return cardInfo def showBrowser(self, nid): browser = dialogs.open('Browser', mw) browser.form.searchEdit.lineEdit().setText('nid:' + str(nid)) browser.onSearchActivated()
class GridControl(QGroupBox): """ Widget for controlling grids in Main Window. """ """Signals""" sig_place_grid = pyqtSignal() sig_crop_region = pyqtSignal(list, float, str) sig_generate_rotated_image = pyqtSignal(float) def __init__(self, parent, title, num_rows, num_cols): super().__init__(parent=parent, title=title) # self.gc_box = QGroupBox(parent=self, title='Grid control') self.layout = QGridLayout() self.label_checkbox = QCheckBox('Toggle labels', parent=self) self.col_label = QLabel('Columns:', parent=self) self.row_label = QLabel('Rows: ', parent=self) self.col_spinbox = QSpinBox(parent=self, value=num_cols, maximum=26, minimum=1) self.row_spinbox = QSpinBox(parent=self, value=num_rows, maximum=40, minimum=1) self.btn_like_grid = QPushButton('I like this grid!', parent=self) self.grid_list = QListWidget(parent=self) self.btn_crop_grid = QPushButton('Crop', parent=self) self.btn_del_grid = QPushButton('Delete', parent=self) self.parent = self.parentWidget() self.configure_gui() self.configure_signals() def configure_gui(self): """ Configure grid control """ """Layout""" self.layout.addWidget(self.row_label, 0, 0) self.layout.addWidget(self.col_label, 0, 1) self.layout.addWidget(self.col_spinbox, 1, 1) self.layout.addWidget(self.row_spinbox, 1, 0) self.layout.addWidget(self.label_checkbox, 2, 0, 1, 2) self.layout.addWidget(self.btn_like_grid, 3, 0, 1, 2) self.layout.addWidget(self.grid_list, 4, 0, 1, 2) self.layout.addWidget(self.btn_crop_grid, 5, 0) self.layout.addWidget(self.btn_del_grid, 5, 1) self.setLayout(self.layout) self.setGeometry(0, 0, 150, 400) self.move(520, 90) """Checkbox""" self.label_checkbox.resize(self.label_checkbox.sizeHint()) self.label_checkbox.setCheckState(Qt.Unchecked) self.label_checkbox.stateChanged.connect(self.label_checkbox_checked) self.label_checkbox_checked() # update grid to default settings """Row/column spinboxes""" self.row_spinbox.valueChanged.connect(self.parent.set_num_rows) self.col_spinbox.valueChanged.connect(self.parent.set_num_cols) """I like this grid! button""" self.btn_like_grid.resize(self.btn_like_grid.sizeHint()) self.btn_like_grid.setEnabled(False) self.btn_like_grid.clicked.connect(self.like_grid_button_clicked) """Grid list""" self.grid_list.setSelectionMode(QAbstractItemView.SingleSelection) self.grid_list.itemSelectionChanged.connect(self.change_selected_grid) self.grid_list.itemDoubleClicked.connect(self.change_grid_name) """Crop and del button""" self.btn_crop_grid.setEnabled(False) self.btn_crop_grid.clicked.connect(self.crop_grid_button_clicked) self.btn_del_grid.setEnabled(False) self.btn_del_grid.clicked.connect(self.del_grid_button_clicked) def configure_signals(self): self.sig_place_grid.connect(self.parent.place_grid) self.sig_generate_rotated_image.connect(self.parent.rotate_image) @pyqtSlot() def change_selected_grid(self): """ Call when selected grid in `GridList` changes. `Crop` and `Del` are enabled, and selected grid changes def_color. Returns ------- """ grid_list = [] for i in range(self.grid_list.count()): grid_list.append(self.grid_list.item(i)) grid_list_selected = self.grid_list.selectedItems() for grid_item in grid_list: grid_item.grid.set_color_and_thickness(color=Qt.blue) for grid_item in grid_list_selected: grid_item.grid.set_color_and_thickness(color=Qt.red, thickness=3.) if len(grid_list_selected) > 0: self.btn_crop_grid.setEnabled(True) self.btn_del_grid.setEnabled(True) else: self.btn_crop_grid.setEnabled(False) self.btn_del_grid.setEnabled(False) @pyqtSlot() def change_grid_name(self): item = self.grid_list.selectedItems()[0] self.grid_list.editItem(item) @pyqtSlot() def crop_grid_button_clicked(self): """ Crop images using the selected grid in `GridList`. For each grid square, emit signal `sig_crop_region`, passing `QRectF` scaled coordinates of region to crop. Returns ------- """ """Collect grid information""" grid_item = self.grid_list.selectedItems()[0] image_coordinates = grid_item.grid.image_coordinates num_rows = grid_item.grid.num_rows num_cols = grid_item.grid.num_cols angle = grid_item.grid.phi alphabet = string.ascii_uppercase """Create directory and crop images""" directory = grid_item.text() if not os.path.exists(directory): os.makedirs(directory) """generate counter-rotated_image for cropping""" self.sig_generate_rotated_image.emit(-angle) """Crop images""" for ix, image_coordinate in enumerate(image_coordinates): """Get bg_image coordinate""" tl_x, tl_y = image_coordinate[0, 0] tl = QPointF(tl_x, tl_y) # top left bl_x, bl_y = image_coordinate[0, 1] bl = QPointF(bl_x, bl_y) # top right tr_x, tr_y = image_coordinate[1, 0] tr = QPointF(tr_x, tr_y) # bottom left br_x, br_y = image_coordinate[1, 1] br = QPointF(br_x, br_y) # bottom right """Find polygon to be cropped""" coords = [tl, bl, br, tr] # pol = QPolygonF(coords) """Determine file name""" col = (ix // num_rows) row = (ix % num_rows) + 1 file_name = str(alphabet[col]) + str(row) full_path = os.path.join(directory, file_name) self.sig_crop_region.emit(coords, angle, full_path) else: print('Can\'t crop bg_image: folder already exist') @pyqtSlot() def del_grid_button_clicked(self): """ Delete grid from `GridList`. Returns ------- """ """Find selected GridListItemWidget """ grid_item = self.grid_list.selectedItems()[0] """Remove from GridList""" item_ix = self.grid_list.row(grid_item) self.grid_list.takeItem(item_ix) """Remove from scene""" grid_item.grid.clear_grid() @pyqtSlot() def like_grid_button_clicked(self): """ Add grid to `grid_list` and emit `sig_place_grid`. Returns ------- """ self.btn_like_grid.setEnabled(False) """Add grid to placed grid widget""" item = GridListWidgetItem(parent=self.grid_list) item.set_grid(self.parent.view.current_grid) # TODO ItemIsEditable does not work # item.setFlags(Qt.ItemIsEditable) # item.setFlags(Qt.ItemIsSelectable) self.sig_place_grid.emit() @pyqtSlot() def label_checkbox_checked(self): """ Enable/disable labels "A1,..,etc." in current grid. :return: """ check_state = self.label_checkbox.checkState() grid = self.parent.view.current_grid if check_state == Qt.Checked: grid.label_enabled = True elif check_state == Qt.Unchecked: grid.label_enabled = False """Redraw grid""" try: tl, br = grid.tl_br_qpointf except ValueError: pass else: tl_x = tl.x() tl_y = tl.y() br_x = br.x() br_y = br.y() angle = grid.phi grid.draw_grid(tl_x, tl_y, br_x, br_y, angle)
class GstPipeEdit(QDialog): def __init__(self, pipe, preferences_mode=False, **kwargs): super().__init__(**kwargs) self.setWindowTitle('Edit Pipeline') self.setWindowModality(Qt.ApplicationModal) self.setLayout(QGridLayout()) self.setMaximumSize(500, 400) self.setMinimumSize(500, 400) self.resize(500, 400) self._preferences_mode = preferences_mode # Input selection self.inputBox = QComboBox(self) self.layout().addWidget(self.inputBox, 0, 0, 1, 3) self.init_inputs(pipe) # Current plugins list self.currentList = QListWidget(self) self.currentList.setDragEnabled(True) self.currentList.setDragDropMode(QAbstractItemView.InternalMove) self.layout().addWidget(self.currentList, 1, 0, 3, 1) self.init_current_plugins(pipe) # Add/Remove plugins buttons self.pluginsLayout = QVBoxLayout(self) self.layout().addLayout(self.pluginsLayout, 2, 1) self.addButton = QPushButton(self) self.addButton.setIcon(QIcon.fromTheme('go-previous')) self.addButton.clicked.connect(self.__add_plugin) self.pluginsLayout.addWidget(self.addButton) self.pluginsLayout.setAlignment(self.addButton, Qt.AlignHCenter) self.delButton = QPushButton(self) self.delButton.setIcon(QIcon.fromTheme('go-next')) self.delButton.clicked.connect(self.__remove_plugin) self.pluginsLayout.addWidget(self.delButton) self.pluginsLayout.setAlignment(self.delButton, Qt.AlignHCenter) # Available plugins list self.availableList = QListWidget(self) self.layout().addWidget(self.availableList, 1, 2, 3, 1) self.init_available_plugins(pipe) # Output selection self.outputBox = QComboBox(self) self.layout().addWidget(self.outputBox, 4, 0, 1, 3) self.init_outputs(pipe) # Confirm/Cancel buttons self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.layout().addWidget(self.dialogButtons, 5, 0, 1, 3) self.dialogButtons.accepted.connect(self.accept) self.dialogButtons.rejected.connect(self.reject) def init_inputs(self, pipe): if self._preferences_mode: self.inputBox.setEnabled(False) else: inputs = sorted(elements.inputs()) self.inputBox.addItems(inputs) self.inputBox.setEnabled(len(inputs) > 1) if pipe != '': self.inputBox.setCurrentIndex(inputs.index(pipe.split('!')[0])) def init_outputs(self, pipe): outputs = sorted(elements.outputs()) self.outputBox.addItems(outputs) self.outputBox.setEnabled(len(outputs) > 1) if pipe != '': self.outputBox.setCurrentIndex(outputs.index(pipe.split('!')[-1])) def init_current_plugins(self, pipe): start = 0 if self._preferences_mode else 1 for plugin in pipe.split('!')[start:-1]: self.currentList.addItem(plugin) def init_available_plugins(self, pipe): currents = pipe.split('!') for plugin in elements.plugins().values(): if plugin.Name not in currents: self.availableList.addItem(plugin.Name) def get_pipe(self): pipe = [] if self._preferences_mode else [self.inputBox.currentText()] for n in range(self.currentList.count()): pipe.append(self.currentList.item(n).text()) pipe.append(self.outputBox.currentText()) return '!'.join(pipe) def __add_plugin(self): item = self.availableList.takeItem(self.availableList.currentRow()) self.currentList.addItem(item) def __remove_plugin(self): item = self.currentList.takeItem(self.currentList.currentRow()) self.availableList.addItem(item)
class GalleryListView(QWidget): SERIES = pyqtSignal(list) def __init__(self, parent=None, modal=False): super().__init__(parent) self.setWindowFlags(Qt.Dialog) layout = QVBoxLayout() self.setLayout(layout) if modal: frame = QFrame() frame.setFrameShape(frame.StyledPanel) modal_layout = QHBoxLayout() frame.setLayout(modal_layout) layout.addWidget(frame) info = QLabel('This mode let\'s you add galleries from ' + 'different folders.') f_folder = QPushButton('Add folders') f_folder.clicked.connect(self.from_folder) f_files = QPushButton('Add files') f_files.clicked.connect(self.from_files) modal_layout.addWidget(info, 3, Qt.AlignLeft) modal_layout.addWidget(f_folder, 0, Qt.AlignRight) modal_layout.addWidget(f_files, 0, Qt.AlignRight) check_layout = QHBoxLayout() layout.addLayout(check_layout) if modal: check_layout.addWidget(QLabel('Please uncheck galleries you do' + ' not want to add. (Exisiting galleries won\'t be added'), 3) else: check_layout.addWidget(QLabel('Please uncheck galleries you do' + ' not want to add. (Existing galleries are hidden)'), 3) self.check_all = QCheckBox('Check/Uncheck All', self) self.check_all.setChecked(True) self.check_all.stateChanged.connect(self.all_check_state) check_layout.addWidget(self.check_all) self.view_list = QListWidget() self.view_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.view_list.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) layout.addWidget(self.view_list) add_btn = QPushButton('Add checked') add_btn.clicked.connect(self.return_gallery) cancel_btn = QPushButton('Cancel') cancel_btn.clicked.connect(self.close_window) btn_layout = QHBoxLayout() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) btn_layout.addWidget(spacer) btn_layout.addWidget(add_btn) btn_layout.addWidget(cancel_btn) layout.addLayout(btn_layout) self.resize(500,550) frect = self.frameGeometry() frect.moveCenter(QDesktopWidget().availableGeometry().center()) self.move(frect.topLeft()) self.setWindowTitle('Gallery List') def all_check_state(self, new_state): row = 0 done = False while not done: item = self.view_list.item(row) if item: row += 1 if new_state == Qt.Unchecked: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) else: done = True def add_gallery(self, item, name): """ Constructs an widgetitem to hold the provided item, and adds it to the view_list """ assert isinstance(name, str) gallery_item = GalleryListItem(item) gallery_item.setText(name) gallery_item.setFlags(gallery_item.flags() | Qt.ItemIsUserCheckable) gallery_item.setCheckState(Qt.Checked) self.view_list.addItem(gallery_item) def return_gallery(self): gallery_list = [] row = 0 done = False while not done: item = self.view_list.item(row) if not item: done = True else: if item.checkState() == Qt.Checked: gallery_list.append(item.gallery) row += 1 self.SERIES.emit(gallery_list) self.close() def from_folder(self): file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.DirectoryOnly) file_dialog.setOption(QFileDialog.DontUseNativeDialog, True) file_view = file_dialog.findChild(QListView, 'listView') if file_view: file_view.setSelectionMode(QAbstractItemView.MultiSelection) f_tree_view = file_dialog.findChild(QTreeView) if f_tree_view: f_tree_view.setSelectionMode(QAbstractItemView.MultiSelection) if file_dialog.exec(): for path in file_dialog.selectedFiles(): self.add_gallery(path, os.path.split(path)[1]) def from_files(self): gallery_list = QFileDialog.getOpenFileNames(self, 'Select 1 or more gallery to add', filter='Archives (*.zip *.cbz)') for path in gallery_list[0]: #Warning: will break when you add more filters if len(path) != 0: self.add_gallery(path, os.path.split(path)[1]) def close_window(self): msgbox = QMessageBox() msgbox.setText('Are you sure you want to cancel?') msgbox.setStandardButtons(msgbox.Yes | msgbox.No) msgbox.setDefaultButton(msgbox.No) msgbox.setIcon(msgbox.Question) if msgbox.exec() == QMessageBox.Yes: self.close()
class GrubList(QDialog): """Classe représentant la liste des répertoires GRUB""" scanner = Scanner("/", "*/grub/grub.cfg") newCurrentItem = pyqtSignal(str) def __init__(self, parent=None, text="Choisissez un répertoire GRUB", allowNone=True): QDialog.__init__(self, parent) # Création des éléments # Labels label = QLabel(text) label.setAlignment(Qt.AlignHCenter) self.scanText = QLabel("No scan running") self.scanText.setAlignment(Qt.AlignHCenter) # List View self.grub_list = QListWidget() self.grub_list.setSelectionMode(QAbstractItemView.SingleSelection) # Buttons self.scanButton = QPushButton("Scanner") self.add = QPushButton("Ajouter") self.scanButton.clicked.connect(self.scan) self.add.clicked.connect(self.openSelectionDialog) self.scanButton.setToolTip("Cette opération peut être <b>très</b> longue !") if allowNone: buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) buttonBox.rejected.connect(self.reject) else: buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) buttonBox.accepted.connect(self.accept) # Progressbar self.progressbar = QProgressBar() self.progressbar.setEnabled(False) # Création des Layouts # Horizontal hbox = QHBoxLayout() hbox.addWidget(self.scanButton) hbox.addWidget(self.add) # Vertical vbox = QVBoxLayout() vbox.addWidget(label) vbox.addWidget(self.grub_list) vbox.addLayout(hbox) vbox.addWidget(self.scanText) vbox.addWidget(self.progressbar) vbox.addWidget(buttonBox) # Affichage de l'interface self.setLayout(vbox) #Signals self.scanner.found_rep.connect(self.add_items) self.scanner.started.connect(self._scan_started) self.scanner.finished.connect(self._scan_finished) self.scanner.max_changed.connect(self.progressbar.setMaximum) self.scanner.value_changed.connect(self.progressbar.setValue) self.scanner.dir_scanned.connect(self._setScanText) # Ajout de /boot/grub s'il existe if path.Path("/boot/grub/grub.cfg").exists(): self.add_item("/boot/grub") def selectGrubRep(self): self.setModal(True) result = self.exec_() if result == QDialog.Accepted: grubRep = self.getGrubRep() return grubRep else: return False def scan(self): warning = QMessageBox(self) msg = ("Cette opération peut être très longue.\n" "Le logiciel va analyser toute votre arborescence de fichier " "pour chercher un éventuel dossier contenant la configuration de GRUB.") warning.setText(msg) warning.setInformativeText("Êtes-vous sûr de vouloir continuer ?") warning.setStandardButtons(QMessageBox.Yes | QMessageBox.No) warning.setDefaultButton(QMessageBox.No) warning.setWindowTitle("Attention") answer = warning.exec_() if answer == QMessageBox.Yes: self.scanner.start() def openSelectionDialog(self): dir = QFileDialog.getExistingDirectory(self, "Sélectionner un répertoire GRUB", expanduser('~')) if (path.Path(dir) / "grub.cfg").exists(): self.add_item(dir) elif dir: error = QMessageBox(self) msg = "{} n'est pas un répertoire GRUB valide !".format(dir) error.setText(msg) error.setWindowTitle("Répertoire non valide") error.exec_() def getGrubRep(self): dir = self.grub_list.selectedItems() try: dir = dir[0].text() except IndexError: return False else: return dir @pyqtSlot(list) def add_items(self, items): for item in items: self.add_item(item) self.grub_list.setCurrentRow(0) @pyqtSlot(str) def add_item(self, dir): item = None for i in range(self.grub_list.count()): if self.grub_list.item(i).text() == dir: item = self.grub_list.item(i) if not item: item = QListWidgetItem(dir, self.grub_list) self.grub_list.setCurrentItem(item) @pyqtSlot() def _scan_started(self): self.progressbar.setEnabled(True) self.progressbar.setMinimum(0) self.scanButton.setEnabled(False) self.add.setEnabled(False) @pyqtSlot() def _scan_finished(self): self.progressbar.reset() self.progressbar.setEnabled(False) self.scanText.setText("No scan running") self.scanButton.setEnabled(True) self.add.setEnabled(True) @pyqtSlot(str) def _setScanText(self, text): self.scanText.setText("Scanning {}...".format(text))
class SelectedLinesWidget(QWidget): """ Widget to show and enable lines to be selected inp : LineList Input LineList init_select : str or list of indices str -- 'All' """ def __init__(self, inp, parent=None, init_select=None, plot_widget=None): """ """ super(SelectedLinesWidget, self).__init__(parent) self.parent=parent # Line list Table if isinstance(inp, LineList): self.lines = inp._data self.llst = inp elif isinstance(inp,Table): raise ValueError('SelectedLineWidget: DEPRECATED') else: raise ValueError('SelectedLineWidget: Wrong type of input') self.plot_widget = plot_widget # Create the line list line_label = QLabel('Lines:') self.lines_widget = QListWidget(self) self.lines_widget.setSelectionMode(QAbstractItemView.MultiSelection) # Initialize list self.item_flg = 0 self.init_list() # Initial selection if init_select is None: self.selected = [0] elif init_select == 'All': self.selected = [] for ii in range(self.lines_widget.count()): self.lines_widget.item(ii).setSelected(True) self.selected.append(ii) else: self.selected = init_select if len(self.selected) == 0: self.selected = [0] for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem( self.lines_widget.item( self.selected[0] ) ) # Events self.lines_widget.itemSelectionChanged.connect(self.on_item_change) # Layout vbox = QVBoxLayout() vbox.addWidget(line_label) vbox.addWidget(self.lines_widget) self.setLayout(vbox) def init_list(self): nlin = len(self.lines['wrest']) for ii in range(nlin): self.lines_widget.addItem('{:s} :: {:.3f} :: {}'.format(self.lines['name'][ii], self.lines['wrest'][ii].value, self.lines['f'][ii])) def on_item_change(self): #,item): # For big changes if self.item_flg == 1: return all_items = [self.lines_widget.item(ii) for ii in range(self.lines_widget.count())] sel_items = self.lines_widget.selectedItems() self.selected = [all_items.index(isel) for isel in sel_items] self.selected.sort() #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() # Update llist try: self.plot_widget.llist['show_line'] = self.selected except AttributeError: if self.parent is not None: self.parent.updated_slines(self.selected) return else: self.plot_widget.on_draw() def on_list_change(self, llist): # Clear if not isinstance(llist, LineList): raise ValueError('Expecting LineList!!') self.item_flg = 1 self.lines = llist._data self.llst = llist self.lines_widget.clear() # Initialize self.init_list() # Set selected for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem(self.lines_widget.item(self.selected[0])) self.item_flg = 0
class TriggersSettings(SettingsSection): Name = 'Triggers' Dialog = CueListDialog() def __init__(self, size, cue=None, **kwargs): super().__init__(size, cue=None, **kwargs) self.setLayout(QVBoxLayout(self)) self.list = QListWidget(self) self.list.setAlternatingRowColors(True) self.layout().addWidget(self.list) self.buttons = QDialogButtonBox(self) self.buttons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.layout().addWidget(self.buttons) self.addButton = self.buttons.addButton('Add', QDialogButtonBox.ActionRole) self.delButton = self.buttons.addButton('Remove', QDialogButtonBox.ActionRole) self.addButton.clicked.connect(self._add_trigger_dialog) self.delButton.clicked.connect(self._remove_trigger) self.Dialog.reset() self.Dialog.add_cues(Application().layout.get_cues()) self.duration = -1 def _add_new_trigger(self, cue_id, action, name): item = QListWidgetItem() item.setSizeHint(QSize(200, 30)) widget = TriggerWidget(item) widget.timeEdit.editingFinished.connect(self.list.sortItems) widget.load_trigger(cue_id, action, self.duration, name) self.list.addItem(item) self.list.setItemWidget(item, widget) def _add_trigger_dialog(self): cue = self.cue_select_dialog() if cue is not None: self._add_new_trigger(cue['id'], 'play', cue['name']) def _remove_trigger(self): widget = self.list.itemWidget(self.list.item(self.list.currentRow())) widget.timeEdit.editingFinished.disconnect() self.list.takeItem(self.list.currentRow()) @classmethod def cue_select_dialog(cls): if cls.Dialog.exec_() == QDialog.Accepted: return cls.Dialog.selected_cues()[0] def set_configuration(self, conf): if conf is not None: self.duration = conf.get('media', {}).get('duration', -1) if 'triggers' in conf: for action, ids in conf['triggers'].items(): for cue_id in ids: cue = Application().layout.get_cue_by_id(cue_id) if cue is not None: self._add_new_trigger(cue_id, action, cue['name']) def get_configuration(self): triggers = {} for n in range(self.list.count()): trigger = self.list.itemWidget(self.list.item(n)) action, target = trigger.get_trigger() if action not in triggers: triggers[action] = [target] else: triggers[action].append(target) return {'triggers': triggers}
class MyApp(QMainWindow): def __init__(self): super(MyApp, self).__init__() self.mainUI() self.setLayout() self.setWindowTitle("List App") self.setFixedSize(500, 300) self.setCentralWidget(self.setWidget) def mainUI(self): self.list = QListWidget() self.buttonAddItem = QPushButton("Add Item") self.buttonRemoveItem = QPushButton("Remove Item") self.buttonUpdateItem = QPushButton("update Item") self.buttonClearAllItem = QPushButton("clear all Item") self.buttonDuplicateItem = QPushButton("duplicate Item") # logic for progresss Bar self.buttonAddItem.clicked.connect(self.setInput) self.buttonRemoveItem.clicked.connect(self.setRemove) self.buttonClearAllItem.clicked.connect(self.setClear) self.buttonUpdateItem.clicked.connect(self.setUpdate) # logic for slider self.buttonDuplicateItem.clicked.connect(self.setDuplicate) self.slider = QSlider(Qt.Horizontal) self.progressbar = QProgressBar() def setLayout(self): self.layoutList = QVBoxLayout() self.layoutList.addWidget(self.list) self.layoutList.addWidget(self.buttonAddItem) self.layoutList.addWidget(self.buttonRemoveItem) self.layoutList.addWidget(self.buttonUpdateItem) self.layoutList.addWidget(self.buttonClearAllItem) self.layoutList.addWidget(self.buttonDuplicateItem) self.layoutList.addWidget(self.slider) self.layoutList.addWidget(self.progressbar) self.setWidget = QWidget() self.setWidget.setLayout(self.layoutList) def setDialog(self): self.dialog = QDialog() self.dialog.setFixedSize(400, 200) self.dialog.setWindowTitle("Custom Dialog Box") self.labelDialog = QLabel("custom label") self.button = QDialogButtonBox.Ok self.buttonBox = QDialogButtonBox(self.button) self.buttonBox.accepted.connect(self.dialog.accept) self.layoutDialog = QVBoxLayout() self.layoutDialog.addWidget(self.labelDialog) self.layoutDialog.addWidget(self.buttonBox) self.dialog.setLayout(self.layoutDialog) self.dialog.exec_() # logic dialog def setInput(self): self.inputDialog, ok = QInputDialog.getText(self, "Add to List Item", "Enter Input") if ok == True: if self.inputDialog != "": self.add = QListWidgetItem(self.inputDialog, self.list) self.list.addItem(self.add) self.progressbar.setValue(self.list.count()) print(f"check input dialog : {self.inputDialog}") print(ok) def setRemove(self): listdata_items = self.list.selectedItems() if not listdata_items: return for item in listdata_items: self.list.takeItem(self.list.row(item)) self.progressbar.setValue(self.list.count()) def setClear(self): self.list.clear() self.progressbar.setValue(self.list.count()) def setUpdate(self): dialog_update = QInputDialog() listdata_selected = self.list.selectedItems() index = ['%s' % (i.text()) for i in listdata_selected] result, ok = dialog_update.getText(self, "Update List Item", "Update List", text=index[0]) if ok == True and result != "": for item in listdata_selected: self.list.item(self.list.row(item)).setText(result) self.progressbar.setValue(self.list.count()) def setDuplicate(self): range_itemselected = self.valuesitem_slider() listdata_selected = [ '%s' % (i.text()) for i in self.list.selectedItems() ] for x in range(range_itemselected): QListWidgetItem(listdata_selected[0], self.list) self.progressbar.setValue(self.list.count()) self.progressbar.setValue(self.list.count()) def valuesitem_slider(self): return self.slider.value()
class DataSettingsPanel(SettingsPanel): def __init__(self, parent=None): super(DataSettingsPanel, self).__init__(parent) # Clear history dialog. self.clearHistoryDialog = clear_history_dialog.ClearHistoryDialog(self) self.clearHistoryDialog.hide() self.showClearHistoryDialogButton = QPushButton(tr("Clear Data"), self) self.showClearHistoryDialogButton.clicked.connect(self.clearHistoryDialog.exec_) self.layout().addWidget(self.showClearHistoryDialogButton) # Remember history checkbox. self.rememberHistoryToggle = QCheckBox(tr("Remember &history"), self) self.layout().addWidget(self.rememberHistoryToggle) # Maximum URL length spinbox. self.maximumURLLengthRow = custom_widgets.SpinBoxRow(tr("Maximum URL length:"), self) self.maximumURLLengthRow.expander.setText(tr("characters")) self.maximumURLLength = self.maximumURLLengthRow.spinBox self.maximumURLLength.setMaximum(9999) self.layout().addWidget(self.maximumURLLengthRow) # Maximum cache size spinbox. # The cache is gone because it f***s Nimbus with a chainsaw. #self.maximumCacheSizeRow = custom_widgets.SpinBoxRow(tr("Maximum cache size:"), self) #self.maximumCacheSizeRow.expander.setText(tr("MB")) #self.maximumCacheSize = self.maximumCacheSizeRow.spinBox #self.maximumCacheSize.setMaximum(20000) #self.layout().addWidget(self.maximumCacheSizeRow) # Checkbox to toggle geolocation. self.geolocationToggle = QCheckBox(tr("Enable geo&location"), self) self.layout().addWidget(self.geolocationToggle) self.geolocationPermissionsRow = custom_widgets.Row(self) self.layout().addWidget(self.geolocationPermissionsRow) self.geolocationWhitelistColumn = custom_widgets.Column(self) self.geolocationPermissionsRow.addWidget(self.geolocationWhitelistColumn) self.geolocationWhitelistLabel = QLabel(tr("Allow these sites to track my location:"), self) self.geolocationWhitelistColumn.addWidget(self.geolocationWhitelistLabel) self.geolocationWhitelist = QListWidget(self) self.geolocationWhitelistColumn.addWidget(self.geolocationWhitelist) self.removeFromWhitelistButton = QPushButton(tr("Remove")) self.removeFromWhitelistButton.clicked.connect(lambda: self.geolocationWhitelist.takeItem(self.geolocationWhitelist.row(self.geolocationWhitelist.currentItem()))) self.geolocationWhitelistColumn.addWidget(self.removeFromWhitelistButton) self.geolocationBlacklistColumn = custom_widgets.Column(self) self.geolocationPermissionsRow.addWidget(self.geolocationBlacklistColumn) self.geolocationBlacklistLabel = QLabel(tr("Prevent these sites from tracking my location:"), self) self.geolocationBlacklistColumn.addWidget(self.geolocationBlacklistLabel) self.geolocationBlacklist = QListWidget(self) self.geolocationBlacklistColumn.addWidget(self.geolocationBlacklist) self.removeFromBlacklistButton = QPushButton(tr("Remove")) self.removeFromBlacklistButton.clicked.connect(lambda: self.geolocationBlacklist.takeItem(self.geolocationBlacklist.row(self.geolocationBlacklist.currentItem()))) self.geolocationBlacklistColumn.addWidget(self.removeFromBlacklistButton) self.removeAction = QAction(self) self.removeAction.setShortcut("Del") self.removeAction.triggered.connect(lambda: self.geolocationWhitelist.takeItem(self.geolocationWhitelist.row(self.geolocationWhitelist.currentItem())) if self.geolocationWhitelist.hasFocus() else self.geolocationBlacklist.takeItem(self.geolocationBlacklist.row(self.geolocationBlacklist.currentItem()))) self.addAction(self.removeAction) self.layout().addWidget(custom_widgets.Expander(self)) def loadSettings(self): self.maximumURLLength.setValue(settings.setting_to_int("data/MaximumURLLength")) #self.maximumCacheSize.setValue(settings.setting_to_int("data/MaximumCacheSize")) self.rememberHistoryToggle.setChecked(settings.setting_to_bool("data/RememberHistory")) self.geolocationToggle.setChecked(settings.setting_to_bool("network/GeolocationEnabled")) self.geolocationWhitelist.clear() for url in data.geolocation_whitelist: self.geolocationWhitelist.addItem(url) self.geolocationBlacklist.clear() for url in data.geolocation_blacklist: self.geolocationBlacklist.addItem(url) def saveSettings(self): settings.settings.setValue("data/MaximumURLLength", self.maximumURLLength.value()) #settings.settings.setValue("data/MaximumCacheSize", self.maximumCacheSize.value()) settings.settings.setValue("data/RememberHistory", self.rememberHistoryToggle.isChecked()) settings.settings.setValue("network/GeolocationEnabled", self.geolocationToggle.isChecked()) data.geolocation_whitelist = [self.geolocationWhitelist.item(authority).text() for authority in range(0, self.geolocationWhitelist.count())] data.geolocation_blacklist = [self.geolocationBlacklist.item(authority).text() for authority in range(0, self.geolocationBlacklist.count())] data.saveData()
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) citiesdict_signal = pyqtSignal([dict]) def __init__(self, citylist, accurate_url, appid, trans_cities_dict, parent=None): super(CityListDlg, self).__init__(parent) self.settings = QSettings() self.citylist = citylist self.trans_cities_dict = trans_cities_dict self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.translate) cities_list = [] for i in self.citylist: cities_list.append(self.trans_cities_dict.get(i, i)) self.listWidget.addItems(cities_list) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) self.translate_button = QPushButton( QCoreApplication.translate('Button', '&Translate', 'Edit cities name')) buttonLayout.addWidget(self.translate_button) self.translate_button.clicked.connect(self.translate) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.setWindowTitle( QCoreApplication.translate('Window title', 'Cities', 'Cities list dialogue')) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = (self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText( QCoreApplication.translate( 'Status bar message', 'The city already exists in the list', 'Cities list dialogue')) return else: self.listWidget.addItem(newitem) self.checklength() self.status.setText('ℹ ' + QCoreApplication.translate( 'Status bar message', 'Toggle cities with mouse scroll on the weather window', 'Cities list dialogue')) def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 1: self.status.setText( QCoreApplication.translate( 'Message when trying to remove the' 'last and unique city in the list', 'This is the default city!', 'Cities list dialogue')) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): if self.listWidget.count() == 1: # After adding the first city the entry is not activated self.listWidget.setCurrentRow(0) if self.listWidget.count() > 0: self.translate_button.setEnabled(True) self.listWidget.setMinimumWidth( self.listWidget.sizeHintForColumn(0)) else: self.translate_button.setEnabled(False) def translate(self): city = self.listWidget.currentItem().text() dialog = citytranslate.CityTranslate(city, self.trans_cities_dict, self) dialog.city_signal.connect(self.current_translation) if dialog.exec_() == 1: row = self.listWidget.currentRow() item = self.listWidget.takeItem(row) del item self.listWidget.insertItem(row, self.current_translated_city) self.listWidget.setCurrentRow(row) def current_translation(self, translated_city): for city, translated in translated_city.items(): if translated == '': translated = city self.trans_cities_dict[city] = translated self.current_translated_city = translated def accept(self): listtosend = [] for row in range(self.listWidget.count()): city = self.find_city_key(self.listWidget.item(row).text()) listtosend.append(city) if self.listWidget.count() == 0: return self.citieslist_signal[list].emit(listtosend) self.citiesdict_signal[dict].emit(self.trans_cities_dict) QDialog.accept(self) def find_city_key(self, city): for key, value in self.trans_cities_dict.items(): if value == city: return key return city
class SettingsTab(QWidget): def __init__(self, elder): super().__init__() self.elder = elder self.layout = QGridLayout(self) self.settings = elder.settings self.listener = elder.listener paths = QGroupBox(self) paths.setTitle('Paths') paths_layout = QGridLayout(paths) self.layout.addWidget(paths) label = QLabel(paths) label.setText('Fiddler') paths_layout.addWidget(label, 0, 0, 1, 1) self.fiddler_path = QLineEdit(paths) self.fiddler_path.setText(self.settings.paths['Fiddler']) self.fiddler_path.setDisabled(True) paths_layout.addWidget(self.fiddler_path, 0, 1, 1, 1) button = QPushButton(paths) button.setText('...') button.clicked.connect(lambda: self.set_path('fiddler')) paths_layout.addWidget(button, 0, 2, 1, 1) label = QLabel(paths) label.setText('TurboHUD') paths_layout.addWidget(label, 1, 0, 1, 1) self.turbohud_path = QLineEdit(paths) self.turbohud_path.setText(self.settings.paths['TurboHUD']) self.turbohud_path.setDisabled(True) paths_layout.addWidget(self.turbohud_path, 1, 1, 1, 1) button = QPushButton(paths) button.setText('...') button.clicked.connect(lambda: self.set_path('turbohud')) paths_layout.addWidget(button, 1, 2, 1, 1) label = QLabel(paths) label.setText('pHelper') paths_layout.addWidget(label, 2, 0, 1, 1) self.phelper_path = QLineEdit(paths) self.phelper_path.setText(self.settings.paths['pHelper']) self.phelper_path.setDisabled(True) paths_layout.addWidget(self.phelper_path, 2, 1, 1, 1) button = QPushButton(paths) button.setText('...') button.clicked.connect(lambda: self.set_path('phelper')) paths_layout.addWidget(button, 2, 2, 1, 1) image_recognition = QGroupBox(self) image_recognition.setTitle('Auto Stuff (Image Recognition)') image_recognition_layout = QGridLayout(image_recognition) self.layout.addWidget(image_recognition, 0, 1) checkbox = QCheckBox(image_recognition) checkbox.setText('Auto start Game') checkbox.setChecked(self.settings.special['auto_start']) checkbox.clicked.connect(lambda: self.checkbox_clicked('auto_start')) image_recognition_layout.addWidget(checkbox, 0, 0) checkbox = QCheckBox(image_recognition) checkbox.setText('Auto open Rift / Grift') checkbox.setChecked(self.settings.special['auto_open']) checkbox.clicked.connect(lambda: self.checkbox_clicked('auto_open')) image_recognition_layout.addWidget(checkbox, 1, 0) radio = QRadioButton(image_recognition) radio.setText('Rift') radio.setChecked(self.settings.special['auto_open_option'] == 'rift') radio.clicked.connect(lambda: self.radio_clicked('rift')) image_recognition_layout.addWidget(radio, 2, 0) radio = QRadioButton(image_recognition) radio.setText('Grift') radio.setChecked(self.settings.special['auto_open_option'] == 'grift') radio.clicked.connect(lambda: self.radio_clicked('grift')) image_recognition_layout.addWidget(radio, 2, 1) checkbox = QCheckBox(image_recognition) checkbox.setText('Auto accept Grift') checkbox.setChecked(self.settings.special['auto_accept_gr']) checkbox.clicked.connect( lambda: self.checkbox_clicked('auto_accept_gr')) image_recognition_layout.addWidget(checkbox, 3, 0) checkbox = QCheckBox(image_recognition) checkbox.setText('Auto upgrade Gem') checkbox.setChecked(self.settings.special['auto_upgrade_gem']) checkbox.clicked.connect( lambda: self.checkbox_clicked('auto_upgrade_gem')) image_recognition_layout.addWidget(checkbox, 4, 0) checkbox = QCheckBox(image_recognition) checkbox.setText('Auto gamble') checkbox.setChecked(self.settings.special['auto_gamble']) checkbox.clicked.connect(lambda: self.checkbox_clicked('auto_gamble')) image_recognition_layout.addWidget(checkbox, 5, 0) poolspots = QGroupBox(self) poolspots.setTitle('Poolspots') poolspots_layout = QGridLayout(poolspots) self.layout.addWidget(poolspots, 1, 0) self.poolspot_list = QListWidget(poolspots) self.poolspot_list.setSelectionMode(QListWidget.MultiSelection) for act, ps in ressources.poolspots().items(): for poolspot in ps: item = QListWidgetItem(self.poolspot_list) item.setText( f'Act {act}: {string.capwords(poolspot.replace("_", " "))}' ) item.poolspot = poolspot if poolspot in self.settings.poolspots: item.setSelected(True) self.poolspot_list.itemSelectionChanged.connect(self.update_poolspots) poolspots_layout.addWidget(self.poolspot_list) # button = QPushButton(poolspots) # button.setText('Save') # button.clicked.connect(self.update_poolspots) # poolspots_layout.addWidget(button) gamble_item = QGroupBox(self) gamble_item.setTitle('Gamble Item') gamble_item_layout = QGridLayout(gamble_item) self.layout.addWidget(gamble_item, 1, 1) self.gamble_item_list = QListWidget(gamble_item) self.gamble_item_list.setSelectionMode(QListWidget.SingleSelection) for _item in ressources.items(): item = QListWidgetItem(self.gamble_item_list) item.setText(string.capwords(_item.replace('_', ' '))) item.item = _item if _item == self.settings.special['gamble_item']: item.setSelected(True) self.gamble_item_list.itemSelectionChanged.connect( self.update_gamble_item) gamble_item_layout.addWidget(self.gamble_item_list) self.setLayout(self.layout) def set_path(self, path): exe = str( QFileDialog.getOpenFileName(self, 'Select Executables', '', 'Executables (*.exe)')[0]) if exe: if path == 'fiddler': self.settings.paths['Fiddler'] = exe self.fiddler_path.setText(exe) elif path == 'turbohud': self.settings.paths['TurboHUD'] = exe self.turbohud_path.setText(exe) elif path == 'phelper': self.settings.paths['pHelper'] = exe self.phelper_path.setText(exe) def update_poolspots(self): self.listener.stop() selected_spots = [] for i in range(self.poolspot_list.count()): item = self.poolspot_list.item(i) if item.isSelected(): selected_spots.append(item.poolspot) self.settings.poolspots = selected_spots if not self.listener.paused: self.listener.start() # TODO: Wenn man seinen Pause key deleted elif self.settings.hotkeys['pause']: keyboard.add_hotkey(self.settings.hotkeys['pause'], self.listener.pause) def update_gamble_item(self): self.listener.stop() selected_item = self.gamble_item_list.selectedItems()[0] self.settings.special['gamble_item'] = selected_item.item if not self.listener.paused: self.listener.start() # TODO: Wenn man seinen Pause key deleted elif self.settings.hotkeys['pause']: keyboard.add_hotkey(self.settings.hotkeys['pause'], self.listener.pause) def checkbox_clicked(self, value): sender = self.sender() self.settings.special[value] = sender.isChecked() def radio_clicked(self, value): self.settings.special['auto_open_option'] = value
class AbbrevationTab(QWidget): def __init__(self, elder): super().__init__() self.elder = elder self.layout = QGridLayout(self) self.settings = elder.settings self.listener = elder.listener checkbox = QCheckBox(self) checkbox.setText('Abbrevations enabled') checkbox.setChecked(self.settings.special['abbrevations_enabled']) checkbox.clicked.connect(self.checkbox_clicked) self.layout.addWidget(checkbox) abbrevations = QGroupBox(self) abbrevations.setTitle('Current Abbrevations') abbrevations_layout = QGridLayout(abbrevations) self.layout.addWidget(abbrevations) self.abbrevations_list = QListWidget(abbrevations) self.abbrevations_list.setSelectionMode(QListWidget.SingleSelection) for abbrevation, msg in self.settings.abbrevations.items(): # Add the new Item to QListWidget custom_widget = CustomListWidget(abbrevation, msg) item = QListWidgetItem(self.abbrevations_list) item.abbrevation = abbrevation item.msg_line_edit = custom_widget.msg_edit self.abbrevations_list.addItem(item) item.setSizeHint(custom_widget.minimumSizeHint()) self.abbrevations_list.setItemWidget(item, custom_widget) abbrevations_layout.addWidget(self.abbrevations_list, 0, 0, 1, 0) button = QPushButton(abbrevations) button.setText('Save') button.clicked.connect(self.update_abbrevations) abbrevations_layout.addWidget(button, 1, 0) button = QPushButton(abbrevations) button.setText('Add new') button.clicked.connect(self.add_item) abbrevations_layout.addWidget(button, 1, 1) button = QPushButton(abbrevations) button.setText('Remove selected') button.clicked.connect(self.remove_selected_item) abbrevations_layout.addWidget(button, 1, 2) self.layout.addWidget(abbrevations) self.setLayout(self.layout) def checkbox_clicked(self): self.listener.stop() sender = self.sender() self.settings.special['abbrevations_enabled'] = sender.isChecked() if not self.listener.paused: self.listener.start() # TODO: Wenn man seinen Pause key deleted elif self.settings.hotkeys['pause']: keyboard.add_hotkey(self.settings.hotkeys['pause'], self.listener.pause) def update_abbrevations(self): self.listener.stop() new_abbrevations = {} for i in range(self.abbrevations_list.count()): item = self.abbrevations_list.item(i) abbrevation = item.abbrevation msg = item.msg_line_edit.text() new_abbrevations[abbrevation] = msg self.settings.abbrevations = new_abbrevations if not self.listener.paused: self.listener.start() # TODO: Wenn man seinen Pause key deleted elif self.settings.hotkeys['pause']: keyboard.add_hotkey(self.settings.hotkeys['pause'], self.listener.pause) def remove_selected_item(self): if self.abbrevations_list.selectedItems(): item = self.abbrevations_list.selectedItems()[0] self.abbrevations_list.takeItem(self.abbrevations_list.row(item)) def add_item(self): dlg = AddItemDialog(self) if dlg.exec_(): abbrevation = dlg.abbrevation_edit.text() msg = dlg.msg_edit.text() # Add the new Item to QListWidget custom_widget = CustomListWidget(abbrevation, msg) item = QListWidgetItem(self.abbrevations_list) item.abbrevation = abbrevation item.msg_line_edit = custom_widget.msg_edit self.abbrevations_list.addItem(item) item.setSizeHint(custom_widget.minimumSizeHint()) self.abbrevations_list.setItemWidget(item, custom_widget)
class ContactCards(QWidget): def __init__(self, context: ListContext, parent: Any = None) -> None: super().__init__(parent) self._context = context self._layout = QVBoxLayout() self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) self._empty_label = None self._list = None contact_identities = self._context.wallet_api.get_identities() if len(contact_identities) > 0: for contact, identity in sorted(contact_identities, key=lambda t: t[0].label): self._add_identity(contact, identity) else: self._add_empty_label() self.setLayout(self._layout) self._context.wallet_api.contact_changed.connect( self._on_contact_changed) def _add_identity(self, contact: ContactEntry, identity: ContactIdentity) -> None: self._remove_empty_label() if self._list is None: self._list = QListWidget(self) self._list.setSortingEnabled(True) self._layout.addWidget(self._list) test_card = ContactCard(self._context, contact, identity) test_card.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) list_item = QListWidgetItem() list_item.setText(contact.label) # The item won't display unless it gets a size hint. It seems to resize horizontally # but unless the height is a minimal amount it won't do anything proactive.. list_item.setSizeHint(QSize(256, 130)) self._list.addItem(list_item) self._list.setItemWidget(list_item, test_card) def _remove_identity(self, contact: ContactEntry, identity: ContactIdentity) -> None: removal_entries = [] for i in range(self._list.count() - 1, -1, -1): item = self._list.item(i) widget = self._list.itemWidget(item) if identity is None and widget._contact.contact_id == contact.contact_id: self._list.takeItem(i) elif widget._identity.identity_id == identity.identity_id: self._list.takeItem(i) if self._list.count() == 0: # Remove the list. self._list.setParent(None) self._list = None # Replace it with the placeholder label. self._add_empty_label() def _on_contact_changed(self, added: bool, contact: ContactEntry, identity: ContactIdentity) -> None: if added: self._add_identity(contact, identity) else: self._remove_identity(contact, identity) def _add_empty_label(self) -> None: self._empty_label = QLabel( _("You do not currently have any contacts.")) self._empty_label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self._layout.addWidget(self._empty_label) def _remove_empty_label(self) -> None: if self._empty_label is not None: self._empty_label.setParent(None) self._empty_label = None
class window(QtWidgets.QMainWindow): def __init__(self): super(window,self).__init__() self.currentlocal=0 self.data=None self.checker=0 self.lastcolored=0 self.photo = QLabel(self) self.port=0 self.pixmap = QPixmap('photo.png') self.pixmap = self.pixmap.scaled(600, 300, QtCore.Qt.KeepAspectRatio) self.photo.setPixmap(self.pixmap) self.labelgif=QLabel(self) self.labelgif.setStyleSheet("QLabel { background-color : white;}"); self.labelgif.setGeometry(100,50,500,430) self.movie = QtGui.QMovie('data.gif', QtCore.QByteArray(),self.labelgif) self.movie.setSpeed(100) self.labelgif.setMovie(self.movie) self.movie.start() self.labelgif.setVisible(False) self.labelyazi=QLabel(self) self.labelgif1=QLabel(self) self.labelgif1.setStyleSheet("QLabel { background-color : white;}") self.labelyazi.setText('G'+"\u00F6"+"zl"+"\u0259"+"yin..") font1=QtGui.QFont('Times',17) self.labelyazi.setFont(font1) self.labelyazi.setVisible(False) self.labelyazi.setGeometry(350,150,150,60) self.labelgif1.setGeometry(150,100,489,289) self.movie1 = QtGui.QMovie('wait.gif', QtCore.QByteArray(),self.labelgif1) self.movie1.setSpeed(100) self.labelgif1.setMovie(self.movie1) self.movie1.start() self.labelgif1.setVisible(False) self.setWindowTitle("Diplom i\u015Fi v1") self.setWindowIcon(QtGui.QIcon('pyicon.png')) self.button = QPushButton('PyQt5 button', self)#button yaradildi self.listw=QListWidget(self)#listWidget yaradildi self.button1=QPushButton(self) self.buttonlocal=QPushButton(self) self.buttonlocal.setText('Qo\u015F') self.button1.setText("Temperaturu"+" " +"\u00F6"+"l"+"\u00E7") self.dial=QDial(self) self.lcd=QLCDNumber(self) self.label=QLabel(self) self.labelrefresh=QLabel(self) self.obj=[] self.listCOM=QListWidget(self) self.spin=QSpinBox(self) self.radiosan=QRadioButton(self) self.radiosan.setText("Saniy"+"\u0259") self.radiodeq=QRadioButton(self) self.radiodeq.setText("D"+"\u0259"+"qiq"+"\u0259") self.starting() self.initUI() def initUI(self): self.setFixedSize(700,500) self.dial.setNotchesVisible(True) self.labelrefresh.setText('Yenil\u0259m\u0259k \u00FC\u00E7\u00FCn F5 d\u00FCym\u0259sini s\u0131x\u0131n ') self.labelrefresh.setStyleSheet("QLabel{background-color: yellow; }") font=QtGui.QFont('Times',10,QtGui.QFont.Bold) self.labelrefresh.setFont(font) self.lcd.setVisible(False) self.photo.setVisible(False) self.photo.raise_() self.labelgif.raise_() self.labelgif1.raise_() self.labelyazi.raise_() self.spin.setRange(1,60) self.dial.setRange(1,60) self.button.setText("\u015E"+"\u0259"+"b\u0259k\u0259ni yoxla") self.button1.setEnabled(False) self.button.setEnabled(False) self.spin.setEnabled(False) self.dial.setEnabled(False) self.radiosan.setEnabled(False) self.radiodeq.setEnabled(False) self.label.setText('Qo\u015Fulmu'+'\u015F cihaz yoxdur') self.label.setStyleSheet("QLabel { background-color : #e20000; color : black; }"); newfont = QtGui.QFont('Times',11) self.label.setFont(newfont) #geometries self.setGeometry(40,50,700,500) self.button.setGeometry(20,40,120,50) self.listw.setGeometry(380,160,300,200) self.button1.setGeometry(575,40,120,50) self.dial.setGeometry(40,400,75,70) self.spin.setGeometry(150,425,50,25) self.radiosan.setGeometry(150,400,75,25) self.radiodeq.setGeometry(150,380,75,25) self.lcd.setGeometry(300,40,100,50) self.buttonlocal.setGeometry(150,40,125,50) self.label.setGeometry(520,440,155,30) self.listCOM.setGeometry(20,160,300,200) self.labelrefresh.setGeometry(20,100,220,30) self.photo.setGeometry(50,100,600,300) #events self.buttonlocal.clicked.connect(self.checklocal) self.button.clicked.connect(self.thread1) self.button.clicked.connect(self.threadnetwork) self.dial.valueChanged.connect(self.spin.setValue) self.spin.valueChanged.connect(self.dial.setValue) self.listCOM.doubleClicked.connect(self.showdialog) self.listw.doubleClicked.connect(self.showdialogremote) self.button1.clicked.connect(self.thread) # communication self.radiodeq.clicked.connect(self.spinvalue) self.radiosan.clicked.connect(self.dialvalue) self.button1.clicked.connect(self.threadback) def threadback(self): if self.radiodeq.isChecked() or self.radiosan.isChecked(): self.thread1=threading.Thread(target=self.send) self.thread1.start() else: pass def loading(self): m=loading() def send(self): try: self.currentlocal.open() remotestr=self.listw.currentItem().text() li=remotestr.split("-") xbee_network=self.currentlocal.get_network() remote=xbee_network.get_device_by_64(XBee64BitAddress.from_hex_string(li[0])) arr_64=self.currentlocal.get_64bit_addr() NEW_TIMEOUT_FOR_SYNC_OPERATIONS = 1 self.currentlocal.set_sync_ops_timeout(NEW_TIMEOUT_FOR_SYNC_OPERATIONS) if self.radiosan.isChecked(): self.currentlocal.send_data(remote,str(arr_64)+"-"+str(self.spin.value())) else: self.currentlocal.send_data(remote,str(arr_64)+"-"+str(self.spin.value()*60)) self.labelgif1.setVisible(False) self.labelgif1.setVisible(True) self.labelyazi.setVisible(True) while(True): self.data=self.currentlocal.read_data() if(self.data!=None): self.data=self.data.data.decode() self.labelgif1.setVisible(False) self.labelyazi.setVisible(False) break self.currentlocal.close() data_list=self.data.split(',') self.labelgif.setVisible(True) objects = [] performance=[] for i in range(1,len(data_list)): objects.append(i) for i in range(len(data_list)-1): li=data_list[i] li=li.split('-') performance.append(li[1]) y_pos = np.arange(len(objects)) objects=tuple(objects) plt.figure("Qrafik") plt.xticks(y_pos, objects) plt.ylabel('Temperatur') plt.xlabel('Zaman') plt.plot(y_pos,performance) self.labelgif.setVisible(False) plt.show() self.data=None except: print('salam') self.currentlocal.close() def showdialog(self): try: li=self.listCOM.currentItem().text().split('-') local=XBeeDevice(li[2],9600) local.open() arr_64=local.get_64bit_addr() arr_16=local.get_16bit_addr() arr_node=local.get_node_id() arr_pro=local.get_protocol() arr_hard=local.get_hardware_version() local.close() dlg=dialog(arr_64,arr_16,arr_node,arr_pro,arr_hard) except: pass #exception def showdialogremote(self): li=self.listw.currentItem().text().split('-') if self.checker !=0: self.lastcolored.setBackground(QtGui.QColor(255,255,255)) self.lastcolored=self.listw.currentItem() self.listw.currentItem().setBackground(QtGui.QColor(239, 255, 25)) try: self.currentlocal.open() xbee_network=self.currentlocal.get_network() remote=xbee_network.get_device_by_64(XBee64BitAddress.from_hex_string(li[0])) arr_64=remote.get_64bit_addr() arr_16=remote.get_16bit_addr() arr_node=remote.get_node_id() arr_pro=remote.get_protocol() arr_hard=remote.get_hardware_version() self.currentlocal.close() dlg=dialog(arr_64,arr_16,arr_node,arr_pro,arr_hard) self.checker=1 except: pass # exception def spinvalue(self): self.dial.setRange(1,60) self.spin.setRange(1,60) self.dial.setValue(1) def dialvalue(self): self.dial.setRange(4,60) self.spin.setRange(4,60) self.dial.setValue(4) def keyPressEvent(self, event): key = event.key() if key == QtCore.Qt.Key_F5: self.threadrefresh() def checklocal(self): try: if (self.currentlocal !=0): for i in range(0,self.listCOM.count()): self.listCOM.item(i).setBackground(QtGui.QColor(255, 255, 255)) self.listCOM.currentItem().setBackground(QtGui.QColor(97, 255, 66)) li=self.listCOM.currentItem().text().split('-') self.currentlocal = XBeeDevice(li[2], 9600) self.port=li[2] self.currentCOM=self.listCOM.currentItem().text() self.currentlocal.open() self.currentlocal.close() self.listw.clear() self.button1.setEnabled(True) self.button.setEnabled(True) self.spin.setEnabled(True) self.dial.setEnabled(True) self.radiosan.setEnabled(True) self.radiodeq.setEnabled(True) if platform.system()=='Linux': self.label.setGeometry(500,440,180,30) self.label.setText('Qo\u015Fulmu'+'\u015F port: '+str(li[2])) self.checker=0 self.label.setStyleSheet("QLabel { background-color : #22ce00; color : white; }") except: QMessageBox.about(self, 'Yanl\u0131\u015F', 'Lokal cihaz\u0131n portu do\u011Fru deyil') def refresh(self): self.listCOM.clear() index=0 if platform.system()=='Windows': for i in range(0,257): try: local_xbee = XBeeDevice('COM'+str(i), 9600) local_xbee.open() addr64=local_xbee.get_64bit_addr() noid=local_xbee.get_node_id() local_xbee.close() self.listCOM.addItem(str(addr64)+"-"+str(noid)+"-"+'COM'+str(i)) if(self.port=='COM'+str(i)): self.listCOM.item(index).setBackground(QtGui.QColor(97, 255, 66)) index+=1 except: pass elif platform.system()=='Linux': for i in range(257): try: local_xbee = XBeeDevice('/dev/ttyUSB'+str(i), 9600) local_xbee.open() addr64=local_xbee.get_64bit_addr() noid=local_xbee.get_node_id() local_xbee.close() self.listCOM.addItem(str(addr64)+"-"+str(noid)+"-"+'/dev/ttyUSB'+str(i)) if(self.port=='/dev/ttyUSB'+str(i)): self.listCOM.item(index).setBackground(QtGui.QColor(97, 255, 66)) index+=1 except: pass self.checker=0 def thread(self): if self.radiodeq.isChecked() or self.radiosan.isChecked(): self.thread=threading.Thread(target=self.timing) self.thread.start() else: QMessageBox.about(self, 'Yanl\u0131\u015F', 'Zaman vahidini se\u00E7in') def thread1(self): if self.radiodeq.isChecked() or self.radiosan.isChecked(): self.thread1=threading.Thread(target=self.scan) self.thread1.start() else: QMessageBox.about(self, 'Yanl\u0131\u015F', 'Zaman vahidini se\u00E7in') def threadnetwork(self): if self.radiodeq.isChecked() or self.radiosan.isChecked(): self.thread1=threading.Thread(target=self.network) self.thread1.start() else: pass def network(self): try: self.button1.setEnabled(False) self.buttonlocal.setEnabled(False) self.button.setEnabled(False) self.button1.setEnabled(False) self.spin.setEnabled(False) self.dial.setEnabled(False) self.radiosan.setEnabled(False) self.radiodeq.setEnabled(False) self.listw.clear() self.currentlocal.open() xbee_network=self.currentlocal.get_network() xbee_network.clear() listdev=[] def callback_device_discovered(remote): listdev.append(str(remote)) if self.radiosan.isChecked(): if(self.spin.value()>25): defe=int((self.spin.value())/25) qaliqsan=(self.spin.value())%25 for i in range(0,defe): xbee_network.set_discovery_timeout(22) xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) if(qaliqsan<4): add=q=4-qaliqsan xbee_network.set_discovery_timeout(qaliqsan+add) xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) else: xbee_network.set_discovery_timeout(qaliqsan) xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) self.currentlocal.close() else: xbee_network.set_discovery_timeout(self.spin.value()) xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) self.currentlocal.close() self.photo.setVisible(True) listdev=list(set(listdev)) for i in range(0,len(listdev)): self.listw.addItem(listdev[i]) QtCore.QThread.msleep(1000) self.photo.setEnabled(True) self.buttonlocal.setEnabled(True) self.button1.setEnabled(True) self.button.setEnabled(True) self.spin.setEnabled(True) self.dial.setEnabled(True) self.radiosan.setEnabled(True) self.radiodeq.setEnabled(True) self.photo.setVisible(False) if self.radiodeq.isChecked(): defe=int((self.spin.value()*60)/25) qaliqsan=(self.spin.value()*60)%25 for i in range(0,defe): xbee_network.set_discovery_timeout(22) # 24 seconds + saniye elave. xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) xbee_network.set_discovery_timeout(qaliqsan) # qaliq saniye. xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) self.currentlocal.close() else: xbee_network.set_discovery_timeout(self.spin.value()) # qaliq saniye xbee_network.add_device_discovered_callback(callback_device_discovered) xbee_network.start_discovery_process() while xbee_network.is_discovery_running(): QtCore.QThread.msleep(100) self.currentlocal.close() self.photo.setVisible(True) listdev=list(set(listdev)) for i in range(0,len(listdev)): self.listw.addItem(listdev[i]) QtCore.QThread.msleep(2000) self.buttonlocal.setEnabled(True) self.button1.setEnabled(True) self.button.setEnabled(True) self.spin.setEnabled(True) self.dial.setEnabled(True) self.radiosan.setEnabled(True) self.radiodeq.setEnabled(True) self.photo.setVisible(False) except: self.currentlocal.close() def threadrefresh(self): t=threading.Thread(target=self.refresh) t.start() #UI has been finished def timing(self): QtCore.QThread.msleep(1000) self.button1.setEnabled(False) if(self.radiodeq.isChecked()): self.lcd.setVisible(True) j=self.spin.value()*60 k=self.spin.value() if(k<10): self.lcd.display("0{}:00".format(k)) QtCore.QThread.msleep(1000) else: self.lcd.display("{}:00".format(k)) QtCore.QThread.msleep(1000) j-=1 k-=1 while(j>-1): if(k<10): if(j%60<10): if(j%60 is 0): self.lcd.display("0{}:0{}".format(k,j%60)) k-=1 j-=1 QtCore.QThread.msleep(1000) continue self.lcd.display("0{}:0{}".format(k,j%60)) app.processEvents() QtCore.QThread.msleep(1000) j-=1 else: self.lcd.display("0{}:{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 else: if(j%60 is 0): self.lcd.display("0{}:0{}".format(k,j%60)) k-=1 j-=1 QtCore.QThread.msleep(1000) continue if(j%60<10): self.lcd.display("{}:0{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 else: self.lcd.display("{}:{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 self.lcd.setVisible(False) self.button1.setEnabled(True) elif (self.radiosan.isChecked()): self.lcd.setVisible(True) timing=self.spin.value() for i in range(timing,-1,-1): if(i<10): self.lcd.display("00:0{}".format(i)) QtCore.QThread.msleep(1000) else: self.lcd.display("00:{}".format(i)) QtCore.QThread.msleep(1000) self.lcd.setVisible(False) self.button1.setEnabled(True) def starting(self): splash=QtWidgets.QSplashScreen(QtGui.QPixmap('splash.jpg'),QtCore.Qt.WindowStaysOnTopHint) splash.show() for i in range(0,257): app.processEvents() if (i is 50): splash.showMessage("<h1><font color=#608fdb>Proqram başladılır!</font></h1>", QtCore.Qt.AlignTop) QtCore.QThread.msleep(1000) try: if (platform.system() == 'Windows'): local_xbee = XBeeDevice('COM'+str(i), 9600) local_xbee.open() addr64=local_xbee.get_64bit_addr() noid=local_xbee.get_node_id() local_xbee.close() self.listCOM.addItem(str(addr64)+"-"+str(noid)+"-"+'COM'+str(i)) elif (platform.system() == 'Linux'): local_xbee = XBeeDevice('/dev/ttyUSB'+str(i), 9600) local_xbee.open() addr64=local_xbee.get_64bit_addr() noid=local_xbee.get_node_id() local_xbee.close() self.listCOM.addItem(str(addr64)+"-"+str(noid)+"-"+'/dev/ttyUSB'+str(i)) except: pass splash.close() def createlistw(self): self.listw.clear() for i in range(0,9): self.obj.append(i) self.obj[i]=elements() self.obj[i].t=[10,20,30,40,2,3,4,5,6] self.obj[i].s=[5,6,7,8,9,1,2,3,4,5,88] self.listw.addItem(str(self.obj[i].t[i])) def scan(self): self.button.setEnabled(False) if(self.radiodeq.isChecked()): self.lcd.setVisible(True) j=self.spin.value()*60 k=self.spin.value() if(k<10): self.lcd.display("0{}:00".format(k)) QtCore.QThread.msleep(1000) else: self.lcd.display("{}:00".format(k)) QtCore.QThread.msleep(1000) j-=1 k-=1 while(j>-1): if(k<10): if(j%60<10): if(j%60 is 0): self.lcd.display("0{}:0{}".format(k,j%60)) k-=1 j-=1 QtCore.QThread.msleep(1000) continue self.lcd.display("0{}:0{}".format(k,j%60)) app.processEvents() QtCore.QThread.msleep(1000) j-=1 else: self.lcd.display("0{}:{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 else: if(j%60 is 0): self.lcd.display("0{}:0{}".format(k,j%60)) k-=1 j-=1 QtCore.QThread.msleep(1000) continue if(j%60<10): self.lcd.display("{}:0{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 else: self.lcd.display("{}:{}".format(k,j%60)) QtCore.QThread.msleep(1000) j-=1 self.lcd.setVisible(False) self.button.setEnabled(True) elif (self.radiosan.isChecked()): self.lcd.setVisible(True) timing=self.spin.value() for i in range(timing,-1,-1): if(i<10): self.lcd.display("00:0{}".format(i)) QtCore.QThread.msleep(1000) else: self.lcd.display("00:{}".format(i)) QtCore.QThread.msleep(1000) self.lcd.setVisible(False) self.button.setEnabled(True)
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error("loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format(available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True" ) self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True" ) self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue("path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue("path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue("path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str(self.regime_list.item( self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon(QIcon( get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format(self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format(reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error("apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data(os.path.join( self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0/self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue((self.speedControl.maximum() - self.speedControl.minimum())/2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, )) ) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der)) ) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class PostProcessor(QMainWindow): sim_results_changed = pyqtSignal() post_results_changed = pyqtSignal() figures_changed = pyqtSignal(list, str) def __init__(self, parent=None): QMainWindow.__init__(self, parent) self._settings = QSettings() self._logger = logging.getLogger(self.__class__.__name__) self.setWindowTitle("Processing") self.setWindowIcon(QIcon(get_resource("processing.png"))) self.mainFrame = QWidget(self) self.resize(1000, 600) # toolbar self.toolBar = QToolBar("file control") self.toolBar.setIconSize(QSize(24, 24)) self.addToolBar(self.toolBar) self.actLoad = QAction(self) self.actLoad.setText("load result file") self.actLoad.setIcon(QIcon(get_resource("load.png"))) self.actLoad.setDisabled(False) self.actLoad.triggered.connect(self.load_result_files) self.actPostLoad = QAction(self) self.actPostLoad.setText("load post-result file") self.actPostLoad.setIcon(QIcon(get_resource("load.png"))) self.actPostLoad.setDisabled(False) self.actPostLoad.triggered.connect(self.load_post_result_files) self.actSwitch = QAction(self) self.actSwitch.setText("switch display mode") self.actSwitch.setIcon(QIcon(get_resource("left_mode.png"))) self.actSwitch.setDisabled(False) self.actSwitch.triggered.connect(self.switch_sides) self.displayLeft = True self.spacer1 = QWidget() self.spacer2 = QWidget() self.spacer1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.actReloadMethods = QAction(self) self.actReloadMethods.setText("reload methods") self.actReloadMethods.setIcon(QIcon(get_resource("reload.png"))) self.actReloadMethods.setDisabled(False) self.actReloadMethods.triggered.connect(self.update_post_method_list) self.actReloadMetaMethods = QAction(self) self.actReloadMetaMethods.setText("reload meta methods") self.actReloadMetaMethods.setIcon(QIcon(get_resource("reload.png"))) self.actReloadMetaMethods.setDisabled(False) self.actReloadMetaMethods.triggered.connect( self.update_meta_method_list) self.toolBar.addAction(self.actLoad) self.toolBar.addAction(self.actReloadMethods) self.toolBar.addWidget(self.spacer1) self.toolBar.addAction(self.actSwitch) self.toolBar.addWidget(self.spacer2) self.toolBar.addAction(self.actReloadMetaMethods) self.toolBar.addAction(self.actPostLoad) # main window self.grid = QGridLayout(self.mainFrame) self.grid.setColumnMinimumWidth(0, 70) self.grid.setColumnStretch(0, 0) self.grid.setColumnStretch(1, 1) self.methodList = QListWidget(self) self.methodList.itemDoubleClicked.connect(self.post_processor_clicked) self.update_post_method_list() self.metaMethodList = QListWidget(self) self.metaMethodList.itemDoubleClicked.connect( self.meta_processor_clicked) self.update_meta_method_list() self.sim_result_list = QListWidget(self) self.sim_results_changed.connect(self.update_result_list) self.results = [] self.delShort = QShortcut(QKeySequence(Qt.Key_Delete), self.sim_result_list) self.delShort.activated.connect(self.remove_result_item) # figures self._figure_dict = {} self.figures_changed.connect(self.update_figure_lists) self.post_figure_list = QListWidget(self) self.post_figure_list.currentItemChanged.connect( self.current_figure_changed) self.meta_figure_list = QListWidget(self) self.meta_figure_list.currentItemChanged.connect( self.current_figure_changed) self.plotView = QWidget() self.lastFigure = None self.post_result_list = QListWidget(self) self.post_results_changed.connect(self.update_post_result_list) self.post_results = [] self.delShortPost = QShortcut(QKeySequence(Qt.Key_Backspace), self.post_result_list) self.delShortPost.activated.connect(self.remove_post_result_item) # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) self.grid.addWidget(QLabel("Result Files:"), 0, 0) self.grid.addWidget(self.sim_result_list, 1, 0) self.grid.addWidget(QLabel("Postprocessors:"), 2, 0) self.grid.addWidget(self.methodList, 3, 0) self.grid.addWidget(QLabel("Figures:"), 4, 0) self.grid.addWidget(self.post_figure_list, 5, 0) self.grid.addWidget(QLabel("Selected Figure:"), 0, 1) self.grid.addWidget(QLabel("Postprocessor Files:"), 0, 2) self.grid.addWidget(self.post_result_list, 1, 2) self.grid.addWidget(QLabel("Metaprocessors:"), 2, 2) self.grid.addWidget(self.metaMethodList, 3, 2) self.grid.addWidget(QLabel("Figures:"), 4, 2) self.grid.addWidget(self.meta_figure_list, 5, 2) self.grid.addWidget(self.logBox, 6, 0, 1, 3) self.mainFrame.setLayout(self.grid) self.setCentralWidget(self.mainFrame) # status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) def load_result_files(self): path = self._settings.value("path/simulation_results") dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFiles) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Result files (*.pmr)") if dialog.exec_(): files = dialog.selectedFiles() for single_file in files: if single_file: self._load_result_file(single_file) def _load_result_file(self, file_name): """ loads a result file """ self._logger.info("loading result file {}".format(file_name)) with open(file_name.encode(), "rb") as f: self.results.append(pickle.load(f)) self.sim_results_changed.emit() def update_result_list(self): self.sim_result_list.clear() for res in self.results: name = res["regime name"] self.sim_result_list.addItem(name) def remove_result_item(self): if self.sim_result_list.currentRow() >= 0: del self.results[self.sim_result_list.currentRow()] self.sim_result_list.takeItem(self.sim_result_list.currentRow()) def load_post_result_files(self): path = self._settings.value("path/processing_results") dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFiles) dialog.setDirectory(path) dialog.setNameFilter("Postprocessing Output files (*.pof)") if dialog.exec_(): files = dialog.selectedFiles() for single_file in files: if single_file: self._load_post_result_file(single_file) def _load_post_result_file(self, file_name): """ loads a post-result file (.pof) """ name = os.path.split(file_name)[-1][:-4] self._logger.info("loading result file {}".format(file_name)) with open(file_name.encode(), "rb") as f: results = pickle.load(f) results.update({"name": name}) self.post_results.append(results) self.post_results_changed.emit() def update_post_result_list(self): self.post_result_list.clear() for res in self.post_results: name = res["name"] self.post_result_list.addItem(name) def remove_post_result_item(self): if self.post_result_list.currentRow() >= 0: del self.post_results[self.post_result_list.currentRow()] self.post_result_list.takeItem(self.post_result_list.currentRow()) def update_post_method_list(self): self.methodList.clear() modules = pm.get_registered_processing_modules(PostProcessingModule) for mod in modules: self.methodList.addItem(mod[1]) def update_meta_method_list(self): self.metaMethodList.clear() modules = pm.get_registered_processing_modules(MetaProcessingModule) for mod in modules: self.metaMethodList.addItem(mod[1]) def post_processor_clicked(self, item): self.run_processor(str(item.text()), "post") def meta_processor_clicked(self, item): self.run_processor(str(item.text()), "meta") def run_processor(self, name, processor_type): if processor_type == "post": result_files = self.results base_cls = PostProcessingModule elif processor_type == "meta": result_files = self.post_results base_cls = MetaProcessingModule else: self._logger.error( "unknown processor type {0}".format(processor_type)) raise ValueError( "unknown processor type {0}".format(processor_type)) if not result_files: self._logger.warning( "run_processor() Error: no result file loaded") return processor_cls = pm.get_processing_module_class_by_name(base_cls, name) processor = processor_cls() figs = [] try: self._logger.info("executing processor '{0}'".format(name)) figs = processor.process(result_files) except Exception as err: self._logger.exception("Error in processor") self.figures_changed.emit(figs, processor_type) self._logger.info("finished postprocessing") def update_figure_lists(self, figures, target_type): # remove no longer needed elements for item, fig in [(key, val[0]) for key, val in self._figure_dict.items() if val[1] == target_type]: if fig not in [new_fig["figure"] for new_fig in figures]: if target_type == "post": old_item = self.post_figure_list.takeItem( self.post_figure_list.row(item)) del old_item elif target_type == "meta": old_item = self.meta_figure_list.takeItem( self.meta_figure_list.row(item)) del old_item del self._figure_dict[item] # add new ones to internal storage for fig in figures: if fig["figure"] not in self._figure_dict.values(): new_entry = [(fig["name"], (QListWidgetItem(fig["name"]), fig["figure"], target_type))] self._figure_dict.update(new_entry) # add to display for key, val in self._figure_dict.items(): if val[2] == "post": self.post_figure_list.addItem(val[0]) elif val[2] == "meta": self.meta_figure_list.addItem(val[0]) self.post_figure_list.setCurrentItem(self.post_figure_list.item(0)) self.meta_figure_list.setCurrentItem(self.meta_figure_list.item(0)) def current_figure_changed(self, current_item, last_item=None): if current_item is None: return figures = self._figure_dict if self.lastFigure: self.grid.removeWidget(self.lastFigure) self.lastFigure.setVisible(False) if current_item.text() in figures: figure_widget = figures[current_item.text()][1] self.grid.addWidget(figure_widget, 1, 1, 5, 1) figure_widget.setVisible(True) self.lastFigure = figure_widget def switch_sides(self): self.displayLeft = not self.displayLeft if self.displayLeft: self.actSwitch.setIcon(QIcon(get_resource("left_mode.png"))) self.post_figure_list.setFocus() self.current_figure_changed(self.post_figure_list.currentItem()) else: self.actSwitch.setIcon(QIcon(get_resource("right_mode.png"))) self.meta_figure_list.setFocus() self.current_figure_changed(self.meta_figure_list.currentItem())
class PageWidget(QWidget): ITEMCLICKED = pyqtSignal(CustomPageItem) RES_PATH = "uilib/img" # 图片资源的路径 STYLE = """ QPushButton#_previous_btn,QPushButton#_next_btn {{ max-width: 49px; max-height: 39px; min-width: 49px; min-height: 39px; width: 49px; height: 39px; border-color: #E4E7EA; }} QPushButton#_previous_btn:hover,QPushButton#_next_btn:hover {{ border-color: #1ABC9C; background-color: #1ABC9C; }} QPushButton#_previous_btn:pressed,QPushButton#_next_btn:pressed {{ border-color: #1ABC9C; background-color: #1ABC9C; }} QPushButton#_previous_btn {{ padding-right: 1px; border-top-left-radius: 6px; border-top-right-radius: 0px; border-bottom-left-radius: 6px; border-bottom-right-radius: 0px; background: #E4E7EA url({RES_PATH}/arrow_left_white.png) no-repeat center center; }} QPushButton#_next_btn {{ padding-left: 1px; border-top-left-radius: 0px; border-top-right-radius: 6px; border-bottom-left-radius: 0px; border-bottom-right-radius: 6px; background: #E4E7EA url({RES_PATH}/arrow_right_white.png) no-repeat center center; }} QListView#_page_list_widget {{ max-height: 40px; background-color: rgba(0,0,0,0); }} QListView#_page_list_widget::item {{ margin: 1px; color: white; background-color: #E4E7EA; height: 40px; }} QListView#_page_list_widget::item:selected {{ background-color: #1ABC9C; }} QListView#_page_list_widget::item:focus {{ background-color: #1ABC9C; }} QListView#_page_list_widget::item:hover {{ background-color: #1ABC9C; }}""" def __init__(self, parent = None, pages = 0): super(PageWidget, self).__init__(parent) self.setObjectName("_page_widget") self.pages = pages # 页数 self.cpage = 0 # 左边按钮 self._previous_btn = BothSidesItem(self, which = "left") self._previous_btn.clicked.connect(self._previous) self._previous_btn.setToolTip("上一页") # 右边按钮 self._next_btn = BothSidesItem(self, which = "right") self._next_btn.clicked.connect(self._next) self._next_btn.setToolTip("下一页") self._page_list_widget = QListWidget(self) self._page_list_widget.setObjectName("_page_list_widget") # 无边框 self._page_list_widget.setFrameShape(QFrame.NoFrame) # 无滑动条 self._page_list_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._page_list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 禁止自动滚动 self._page_list_widget.setAutoScroll(False) # tab切换 self._page_list_widget.setTabKeyNavigation(True) # 设置为横向 self._page_list_widget.setFlow(QListView.LeftToRight) # 设置字体 font = QFont() font.setPointSize(14) self._page_list_widget.setFont(font) # item点击事件 self._page_list_widget.itemClicked.connect(self.ITEMCLICKED.emit) # 布局 hLayout = QHBoxLayout(self) hLayout.setSpacing(0) hLayout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum) hLayout.addItem(spacerItem) hLayout.addWidget(self._previous_btn) hLayout.addWidget(self._page_list_widget) hLayout.addWidget(self._next_btn) hLayout.addItem(spacerItem) self._refresh(pages) self.updateStyle() # 设置样式 def _refresh(self, pages): # 动态调整宽度 if pages == 0: self.resize(100, 40) elif pages >= 8: self._page_list_widget.setMinimumSize(320, 40) self._page_list_widget.resize(320, 40) self.resize(420, 40) self._page_list_widget.clear() # 中间 for i in range(1, pages + 1): CustomPageItem(i, self._page_list_widget) def _previous(self, clicked): '''上一页''' num = int(self.pages / 8) if num == 0: return self.cpage -= 1 if self.cpage >= 0: print("_previous: ", self.cpage) if self.cpage == 0: previousItem = self._page_list_widget.item(0) else: previousItem = self._page_list_widget.item(self.cpage * 8 - 4) self._page_list_widget.scrollTo(self._page_list_widget.indexFromItem(previousItem)) else: self.cpage += 1 def _next(self, clicked): '''下一页''' num = int(self.pages / 8) if num == 0: return self.cpage += 1 if self.cpage <= num: print("_next: ", self.cpage) remainder = self.pages - self.cpage * 8 if remainder > 8: nextItem = self._page_list_widget.item(self.cpage * 8 + 7) else: nextItem = self._page_list_widget.item(self.pages - 1) self._page_list_widget.scrollTo(self._page_list_widget.indexFromItem(nextItem)) else: self.cpage -= 1 def setResPath(self, resPath): '''设置资源路径''' self.RES_PATH = resPath def getResPath(self): return self.RES_PATH def updateStyle(self): '''刷新样式''' self.setStyleSheet(self.STYLE.format(RES_PATH = self.RES_PATH)) def setPages(self, pages): '''设置页数''' self.pages = pages self._refresh(pages) def getPages(self): '''得到当前总的页数''' return self.pages
class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self) self.current_image = None self.createComponents() self.loadFromBufferMemory() #print("Input file type", type(input_file)) self.createMenu() self.createLayout() self.createConnects() self.setWindowTitle('Micrograph Selection Tool') def createComponents(self): self.buttonLoad = QPushButton('Load') self.buttonSave = QPushButton('Save') self.buttonKeep = QPushButton('Keep (Right arrow key)') self.buttonKeep.setStyleSheet('QPushButton {color: green;}') self.buttonDiscard = QPushButton('Discard (Left arrow key)') self.buttonDiscard.setStyleSheet('QPushButton {color: red;}') self.fileList = QListWidget() self.micrograph = emimage2d.EMImage2DWidget(image=EMAN2.EMData( 700, 700), application=app, parent=self) self.powerSpectrum = emimage2d.EMImage2DWidget(image=EMAN2.EMData( 700, 700), application=app, parent=self) self.all_items = [] def loadFromBufferMemory(self): """loads the content from the buffer database""" image_list = [] buffer_lines = [] self.buffer_path = "/home/turi/Schreibtisch/buffermemory.txt" with open(self.buffer_path) as buffer: buffer = buffer.read() if len(buffer) > 0: buffer_lines = buffer.split(',') print("buffer", buffer_lines) else: print("Buffer is empty") for image_index in range(len(buffer_lines)): # cleans the image name and status to add them in the GUI image_path = str(buffer_lines[image_index].split(':')[0]) print("imagepath", image_path) image_status = str(buffer_lines[image_index].split(':')[1]) print("imagestatus", image_status) # remove symbols from the image_name string if "(u'" in image_path: image_path = image_path.replace("(u'", "") if "',)" in image_path: image_path = image_path.replace("',)", "") if "\n" in image_path: image_path = image_path.replace("\n", "") if "'" in image_path: image_path = image_path.replace("'", "") if "\"" in image_path: image_path = image_path.replace("\"", "") if "[" in image_path: image_path = image_path.replace("[", "") if "]" in image_path: image_path = image_path.replace("]", "") if "\\" in image_path: image_path = image_path.replace("\\", "") image_name = QFileInfo(image_path).fileName() image_list.append(image_path) # adds the name item = QListWidgetItem(image_name) # remove symbols from the status string if "(u'" in image_status: image_status = image_status.replace("(u'", "") if "',)" in image_status: image_status = image_status.replace("',)", "") if "\n" in image_status: image_status = image_status.replace("\n", "") if "'" in image_status: image_status = image_status.replace("'", "") if "[" in image_status: image_status = image_status.replace("[", "") if "]" in image_status: image_status = image_status.replace("]", "") if image_status == "checked": item.setCheckState(Qt.Checked) if image_status == "unchecked": item.setCheckState(Qt.Unchecked) self.fileList.addItem(item) if len(image_list) > 0: self.uploadMicrographs(image_list=image_list, upload_from_buffer=True) else: pass def createLayout(self): """layout: left, middle, right layouts, all three are vertically structured""" layoutMain = QHBoxLayout() layoutLeft = QVBoxLayout() layoutLeft.addWidget(self.buttonLoad) layoutLeft.addWidget(self.fileList) layoutLeft.addWidget(self.buttonSave) layoutMiddle = QVBoxLayout() layoutMiddle.addWidget(self.micrograph) layoutMiddle.addWidget(self.buttonKeep) layoutRight = QVBoxLayout() layoutRight.addWidget(self.powerSpectrum) layoutRight.addWidget(self.buttonDiscard) layoutMain.addLayout(layoutLeft, stretch=1) layoutMain.addLayout(layoutMiddle, stretch=2) layoutMain.addLayout(layoutRight, stretch=2) widgetMain = QWidget() widgetMain.setLayout(layoutMain) self.setCentralWidget(widgetMain) def createMenu(self): self.actionOpenData = QAction(("Open file.."), self) self.actionSaveData = QAction(("Save"), self) self.actionSelectAll = QAction(("Select all"), self) self.actionDeselectAll = QAction(("Deselect all"), self) self.actionSetCheck_fromKeepFile = QAction("Upload Keep/Discard Files", self) self.actionQuit = QAction(("Quit"), self) self.actionQuit.setMenuRole(QAction.QuitRole) menu_file = self.menuBar().addMenu("File") menu_file.addAction(self.actionOpenData) menu_file.addAction(self.actionSaveData) menu_file.addAction(self.actionSelectAll) menu_file.addAction(self.actionDeselectAll) menu_file.addAction(self.actionSetCheck_fromKeepFile) menu_file.addSeparator() menu_file.addAction(self.actionQuit) self.menuBar().addMenu("Help") def createConnects(self): self.buttonLoad.clicked.connect( self.buttonLoad_clicked) # function call without ()-sign self.buttonSave.clicked.connect(self.buttonSave_clicked) self.buttonKeep.clicked.connect(self.buttonKeep_clicked) self.buttonDiscard.clicked.connect(self.buttonDiscard_clicked) self.fileList.clicked.connect(self.changeImageByMouse) self.fileList.keyPressEvent = self.myKeyPressEvent self.micrograph.keyPressEvent = self.myKeyPressEvent self.powerSpectrum.keyPressEvent = self.myKeyPressEvent self.actionSelectAll.triggered.connect(self.selectAll) self.actionDeselectAll.triggered.connect(self.deselectAll) self.actionSetCheck_fromKeepFile.triggered.connect( self.setCheck_fromKeepFile) def myKeyPressEvent(self, buttonSignal): if self.fileList.count() > 0: if buttonSignal.key() == Qt.Key_Right: print "Right" self.arrowKeyRight_clicked() elif buttonSignal.key() == Qt.Key_Left: print "Left" self.arrowKeyLeft_clicked() elif buttonSignal.key() == Qt.Key_Up: print "Up" self.arrowKeyUp_clicked() elif buttonSignal.key() == Qt.Key_Down: print "Down!" self.arrowKeyDown_clicked() def loadMicrographsFromItemList(self, image_index): """creates an EMImage-Widget""" if self.current_image is None: image_path = self.all_items[image_index] # self.current_image = EMAN2.EMData(image_path) self.micrograph.set_data( self.current_image ) # in project description as 'aaa' instead of 'micrograph' def loadPowerSpectrumFromItemList(self, image_index): """creates power spectrum of a micrograph""" # Try to load image from buffer # If buffer is empty, wait the image appears while True: load_successfull = False try: preload_image_path, preload_image_index, img, fft_img = img_buffer.popleft( ) if preload_image_index == image_index: load_successfull = True except IndexError as e: print("Index error", e) time.sleep(1) if load_successfull: break self.current_image = img self.powerSpectrum.set_data(fft_img) def preload_images(self, micrograph_list, img_buffer): """ Preloads IMG_BUFFER_SIZE number of images into the memory. :param micrograph_list: list of all micrographs ######## mistake --> here it gets just a string not a list :param img_buffer: deque as buffer for images :return: None """ print("preload_images micrograph_list", micrograph_list) offset = 0 last_index = -1 while True: index = self.fileList.row(self.fileList.currentItem()) if last_index != -1: if index - last_index == 1: offset = offset - 1 elif index - last_index != 0: offset = 0 if len(img_buffer) < IMG_BUFFER_SIZE and ( index + offset) < len(micrograph_list): start = time.time() print("in", index + offset) print("micrograph_list", micrograph_list) filename = str(micrograph_list[index + offset]) print("filename", filename) image = EMAN2.EMData(filename) fft_img = image.do_fft() fft_img.set_value_at(0, 0, 0, 0) fft_img.set_value_at(1, 0, 0, 0) fft_img.process_inplace("xform.phaseorigin.tocorner") fft_img.process_inplace("xform.fourierorigin.tocenter") fft_img = fft_img.get_fft_amplitude() img_buffer.append((filename, index + offset, image, fft_img)) end = time.time() print("Put new image:", filename, "Current index", index, "Image+offset:", index + offset, "offset", offset, "time", end - start) offset = offset + 1 else: time.sleep(1) last_index = index def buttonLoad_clicked(self): """"opens dialog for folder selection, """ image_dir = QFileDialog.getExistingDirectory(parent=None, caption=u"Open directory") image_dir = str(image_dir) image_list = [] for root, dirs, files in os.walk(image_dir): for file in files: if (".mrc" or ".tiff" or ".tif" or ".hdf" or ".png") in file: image_list.append(image_dir + "/" + file) #if no micrograph loaded if len(image_list) == 0: msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setInformativeText("No images in the selected folder") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() if len(image_list) > 0: self.uploadMicrographs(image_list=image_list, upload_from_buffer=False) else: pass def buttonSave_clicked(self): """the keep and discard image names are added to two independent text files one for keep one for discard""" save_dir = QFileDialog.getExistingDirectory( parent=None, caption="Directory of selected and discarded file with image names" ) path_selected = str(save_dir) + "/" + "selected_images.txt" path_discarded = str(save_dir) + "/" + "discarded_images.txt" try: file_selected = open(path_selected, "w+") file_discarded = open(path_discarded, "w+") except IOError: return number_of_items = self.fileList.count() for index in range(number_of_items): # as often as number of files if (self.fileList.item(index).checkState()) == Qt.Checked: file_selected.write( os.path.basename(self.all_items[index]) + "\n") else: file_discarded.write( os.path.basename(self.all_items[index]) + "\n") file_selected.close() file_discarded.close() def uploadMicrographs(self, image_list, upload_from_buffer): """loads the micrograph into the GUI and saves their path and status in buffer memory""" #global all_items # maybe problematic? #all_items = [] self.all_items = [] image_path_and_status = [] if upload_from_buffer == True: for image_path in image_list: # loads the micrograph names and status into the GUI self.all_items.append(image_path) if upload_from_buffer == False: self.fileList.clear() # add a try except tree for image_path in image_list: print "Path", image_path image_name = QFileInfo(image_path).fileName() item = QListWidgetItem(image_name) image_path = str( image_path) # contains the path and the name of the image self.all_items.append(image_path) image_path_and_status.append(image_path + ":" + "checked") item.setCheckState(Qt.Checked) self.fileList.addItem(item) # remove symbols from the list image_path_and_status = str(image_path_and_status) if "'" in image_path_and_status: image_path_and_status = image_path_and_status.replace("'", "") if "[" in image_path_and_status: image_path_and_status = image_path_and_status.replace("[", "") if "]" in image_path_and_status: image_path_and_status = image_path_and_status.replace("]", "") if " " in image_path_and_status: image_path_and_status = image_path_and_status.replace(" ", "") with open(self.buffer_path, "w") as buffer: buffer.write(image_path_and_status) if len(image_list) > 0: # loads the micrographs into the GUI self.fileList.setCurrentRow(0) thread.start_new_thread(self.preload_images, ( image_list, img_buffer, )) self.loadPowerSpectrumFromItemList(0) self.loadMicrographsFromItemList(0) last_loaded_image = 0 def writeToBuffer_check(self): """the status of the image is written to the buffer""" row_index = self.fileList.row(self.fileList.currentItem()) with open(self.buffer_path, "r") as buffer: buffer_lines = buffer.read().split(",") buffer_line = buffer_lines[row_index].split(":") print(buffer_line) image_path = buffer_line[0] image_status = buffer_line[1] # order of checking importance: checked is included in unchecked !!! if "unchecked" in image_status: image_status = image_status.replace("unchecked", "checked") elif "checked" in image_status: pass buffer_lines[row_index] = str(image_path + ":" + image_status) buffer_lines = str(buffer_lines) if "'" in buffer_lines: buffer_lines = buffer_lines.replace("'", "") if "\"" in buffer_lines: buffer_lines = buffer_lines.replace("\"", "") if "[" in buffer_lines: buffer_lines = buffer_lines.replace("[", "") if "]" in buffer_lines: buffer_lines = buffer_lines.replace("]", "") if " " in buffer_lines: buffer_lines = buffer_lines.replace(" ", "") if "\\" in buffer_lines: buffer_lines = buffer_lines.replace("\\", "") print("Bufferline", str(buffer_lines)) with open(self.buffer_path, "w") as buffer: buffer.write(str(buffer_lines)) def writeToBuffer_uncheck(self): """the status of the image is written to the buffer""" row_index = self.fileList.row(self.fileList.currentItem()) with open(self.buffer_path, "r") as buffer: buffer_lines = buffer.read().split(",") buffer_line = buffer_lines[row_index].split(":") image_path = buffer_line[0] image_status = buffer_line[1] # order of checking importance: checked is included in unchecked !!! if "unchecked" in image_status: pass elif "checked" in image_status: image_status = image_status.replace("checked", "unchecked") buffer_lines[row_index] = str(image_path + ":" + image_status) print("Puffer", buffer_lines) buffer_lines = str(buffer_lines) if "'" in buffer_lines: buffer_lines = buffer_lines.replace("'", "") if "\"" in buffer_lines: buffer_lines = buffer_lines.replace("\"", "") if "[" in buffer_lines: buffer_lines = buffer_lines.replace("[", "") if "]" in buffer_lines: buffer_lines = buffer_lines.replace("]", "") if " " in buffer_lines: buffer_lines = buffer_lines.replace(" ", "") if "\\" in buffer_lines: buffer_lines = buffer_lines.replace("\\", "") print("Bufferline", str(buffer_lines)) with open(self.buffer_path, "w") as buffer: buffer.write(str(buffer_lines)) def setCheck_fromKeepFile(self): """images are automatically selected or discarded according to saved keep files""" file_dirs = QFileDialog.getExistingDirectory(parent=None, caption=u"Open files") file_list = [] files_selected = [] for root, dirs, files in os.walk(file_dirs): for file in files: if ("selected_images.txt") in file: file_list.append(file_dirs + "/" + file) files_selected.append(file_dirs + "/" + file) if ("discarded_images.txt") in file: file_list.append(file_dirs + "/" + file) # if no micrograph loaded if len(file_list) == 0: msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setInformativeText("No textfile in the selected folder") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() else: for index in range(len(files_selected)): with open(files_selected[index], "r") as file: selected_images = file.readlines() if len(selected_images) > 0: if self.fileList.currentItem() is not None: self.fileList.setCurrentRow(0) for uploaded_image in self.all_items: # image contains name and path # all_items has the same order like self.fileList uploaded_image = str(uploaded_image) uploaded_image = uploaded_image.split("/")[ -1] # removes the path, extracts the name for index in range(len(selected_images)): if "\n" in selected_images[index]: selected_images[index] = selected_images[ index].replace("\n", "") if uploaded_image in selected_images: self.setCheck() self.moveToNextItem() else: self.setUncheck() self.moveToNextItem() else: pass def buttonKeep_clicked(self): if self.fileList.currentItem() is not None: self.setCheck() self.moveToNextItem() self.showImageOfCurrentRow() def buttonDiscard_clicked(self): if self.fileList.currentItem() is not None: self.setUncheck() self.moveToNextItem() self.showImageOfCurrentRow() def arrowKeyRight_clicked(self): self.setCheck() self.moveToNextItem() self.showImageOfCurrentRow() def arrowKeyLeft_clicked(self): self.setUncheck() self.moveToNextItem() self.showImageOfCurrentRow() def arrowKeyUp_clicked(self): self.moveToPrevItem() self.showImageOfCurrentRow() def arrowKeyDown_clicked(self): self.moveToNextItem() self.showImageOfCurrentRow() def setCheck(self): self.fileList.currentItem().setCheckState(Qt.Checked) self.writeToBuffer_check() def setUncheck(self): self.fileList.currentItem().setCheckState(Qt.Unchecked) self.writeToBuffer_uncheck() def selectAll(self): """all images get the checked status""" print("bbb") self.fileList.setCurrentRow(0) self.loadPowerSpectrumFromItemList(0) self.loadMicrographsFromItemList(0) print(len(self.all_items)) for row_index in range(len(self.all_items)): print("bbbbb") self.setCheck() self.moveToNextItem() def deselectAll(self): """all images get the unchecked status""" print("ccc") self.fileList.setCurrentRow(0) self.loadPowerSpectrumFromItemList(0) self.loadMicrographsFromItemList(0) print(len(self.all_items)) for row_index in range(len(self.all_items)): print("ccccc") self.setUncheck() self.moveToNextItem() def moveToNextItem(self): row_index = self.fileList.row(self.fileList.currentItem()) self.fileList.setCurrentRow(row_index + 1) if row_index >= (self.fileList.count()) - 1: self.fileList.setCurrentRow(0) def moveToPrevItem(self): row_index = self.fileList.row(self.fileList.currentItem()) self.fileList.setCurrentRow(row_index - 1) if row_index >= (self.fileList.count()) - 1: self.fileList.setCurrentRow(0) def changeImageByMouse(self): self.showImageOfCurrentRow() def showImageOfCurrentRow(self): """the image of current row is shown""" global last_loaded_image # maybe problematic? row_index = self.fileList.row(self.fileList.currentItem()) if last_loaded_image - row_index != 0: self.loadPowerSpectrumFromItemList(row_index) self.loadMicrographsFromItemList(row_index) last_loaded_image = row_index
class DebuggerMainWindow(object): def __init__(self, comm): self.comm = comm self.follow_state = True self.init_instances() self.init_widgets() self.init_actions() self.window.closeEvent = self.close_handler self.window.showMaximized() def run(self): self.poll_timer = QTimer() self.poll_timer.timeout.connect(self.poll_handler) self.comm.start_worker() self.poll_timer.start(1000) def poll_handler(self): self.comm.put_job(self.comm.get_agent_track) data = self.comm.take_data() for location_info, state in data: file_name, node_id = location_info i = self.get_instance(file_name) if i is None: text = "%s:%s" % (str(location_info), state) else: node = i.model.get_node(node_id) if node is not None: text = "%s: %s: %s" % (file_name, node.get_display_text(), state) else: text = "%s:%s" % (str(location_info), state) self.add_to_history_list(location_info, text) if self.follow_state: self.focus_last() def init_widgets(self): self.window = loadUi(MAIN_UI_PATH) self.mdi = self.window.mdiArea self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.setTabsMovable(True) self.mdi.setTabsClosable(True) self.mdi.setTabShape(QTabWidget.Rounded) self.dock_anchor = self.window.dockAnchor self.dock_anchor.layout().setAlignment(Qt.AlignTop) self.history_list = QListWidget() self.history_list.itemSelectionChanged.connect(self.list_item_selected) self.add_dock_content(self.history_list) def list_item_selected(self): citem = self.history_list.currentItem() self.focus_on_location(citem.location_info) def init_instances(self): self.instances = {} def remove_instance(self, ins): file_name = ins.file_name self.instances.pop(file_name) def add_instance(self, ins): file_name = ins.file_name self.instances[file_name] = ins def get_instance(self, file_name): if file_name in self.instances: return self.instances[file_name] else: full_path = GraphInstanceVM.get_full_path(file_name) model = GraphInstanceVM.get_model(full_path) if model is None: return None ins = GraphInstanceVM(model, self, file_name) return ins def init_actions(self): self.window.actionResume.triggered.connect(self.action_resume_handler) self.window.actionStop.triggered.connect(self.action_stop_handler) self.window.actionFollow.triggered.connect(self.action_follow_handler) self.window.actionHold.triggered.connect(self.action_hold_handler) def action_resume_handler(self): self.clear_list() self.comm.put_job(functools.partial(self.comm.track_agent, True)) def action_stop_handler(self): self.comm.put_job(functools.partial(self.comm.track_agent, False)) def action_follow_handler(self): self.follow_state = True def action_hold_handler(self): self.follow_state = False def close_handler(self, ev): # disable tracking for some agents self.poll_timer.stop() self.comm.put_job(self.comm.finish) self.comm.shutdown() def add_dock_content(self, widget): self.clear_dock_contents() self.dock_anchor.layout().addWidget(widget) def get_dock_contents(self): l = self.dock_anchor.layout() ret = [] for i in range(l.count()): ret.append(l.itemAt(i).widget()) return ret def clear_dock_contents(self): ws = self.get_dock_contents() for w in ws: w.deleteLater() def focus_on_location(self, location): file_name, node_id = location instance = self.get_instance(file_name) if instance is None: logger.fatal("can not find src file for %s" % file_name) return False ret = instance.focus_on(node_id) if ret is False: logger.fatal("can not find node %s for %s" % (node_id, file_name)) return False return True def add_to_history_list(self, location, text): item = HistoryListItem(location, text) self.history_list.addItem(item) def clear_list(self): self.history_list.clear() def focus_last(self): c = self.history_list.count() if c > 0: item = self.history_list.item(c-1) self.history_list.setCurrentItem(item) self.focus_on_location(item.location_info)
class SubSheet(SimpleBlackbox): author = "DrLuke" name = "Subsheet" modulename = "subsheet" Category = ["Builtin"] placeable = True implementation = SubSheetImplementation def __init__(self, *args, **kwargs): self.ownsheet = None self.sheets = None self.selectedSheet = None self.listSheetItems = {} super(SubSheet, self).__init__(*args, **kwargs) self.propertiesWidget = QWidget() self.vlayout = QVBoxLayout() self.listWidget = QListWidget() self.listWidget.itemClicked.connect(self.listClicked) self.vlayout.addWidget(self.listWidget) self.vlayout.addItem( QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.propertiesWidget.setLayout(self.vlayout) def getPropertiesWidget(self): return self.propertiesWidget def updateSheets(self): if self.sheets is not None and self.ownsheet is not None: self.listSheetItems = {} self.listWidget.clear() for sheetId in self.sheets: if not sheetId == self.ownsheet: newItem = QListWidgetItem(self.sheets[sheetId]) newItem.setToolTip(str(sheetId)) newItem.setData(Qt.UserRole, sheetId) self.listSheetItems[sheetId] = newItem self.listWidget.addItem(newItem) if sheetId == self.selectedSheet: boldFont = QFont() boldFont.setBold(True) newItem.setFont(boldFont) def listClicked(self, item): normalFont = QFont() boldFont = QFont() boldFont.setBold(True) for i in range(self.listWidget.count()): itemnormal = self.listWidget.item(i) itemnormal.setFont(normalFont) self.selectedSheet = item.data(Qt.UserRole) self.sendDataToImplementations({"subsheetid": self.selectedSheet}) item.setFont(boldFont) def serialize(self): return {"subsheetid": self.selectedSheet} def deserialize(self, data): if data is not None: if "subsheetid" in data: self.selectedSheet = data["subsheetid"] self.sendDataToImplementations( {"subsheetid": self.selectedSheet}) def selectedChanged(self, state): if state: self.mainRect.setPen(QPen(Qt.red)) else: self.mainRect.setPen(QPen(Qt.blue)) def defineIO(self): self.addInput(execType, "execInit", "Execute Init") self.addInput(execType, "execLoop", "Execute Loop") self.addOutput(execType, "ExecInitOut", "Init Done") self.addOutput(execType, "ExecLoopOut", "Loop Done")
class CollectionCueSettings(SettingsPage): Name = 'Edit Collection' def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout(self)) self.cuesWidget = QListWidget(self) self.cuesWidget.setAlternatingRowColors(True) self.layout().addWidget(self.cuesWidget) # Buttons self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.layout().addWidget(self.dialogButtons) self.addButton = self.dialogButtons.addButton('Add', QDialogButtonBox.ActionRole) self.addButton.clicked.connect(self._add_dialog) self.delButton = self.dialogButtons.addButton('Remove', QDialogButtonBox.ActionRole) self.delButton.clicked.connect(self._remove_selected) self.cue_dialog = CueListDialog(cues=Application().cue_model) self.cue_dialog.list.setSelectionMode(QAbstractItemView.ExtendedSelection) def load_settings(self, settings): for target_id, action in settings.get('targets', []): target = Application().cue_model.get(target_id) if target is not None: self._add_cue(target, action) def get_settings(self): targets = [] for n in range(self.cuesWidget.count()): widget = self.cuesWidget.itemWidget(self.cuesWidget.item(n)) target_id, action = widget.get_target() targets.append((target_id, action)) return {'targets': targets} def _add_cue(self, cue, action): item = QListWidgetItem() item.setSizeHint(QSize(200, 30)) widget = CueItemWidget(cue, action, self.cue_dialog) self.cuesWidget.addItem(item) self.cuesWidget.setItemWidget(item, widget) self.cue_dialog.remove_cue(cue) def _add_dialog(self): if self.cue_dialog.exec_() == QDialog.Accepted: for target in self.cue_dialog.selected_cues(): self._add_cue(target, tuple(target.CueActions)[0].name) def _remove_selected(self): cue = self.cuesWidget.itemWidget(self.cuesWidget.currentItem()).target self.cuesWidget.takeItem(self.cuesWidget.currentRow()) self.cue_dialog.add_cue(cue)
class ChatSetter(QWidget): """...""" saved = pyqtSignal(str, bool, list) def __init__(self, name_): super(ChatSetter, self).__init__() self._name = name_ self._setup_ui() def enable(self, enable_): """...""" self._enable.setChecked(enable_) def display_filter(self, filter_list_): """...""" self._filter.clear() for _name in filter_list_: _item = QListWidgetItem(_name) _item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable) self._filter.addItem(_item) def _on_clicked(self, item_): self._filter.setCurrentItem(item_) for _index in range(self._filter.count()): self._filter.item(_index).setBackground(QColor('white')) self._filter.item(_index).setForeground(QColor('black')) item_.setBackground(QColor(0, 105, 217)) item_.setForeground(QColor('white')) def _setup_ui(self): _vbox = QVBoxLayout() self._enable = QCheckBox(self._name) self._filter = QListWidget() self._filter.itemClicked.connect(self._on_clicked) self._filter.itemChanged.connect(self._save_filter) self._filter.setSelectionMode(QAbstractItemView.SingleSelection) _vbox.addWidget(self._enable) _vbox.addWidget(self._filter) _vbox.addLayout(self._gen_filter_btn_group()) self.setLayout(_vbox) def _gen_filter_btn_group(self): _add = QPushButton('Add') _add.clicked.connect(self._add_filter) _delete = QPushButton('Delete') _delete.clicked.connect(self._delete_filter) _btn_group = QHBoxLayout() _btn_group.addWidget(_add) _btn_group.addWidget(_delete) _btn_group.setSpacing(0) return _btn_group def _add_filter(self): _name = GuiUtils.input_dialog(self, 'Filter', 'Add filter:') if _name: _item = QListWidgetItem(_name) _item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable) self._filter.addItem(_item) self._save_filter() def _delete_filter(self): try: _item = self._filter.currentItem() if GuiUtils.question_dialog(self, 'Filter', 'Delete filter: ' + _item.text()): _item = self._filter.takeItem(self._filter.currentRow()) del _item self._save_filter() except Exception as e: _msg = 'Choose one item before deleting.' print(_msg) def _save_filter(self): self.saved.emit(self._name, self._enable.isChecked(), [ self._filter.item(_index).text() for _index in range(self._filter.count()) ])
class PostProcessor(QMainWindow): sim_results_changed = pyqtSignal() post_results_changed = pyqtSignal() figures_changed = pyqtSignal(list, str) def __init__(self, parent=None): QMainWindow.__init__(self, parent) self._settings = QSettings() self._logger = logging.getLogger(self.__class__.__name__) self.setWindowTitle("Processing") self.setWindowIcon(QIcon(get_resource("processing.png"))) self.mainFrame = QWidget(self) self.resize(1000, 600) # toolbar self.toolBar = QToolBar("file control") self.toolBar.setIconSize(QSize(24, 24)) self.addToolBar(self.toolBar) self.actLoad = QAction(self) self.actLoad.setText("load result file") self.actLoad.setIcon(QIcon(get_resource("load.png"))) self.actLoad.setDisabled(False) self.actLoad.triggered.connect(self.load_result_files) self.actPostLoad = QAction(self) self.actPostLoad.setText("load post-result file") self.actPostLoad.setIcon(QIcon(get_resource("load.png"))) self.actPostLoad.setDisabled(False) self.actPostLoad.triggered.connect(self.load_post_result_files) self.actSwitch = QAction(self) self.actSwitch.setText("switch display mode") self.actSwitch.setIcon(QIcon(get_resource("left_mode.png"))) self.actSwitch.setDisabled(False) self.actSwitch.triggered.connect(self.switch_sides) self.displayLeft = True self.spacer1 = QWidget() self.spacer2 = QWidget() self.spacer1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.actReloadMethods = QAction(self) self.actReloadMethods.setText("reload methods") self.actReloadMethods.setIcon(QIcon(get_resource("reload.png"))) self.actReloadMethods.setDisabled(False) self.actReloadMethods.triggered.connect(self.update_post_method_list) self.actReloadMetaMethods = QAction(self) self.actReloadMetaMethods.setText("reload meta methods") self.actReloadMetaMethods.setIcon(QIcon(get_resource("reload.png"))) self.actReloadMetaMethods.setDisabled(False) self.actReloadMetaMethods.triggered.connect( self.update_meta_method_list) self.toolBar.addAction(self.actLoad) self.toolBar.addAction(self.actReloadMethods) self.toolBar.addWidget(self.spacer1) self.toolBar.addAction(self.actSwitch) self.toolBar.addWidget(self.spacer2) self.toolBar.addAction(self.actReloadMetaMethods) self.toolBar.addAction(self.actPostLoad) # main window self.grid = QGridLayout(self.mainFrame) self.grid.setColumnMinimumWidth(0, 70) self.grid.setColumnStretch(0, 0) self.grid.setColumnStretch(1, 1) self.methodList = QListWidget(self) self.methodList.itemDoubleClicked.connect( self.post_processor_clicked) self.update_post_method_list() self.metaMethodList = QListWidget(self) self.metaMethodList.itemDoubleClicked.connect( self.meta_processor_clicked) self.update_meta_method_list() self.sim_result_list = QListWidget(self) self.sim_results_changed.connect(self.update_result_list) self.results = [] self.delShort = QShortcut(QKeySequence(Qt.Key_Delete), self.sim_result_list) self.delShort.activated.connect(self.remove_result_item) # figures self._figure_dict = {} self.figures_changed.connect(self.update_figure_lists) self.post_figure_list = QListWidget(self) self.post_figure_list.currentItemChanged.connect( self.current_figure_changed) self.meta_figure_list = QListWidget(self) self.meta_figure_list.currentItemChanged.connect( self.current_figure_changed) self.plotView = QWidget() self.lastFigure = None self.post_result_list = QListWidget(self) self.post_results_changed.connect(self.update_post_result_list) self.post_results = [] self.delShortPost = QShortcut(QKeySequence(Qt.Key_Backspace), self.post_result_list) self.delShortPost.activated.connect(self.remove_post_result_item) # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) self.grid.addWidget(QLabel("Result Files:"), 0, 0) self.grid.addWidget(self.sim_result_list, 1, 0) self.grid.addWidget(QLabel("Postprocessors:"), 2, 0) self.grid.addWidget(self.methodList, 3, 0) self.grid.addWidget(QLabel("Figures:"), 4, 0) self.grid.addWidget(self.post_figure_list, 5, 0) self.grid.addWidget(QLabel("Selected Figure:"), 0, 1) self.grid.addWidget(QLabel("Postprocessor Files:"), 0, 2) self.grid.addWidget(self.post_result_list, 1, 2) self.grid.addWidget(QLabel("Metaprocessors:"), 2, 2) self.grid.addWidget(self.metaMethodList, 3, 2) self.grid.addWidget(QLabel("Figures:"), 4, 2) self.grid.addWidget(self.meta_figure_list, 5, 2) self.grid.addWidget(self.logBox, 6, 0, 1, 3) self.mainFrame.setLayout(self.grid) self.setCentralWidget(self.mainFrame) # status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) def load_result_files(self): path = self._settings.value("path/simulation_results") dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFiles) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Result files (*.pmr)") if dialog.exec_(): files = dialog.selectedFiles() for single_file in files: if single_file: self._load_result_file(single_file) def _load_result_file(self, file_name): """ loads a result file """ self._logger.info("loading result file {}".format(file_name)) with open(file_name.encode(), "rb") as f: self.results.append(pickle.load(f)) self.sim_results_changed.emit() def update_result_list(self): self.sim_result_list.clear() for res in self.results: name = res["regime name"] self.sim_result_list.addItem(name) def remove_result_item(self): if self.sim_result_list.currentRow() >= 0: del self.results[self.sim_result_list.currentRow()] self.sim_result_list.takeItem(self.sim_result_list.currentRow()) def load_post_result_files(self): path = self._settings.value("path/processing_results") dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFiles) dialog.setDirectory(path) dialog.setNameFilter("Postprocessing Output files (*.pof)") if dialog.exec_(): files = dialog.selectedFiles() for single_file in files: if single_file: self._load_post_result_file(single_file) def _load_post_result_file(self, file_name): """ loads a post-result file (.pof) """ name = os.path.split(file_name)[-1][:-4] self._logger.info("loading result file {}".format(file_name)) with open(file_name.encode(), "rb") as f: results = pickle.load(f) results.update({"name": name}) self.post_results.append(results) self.post_results_changed.emit() def update_post_result_list(self): self.post_result_list.clear() for res in self.post_results: name = res["name"] self.post_result_list.addItem(name) def remove_post_result_item(self): if self.post_result_list.currentRow() >= 0: del self.post_results[self.post_result_list.currentRow()] self.post_result_list.takeItem(self.post_result_list.currentRow()) def update_post_method_list(self): self.methodList.clear() modules = pm.get_registered_processing_modules(PostProcessingModule) for mod in modules: self.methodList.addItem(mod[1]) def update_meta_method_list(self): self.metaMethodList.clear() modules = pm.get_registered_processing_modules(MetaProcessingModule) for mod in modules: self.metaMethodList.addItem(mod[1]) def post_processor_clicked(self, item): self.run_processor(str(item.text()), "post") def meta_processor_clicked(self, item): self.run_processor(str(item.text()), "meta") def run_processor(self, name, processor_type): if processor_type == "post": result_files = self.results base_cls = PostProcessingModule elif processor_type == "meta": result_files = self.post_results base_cls = MetaProcessingModule else: self._logger.error("unknown processor type {0}".format( processor_type)) raise ValueError("unknown processor type {0}".format( processor_type)) if not result_files: self._logger.warning("run_processor() Error: no result file loaded") return processor_cls = pm.get_processing_module_class_by_name(base_cls, name) processor = processor_cls() figs = [] try: self._logger.info("executing processor '{0}'".format(name)) figs = processor.process(result_files) except Exception as err: self._logger.exception("Error in processor") self.figures_changed.emit(figs, processor_type) self._logger.info("finished postprocessing") def update_figure_lists(self, figures, target_type): # remove no longer needed elements for item, fig in [(key, val[0]) for key, val in self._figure_dict.items() if val[1] == target_type]: if fig not in [new_fig["figure"] for new_fig in figures]: if target_type == "post": old_item = self.post_figure_list.takeItem( self.post_figure_list.row(item)) del old_item elif target_type == "meta": old_item = self.meta_figure_list.takeItem( self.meta_figure_list.row(item)) del old_item del self._figure_dict[item] # add new ones to internal storage for fig in figures: if fig["figure"] not in self._figure_dict.values(): new_entry = [(fig["name"], (QListWidgetItem(fig["name"]), fig["figure"], target_type) )] self._figure_dict.update(new_entry) # add to display for key, val in self._figure_dict.items(): if val[2] == "post": self.post_figure_list.addItem(val[0]) elif val[2] == "meta": self.meta_figure_list.addItem(val[0]) self.post_figure_list.setCurrentItem(self.post_figure_list.item(0)) self.meta_figure_list.setCurrentItem(self.meta_figure_list.item(0)) def current_figure_changed(self, current_item, last_item=None): if current_item is None: return figures = self._figure_dict if self.lastFigure: self.grid.removeWidget(self.lastFigure) self.lastFigure.setVisible(False) if current_item.text() in figures: figure_widget = figures[current_item.text()][1] self.grid.addWidget(figure_widget, 1, 1, 5, 1) figure_widget.setVisible(True) self.lastFigure = figure_widget def switch_sides(self): self.displayLeft = not self.displayLeft if self.displayLeft: self.actSwitch.setIcon(QIcon(get_resource("left_mode.png"))) self.post_figure_list.setFocus() self.current_figure_changed(self.post_figure_list.currentItem()) else: self.actSwitch.setIcon(QIcon(get_resource("right_mode.png"))) self.meta_figure_list.setFocus() self.current_figure_changed(self.meta_figure_list.currentItem())
class MultiSelectComboBox(QComboBox): search_bar_index = 0 updated = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.list_widget = QListWidget(self) self.line_edit = QLineEdit(self) self.search_bar = QLineEdit(self) current_item = QListWidgetItem(self.list_widget) self.search_bar.setPlaceholderText("Search...") self.search_bar.setClearButtonEnabled(True) # First item in the list is the SEARCH BAR self.list_widget.addItem(current_item) self.list_widget.setItemWidget(current_item, self.search_bar) self.line_edit.setReadOnly(True) self.line_edit.installEventFilter(self) self.setModel(self.list_widget.model()) self.setView(self.list_widget) self.setLineEdit(self.line_edit) self.search_bar.textChanged.connect(self.onSearch) self.activated.connect(self.itemClicked) def hidePopup(self): width = self.width() height = self.list_widget.height() x = QCursor.pos().x() - self.mapToGlobal( self.geometry().topLeft()).x() + self.geometry().x() y = QCursor.pos().y() - self.mapToGlobal( self.geometry().topLeft()).y() + self.geometry().y() if x >= 0 and x <= width and y >= self.height( ) and y <= height + self.height(): pass # Item was clicked do not hide popup else: super().hidePopup() def stateChanged(self, state=None): # state is unused selected_data = [] count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) if check_box.isChecked(): selected_data.append(check_box.text()) if selected_data: self.line_edit.setText(', '.join(selected_data)) else: self.line_edit.clear() self.updated.emit() def addItem(self, text, user_data=None): # user_data is unused list_widget_item = QListWidgetItem(self.list_widget) check_box = QCheckBox(self) check_box.setText(text) self.list_widget.addItem(list_widget_item) self.list_widget.setItemWidget(list_widget_item, check_box) check_box.stateChanged.connect(self.stateChanged) def currentText(self): if self.line_edit.text(): return [_.strip() for _ in self.line_edit.text().split(",")] return [] def addItems(self, texts): for s in texts: self.addItem(s) def count(self): return max(0, self.list_widget.count() - 1) # Do not count the search bar def onSearch(self, s): for i in range(self.list_widget.count()): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) if s.lower() in check_box.text().lower(): self.list_widget.item(i).setHidden(False) else: self.list_widget.item(i).setHidden(True) def itemClicked(self, index): if index != self.search_bar_index: check_box = self.list_widget.itemWidget( self.list_widget.item(index)) check_box.setChecked(not check_box.isChecked()) def setSearchBarPlaceholderText(self, placeholder_text): self.search_bar.setPlaceholderText(placeholder_text) def setPlaceholderText(self, placeholder_text): self.line_edit.setPlaceholderText(placeholder_text) def clear(self): self.list_widget.clear() current_item = QListWidgetItem(self.list_widget) self.search_bar = QLineEdit(self) self.search_bar.setPlaceholderText("Search...") self.search_bar.setClearButtonEnabled(True) self.list_widget.addItem(current_item) self.list_widget.setItemWidget(current_item, self.search_bar) self.search_bar.textChanged.connect(self.onSearch) def wheelEvent(self, wheel_event): pass # Do not handle the wheel event def setCurrentText(self, text): pass def setCurrentTexts(self, texts): count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) check_box_string = check_box.text() if check_box_string in texts: check_box.setChecked(True) def ResetSelection(self): count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) check_box.setChecked(False)
class RmExplorerWindow(QMainWindow): def __init__(self): super().__init__() self.settings = Settings() self.updateFromSettings() self.statusBar() self.makeMenus() self.dirsList = QListWidget(self) self.dirsList.setSelectionMode(QAbstractItemView.ExtendedSelection) self.dirsList.itemDoubleClicked.connect(self.dirsListItemDoubleClicked) self.dirsList.setContextMenuPolicy(Qt.CustomContextMenu) self.dirsList.customContextMenuRequested.connect(self.dirsListContextMenuRequested) self.filesList = QListWidget(self) self.filesList.setSelectionMode(QAbstractItemView.ExtendedSelection) self.filesList.itemDoubleClicked.connect(self.filesListItemDoubleClicked) self.filesList.setContextMenuPolicy(Qt.CustomContextMenu) self.filesList.customContextMenuRequested.connect(self.filesListContextMenuRequested) self.curDirLabel = QLabel(self) browserLayout = QGridLayout() browserLayout.addWidget(QLabel('Folders:'), 0, 0) browserLayout.addWidget(QLabel('Files:'), 0, 1) browserLayout.addWidget(self.dirsList, 1, 0) browserLayout.addWidget(self.filesList, 1, 1) mainLayout = QVBoxLayout() mainLayout.addLayout(browserLayout) mainLayout.addWidget(self.curDirLabel) centralWidget = QWidget(self) centralWidget.setLayout(mainLayout) self.setCentralWidget(centralWidget) self.curDir = '' self.curDirName = '' self.curDirParents = [] self.curDirParentsNames = [] self.dirIds = [] self.dirNames = [] self.fileIds = [] self.goToDir('', '') self.currentWarning = '' self.hasRaised = None self.progressWindow = None self.downloadFilesWorker = None self.uploadDocsWorker = None self.backupDocsWorker = None self.restoreDocsWorker = None self.taskThread = None self._masterKey = None self.setWindowTitle(constants.AppName) ################### # General methods # ################### def updateFromSettings(self): """Call this whenever settings are changed""" socket.setdefaulttimeout(self.settings.value('HTTPShortTimeout', type=float)) def makeMenus(self): menubar = self.menuBar() # Explorer menu uploadDocsAct = QAction('&Upload documents', self) uploadDocsAct.setShortcut('Ctrl+U') uploadDocsAct.setStatusTip('Upload documents from the computer to the tablet.') uploadDocsAct.triggered.connect(self.uploadDocs) # dlAllAct = QAction('&Download all', self) dlAllAct.setShortcut('Ctrl+D') dlAllAct.setStatusTip('Download all files to a local folder.') dlAllAct.triggered.connect(self.downloadAll) # refreshAct = QAction('&Refresh', self) refreshAct.setShortcut('Ctrl+R') refreshAct.setStatusTip('Refresh folders and files lists.') refreshAct.triggered.connect(self.refreshLists) # settingsAct = QAction('&Settings', self) settingsAct.setShortcut('Ctrl+S') settingsAct.setStatusTip('%s settings' % constants.AppName) settingsAct.triggered.connect(self.editSettings) # exitAct = QAction('&Exit', self) exitAct.setShortcut('Ctrl+Q') exitAct.setStatusTip('Exit %s.' % constants.AppName) exitAct.triggered.connect(qApp.quit) # explorerMenu = menubar.addMenu('&Explorer') explorerMenu.addAction(uploadDocsAct) explorerMenu.addAction(dlAllAct) explorerMenu.addAction(refreshAct) explorerMenu.addSeparator() explorerMenu.addAction(settingsAct) explorerMenu.addSeparator() explorerMenu.addAction(exitAct) # SSH menu backupDocsAct = QAction('&Backup documents', self) backupDocsAct.setStatusTip('Backup all notebooks, documents, ebooks and bookmarks to a folder on this computer.') backupDocsAct.triggered.connect(self.backupDocs) # restoreDocsAct = QAction('&Restore documents', self) restoreDocsAct.setStatusTip('Restore documents on the tablet from a backup on this computer.') restoreDocsAct.triggered.connect(self.restoreDocs) # sshMenu = menubar.addMenu('&SSH') sshMenu.addAction(backupDocsAct) sshMenu.addAction(restoreDocsAct) # About menu aboutAct = QAction(constants.AppName, self) aboutAct.setStatusTip("Show %s's About box." % constants.AppName) aboutAct.triggered.connect(self.about) # aboutQtAct = QAction('Qt', self) aboutQtAct.setStatusTip("Show Qt's About box.") aboutQtAct.triggered.connect(qApp.aboutQt) # explorerMenu = menubar.addMenu('&About') explorerMenu.addAction(aboutAct) explorerMenu.addAction(aboutQtAct) # Context menu of the directories QListWidget self.dirsListContextMenu = QMenu(self) downloadDirsAct = self.dirsListContextMenu.addAction('&Download') downloadDirsAct.triggered.connect(self.downloadDirsClicked) # Context menu of the files QListWidget self.filesListContextMenu = QMenu(self) downloadFilesAct = self.filesListContextMenu.addAction('&Download') downloadFilesAct.triggered.connect(self.downloadFilesClicked) def goToDir(self, dirId, dirName): try: collections, docs = tools.listDir(dirId, self.settings) except (urllib.error.URLError, socket.timeout) as e: msg = getattr(e, 'reason', 'timeout') QMessageBox.critical(self, constants.AppName, 'Could not go to directory "%s": URL error:\n%s' % (dirId, msg)) return if dirId != self.curDir: # We are either moving up or down one level if len(self.curDirParents) == 0 or self.curDirParents[-1] != dirId: # Moving down self.curDirParents.append(self.curDir) self.curDirParentsNames.append(self.curDirName) else: # Moving up self.curDirParents.pop() self.curDirParentsNames.pop() self.curDir = dirId self.curDirName = dirName if self.curDirParents: path = '%s/%s' % ('/'.join(self.curDirParentsNames), self.curDirName) else: path = '/' self.curDirLabel.setText(path) # Update dirsList and filesList self.dirsList.clear() self.filesList.clear() if dirId != '': self.dirIds = [self.curDirParents[-1]] self.dirNames = [self.curDirParentsNames[-1]] self.dirsList.addItem('..') else: self.dirIds = [] self.dirNames = [] self.fileIds = [] for id_, name in collections: self.dirIds.append(id_) self.dirNames.append(name) self.dirsList.addItem(name) for id_, name in docs: self.fileIds.append(id_) self.filesList.addItem(name) def downloadFile(self, basePath, fileDesc, mode): if not os.path.isdir(basePath): raise OSError('Not a directory: %s' % basePath) fid, destRelPath = fileDesc self.statusBar().showMessage('Downloading %s...' % os.path.split(destRelPath)[1]) try: tools.downloadFile(fid, basePath, destRelPath, mode, self.settings) except (urllib.error.URLError, socket.timeout) as e: msg = getattr(e, 'reason', 'timeout') QMessageBox.error(self, constants.AppName, 'URL error: %s. Aborted.' % msg) self.statusBar().showMessage('Download error.', constants.StatusBarMsgDisplayDuration) else: self.statusBar().showMessage('Download finished.', constants.StatusBarMsgDisplayDuration) def downloadDirs(self, dirs): def listFiles(ext, baseFolderId, baseFolderPath, filesList): url = self.settings.value('listFolderURL', type=str) % baseFolderId try: res = urllib.request.urlopen(url) data = res.read().decode(constants.HttpJsonEncoding) except (urllib.error.URLError, socket.timeout) as e: warningBox = QMessageBox(self) msg = getattr(e, 'reason', 'timeout') warningBox.setText('URL error: %s. Aborted.' % msg) warningBox.setIcon(QMessageBox.Warning) warningBox.exec() self.statusBar().showMessage('Download error.', constants.StatusBarMsgDisplayDuration) return data = json.loads(data) for elem in data: if elem['Type'] == 'DocumentType': path = '%s.%s' % (os.path.join(baseFolderPath, elem['VissibleName']), ext) # yes, "Vissible" filesList.append((elem['ID'], path)) elif elem['Type'] == 'CollectionType': listFiles(ext, elem['ID'], os.path.join(baseFolderPath, elem['VissibleName']), filesList) dialog = SaveOptsDialog(self.settings, self) if dialog.exec() == QDialog.Accepted: mode = dialog.getSaveMode() ext = mode # Ask for destination folder folder = QFileDialog.getExistingDirectory(self, 'Save directory', self.settings.value('lastDir', type=str), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if folder: self.settings.setValue('lastDir', os.path.split(folder)[0]) # Construct files list dlList = [] for dir_id, dir_name in dirs: listFiles(ext, dir_id, dir_name, dlList) self.progressWindow = ProgressWindow(self) self.progressWindow.setWindowTitle("Downloading...") self.progressWindow.nSteps = len(dlList) self.progressWindow.open() self.settings.sync() self.currentWarning = '' self.downloadFilesWorker = DownloadFilesWorker(folder, dlList, mode) self.taskThread = QThread() self.downloadFilesWorker.moveToThread(self.taskThread) self.taskThread.started.connect(self.downloadFilesWorker.start) self.downloadFilesWorker.notifyProgress.connect(self.progressWindow.updateStep) self.downloadFilesWorker.finished.connect(self.onDownloadFilesFinished) self.downloadFilesWorker.warning.connect(self.warningRaised) self.taskThread.start() else: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) def downloadFiles(self, files): dialog = SaveOptsDialog(self.settings, self) if dialog.exec() == QDialog.Accepted: mode = dialog.getSaveMode() ext = mode # Ask for destination folder folder = QFileDialog.getExistingDirectory(self, 'Save directory', self.settings.value('lastDir', type=str), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if folder: self.settings.setValue('lastDir', os.path.split(folder)[0]) # Construct files list dlList = tuple((id_, os.path.join(folder, '%s.%s' % (name, ext))) for id_, name in files) self.progressWindow = ProgressWindow(self) self.progressWindow.setWindowTitle("Downloading...") self.progressWindow.nSteps = len(dlList) self.progressWindow.open() self.settings.sync() self.currentWarning = '' self.downloadFilesWorker = DownloadFilesWorker(folder, dlList, mode) self.taskThread = QThread() self.downloadFilesWorker.moveToThread(self.taskThread) self.taskThread.started.connect(self.downloadFilesWorker.start) self.downloadFilesWorker.notifyProgress.connect(self.progressWindow.updateStep) self.downloadFilesWorker.finished.connect(self.onDownloadFilesFinished) self.downloadFilesWorker.warning.connect(self.warningRaised) self.taskThread.start() else: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) def backupDocs(self): # Destination folder defaultDir = (self.settings.value('lastSSHBackupDir', type=str) or self.settings.value('lastDir', type=str)) folder = QFileDialog.getExistingDirectory(self, 'Save directory', defaultDir, QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if not folder: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return self.settings.setValue('lastSSHBackupDir', os.path.split(folder)[0]) if not self.settings.unlockMasterKeyInteractive(self): self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return self.progressWindow = ProgressWindow(self) self.progressWindow.setWindowTitle("Downloading backup...") self.progressWindow.open() self.settings.sync() self.currentWarning = '' self.backupDocsWorker = BackupDocsWorker(folder, self.settings._masterKey) self.taskThread = QThread() self.backupDocsWorker.moveToThread(self.taskThread) self.taskThread.started.connect(self.backupDocsWorker.start) self.backupDocsWorker.notifyNSteps.connect(self.progressWindow.updateNSteps) self.backupDocsWorker.notifyProgress.connect(self.progressWindow.updateStep) self.backupDocsWorker.finished.connect(self.onBackupDocsFinished) self.backupDocsWorker.warning.connect(self.warningRaised) self.taskThread.start() def restoreDocs(self): # Confirm user has a backup folder tabletDir = self.settings.value('TabletDocumentsDir') msg = "To restore a backup, you need a previous copy on your computer of the tablet's \"%s\" folder. Ensure the backup you select was made with a tablet having the same software version as the device on which you want to restore the files.\n\n" % tabletDir msg += "Do you have such a backup and want to proceed to the restoration?" reply = QMessageBox.question(self, constants.AppName, msg) if reply == QMessageBox.No: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return # Source folder defaultDir = (self.settings.value('lastSSHBackupDir', type=str) or self.settings.value('lastDir', type=str)) folder = QFileDialog.getExistingDirectory(self, 'Backup directory', defaultDir, QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if not folder: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return self.settings.setValue('lastSSHBackupDir', os.path.split(folder)[0]) # Basic check that the folder contents looks like a backup success, msg = tools.isValidBackupDir(folder) if not success: QMessageBox.warning(self, constants.AppName, '%s\nAborting.' % msg) self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return if not self.settings.unlockMasterKeyInteractive(self): self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return # Last chance to cancel! msg = "%s is now ready to restore the documents. Please check that the tablet is turned on, unlocked and that Wifi is enabled. Make sure no file is open and do not use the tablet during the upload.\n\n" % constants.AppName msg += "When the upload finishes, please reboot the tablet.\n\n" msg += "To restore documents, contents on the tablet will first be deleted. By continuing, you acknowledge that you take the sole responsibility for any possible data loss or damage caused to the tablet that may result from using %s.\n\n" % constants.AppName msg += "Do you want to continue?" reply = QMessageBox.question(self, constants.AppName, msg) if reply == QMessageBox.No: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return self.progressWindow = ProgressWindow(self) self.progressWindow.setWindowTitle("Restoring backup...") self.progressWindow.open() self.settings.sync() self.hasRaised = False self.restoreDocsWorker = RestoreDocsWorker(folder, self.settings._masterKey) self.taskThread = QThread() self.restoreDocsWorker.moveToThread(self.taskThread) self.taskThread.started.connect(self.restoreDocsWorker.start) self.restoreDocsWorker.notifyNSteps.connect(self.progressWindow.updateNSteps) self.restoreDocsWorker.notifyProgress.connect(self.progressWindow.updateStep) self.restoreDocsWorker.finished.connect(self.onRestoreDocsFinished) self.restoreDocsWorker.error.connect(self.errorRaised) self.taskThread.start() ######### # Slots # ######### def refreshLists(self): self.goToDir(self.curDir, self.curDirName) def dirsListItemDoubleClicked(self, item): idx = self.dirsList.currentRow() self.goToDir(self.dirIds[idx], self.dirNames[idx]) def dirsListContextMenuRequested(self, pos): if len(self.dirsList.selectedItems()) > 0: self.dirsListContextMenu.exec(self.dirsList.mapToGlobal(pos)) def filesListContextMenuRequested(self, pos): if len(self.filesList.selectedItems()) > 0: self.filesListContextMenu.exec(self.filesList.mapToGlobal(pos)) def filesListItemDoubleClicked(self, item): fid = self.fileIds[self.filesList.currentRow()] dialog = SaveOptsDialog(self.settings, self) if dialog.exec() == QDialog.Accepted: mode = dialog.getSaveMode() ext = mode filename = '%s.%s' % (item.text(), ext) # Ask for file destination result = QFileDialog.getSaveFileName(self, 'Save %s' % ext.upper(), os.path.join(self.settings.value('lastDir', type=str), filename), '%s file (*.%s)' % (ext.upper(), ext)) if result[0]: dest_path = result[0] if result[0].endswith('.%s' % ext) else '%s.%s' % (result[0], ext) parts = os.path.split(dest_path) self.settings.setValue('lastDir', parts[0]) self.downloadFile(parts[0], (fid, parts[1]), ext) else: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) else: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) def downloadFilesClicked(self): items = self.filesList.selectionModel().selectedIndexes() files = tuple((self.fileIds[i.row()], self.filesList.item(i.row()).text()) for i in items) self.downloadFiles(files) def downloadDirsClicked(self): items = self.dirsList.selectionModel().selectedIndexes() dirs = tuple((self.dirIds[i.row()], self.dirNames[i.row()]) for i in items) self.downloadDirs(dirs) def downloadAll(self): self.downloadDirs((('', ''),)) def uploadDocs(self): defaultDir = (self.settings.value('lastDir', type=str)) paths = QFileDialog.getOpenFileNames(self, 'Select files to upload', defaultDir, 'Documents (*.pdf *.epub)')[0] nFiles = len(paths) if nFiles == 0: self.statusBar().showMessage('Cancelled.', constants.StatusBarMsgDisplayDuration) return self.settings.setValue('lastDir', os.path.split(paths[0])[0]) self.progressWindow = ProgressWindow(self) self.progressWindow.setWindowTitle("Uploading documents...") self.progressWindow.nSteps = nFiles self.progressWindow.open() self.settings.sync() self.currentWarning = '' self.uploadDocsWorker = UploadDocsWorker(paths) self.taskThread = QThread() self.uploadDocsWorker.moveToThread(self.taskThread) self.taskThread.started.connect(self.uploadDocsWorker.start) self.uploadDocsWorker.notifyNSteps.connect(self.progressWindow.updateNSteps) self.uploadDocsWorker.notifyProgress.connect(self.progressWindow.updateStep) self.uploadDocsWorker.finished.connect(self.onUploadDocsFinished) self.uploadDocsWorker.warning.connect(self.warningRaised) self.taskThread.start() def warningRaised(self, msg): self.currentWarning = msg def errorRaised(self, msg): self.hasRaised = True QMessageBox.critical(self, constants.AppName, 'Error:\n%s\nAborted.' % msg) def onDownloadFilesFinished(self): self.progressWindow.hide() self.taskThread.started.disconnect(self.downloadFilesWorker.start) self.downloadFilesWorker.notifyProgress.disconnect(self.progressWindow.updateStep) self.downloadFilesWorker.warning.disconnect(self.warningRaised) self.downloadFilesWorker.finished.disconnect(self.onDownloadFilesFinished) self.progressWindow.deleteLater() # Not sure that the following is entirely safe. For example, what if a # new thread is created before the old objects are actually deleted? self.taskThread.quit() self.downloadFilesWorker.deleteLater() self.taskThread.deleteLater() self.taskThread.wait() if self.currentWarning: QMessageBox.warning(self, constants.AppName, 'Errors were encountered:\n%s' % self.currentWarning) self.statusBar().showMessage('Finished downloading files.', constants.StatusBarMsgDisplayDuration) def onUploadDocsFinished(self): self.progressWindow.hide() self.taskThread.started.disconnect(self.uploadDocsWorker.start) self.uploadDocsWorker.notifyNSteps.disconnect(self.progressWindow.updateNSteps) self.uploadDocsWorker.notifyProgress.disconnect(self.progressWindow.updateStep) self.uploadDocsWorker.warning.disconnect(self.warningRaised) self.uploadDocsWorker.finished.disconnect(self.onUploadDocsFinished) self.progressWindow.deleteLater() # Not sure that the following is entirely safe. For example, what if a # new thread is created before the old objects are actually deleted? self.taskThread.quit() self.uploadDocsWorker.deleteLater() self.taskThread.deleteLater() self.taskThread.wait() if self.currentWarning: QMessageBox.warning(self, constants.AppName, 'Errors were encountered:\n%s' % self.currentWarning) self.refreshLists() self.statusBar().showMessage('Finished uploading files.', constants.StatusBarMsgDisplayDuration) def editSettings(self): dialog = SettingsDialog(self.settings, self) if dialog.exec() == QDialog.Accepted: self.updateFromSettings() self.statusBar().showMessage('Settings updated.', constants.StatusBarMsgDisplayDuration) def onBackupDocsFinished(self): self.progressWindow.hide() self.taskThread.started.disconnect(self.backupDocsWorker.start) self.backupDocsWorker.warning.disconnect(self.warningRaised) self.backupDocsWorker.finished.disconnect(self.onBackupDocsFinished) self.backupDocsWorker.notifyNSteps.disconnect(self.progressWindow.updateNSteps) self.backupDocsWorker.notifyProgress.disconnect(self.progressWindow.updateStep) self.progressWindow.deleteLater() self.taskThread.quit() self.backupDocsWorker.deleteLater() self.taskThread.deleteLater() self.taskThread.wait() if self.currentWarning: QMessageBox.warning(self, constants.AppName, 'Errors were encountered:\n%s' % self.currentWarning) else: QMessageBox.information(self, constants.AppName, 'Backup was created successfully!') self.statusBar().showMessage('Finished downloading backup.', constants.StatusBarMsgDisplayDuration) def onRestoreDocsFinished(self): self.progressWindow.hide() self.taskThread.started.disconnect(self.restoreDocsWorker.start) self.restoreDocsWorker.error.disconnect(self.errorRaised) self.restoreDocsWorker.finished.disconnect(self.onRestoreDocsFinished) self.restoreDocsWorker.notifyNSteps.disconnect(self.progressWindow.updateNSteps) self.restoreDocsWorker.notifyProgress.disconnect(self.progressWindow.updateStep) self.progressWindow.deleteLater() self.taskThread.quit() self.restoreDocsWorker.deleteLater() self.taskThread.deleteLater() self.taskThread.wait() if not self.hasRaised: QMessageBox.information(self, constants.AppName, 'Backup was restored successfully! Please reboot the tablet now.') self.statusBar().showMessage('Finished restoring backup.', constants.StatusBarMsgDisplayDuration) def about(self): msg = """<b>pyrmexplorer: Explorer for Remarkable tablets</b><br/><br/> Version %s<br/><br/> Copyright (C) 2019 Nicolas Bruot (<a href="https://www.bruot.org/hp/">https://www.bruot.org/hp/</a>)<br/><br/> Some parts of this software are copyright other contributors. Refer to the individual source files for details.<br/><br/> pyrmexplorer is released under the terms of the GNU General Public License (GPL) v3.<br/><br/> The source code is available at <a href=\"https://github.com/bruot/pyrmexplorer/\">https://github.com/bruot/pyrmexplorer/</a>.<br/><br/> """ msg = msg % __version__ msgBox = QMessageBox(self) msgBox.setText(msg) msgBox.exec()
class Example(QWidget): def __init__(self): super().__init__() self.filenames = json_files() if type(self.filenames) is list: self.curr_file = self.filenames[0] else: self.curr_file = self.filenames self.initUI() def initUI(self): self.num = -1 # index for search bar query self.show_save = False # bool for showing unsaved changes dialog self.temp_file = ".temp.csv" self.refresh_file = False # failsafe 1 for itemChanged trigger self.list_1 = QListWidget() lister(file=self.curr_file, target=self.list_1, index=0, mode=0) self.list_1.clicked.connect(self.clear_selection) self.list_1.installEventFilter(self) self.list_items = self.list_1.count( ) # failsafe 2 for itemChanged trigger self.list_1.itemChanged.connect(self.edit_next_item) self.list_1.verticalScrollBar().valueChanged.connect(self.sync_scroll) self.list_2 = QListWidget() lister(file=self.curr_file, target=self.list_2, index=1, mode=0) self.list_2.clicked.connect(self.clear_selection) self.list_3 = QListWidget() lister(file=self.curr_file, target=self.list_3, index=2, mode=0) self.list_3.clicked.connect(self.clear_selection) self.all_lists = [self.list_1, self.list_2, self.list_3] self.menubar = QMenuBar() self.menubar.setNativeMenuBar(False) exit_event = QAction('Exit', self) exit_event.setShortcut('Ctrl+W') exit_event.triggered.connect(app.quit) showAct = QAction('Show extras', self, checkable=True) showAct.setChecked(False) showAct.setShortcut('Ctrl+E') showAct.triggered.connect(self.hide_notes) addAct = QAction('Fields', self) addAct.setShortcut('Ctrl+N') addAct.triggered.connect(self.add_item) fileOpen = QAction('Open file', self) fileOpen.triggered.connect(self.fileDialog) fileOpen.setShortcut('Ctrl+O') fileSave = QAction('Save file', self) fileSave.triggered.connect(self.save) fileSave.triggered.connect(self.refresh_recents) fileSave.setShortcut('Ctrl+S') self.fileRecents = QMenu('Recent file', self) self.refresh_recents() self.toggle_theme = QAction('Toggle theme', self, checkable=True) self.toggle_theme.setChecked(json_theme()) self.toggle_theme.triggered.connect(self.theme) self.toggle_theme.setShortcut('Ctrl+T') self.col_sort_index = QMenu('Sorting column index', self) self.col_sort_index.addAction(QAction(str(0), self)) self.col_sort_index.addAction(QAction(str(1), self)) self.col_sort_index.addAction(QAction(str(2), self)) self.col_sort_index.triggered.connect(self.sort_col_choice) self.col_search_index = QMenu('Searching column index', self) self.col_search_index.addAction(QAction(str(0), self)) self.col_search_index.addAction(QAction(str(1), self)) self.col_search_index.addAction(QAction(str(2), self)) self.col_search_index.triggered.connect(self.search_col_choice) self.sort = QAction('Sort entries', self, checkable=True) self.curr_col = 0 self.search_col = 0 self.sort.triggered.connect(self.refresh_list) self.sort.setShortcut('Ctrl+R') self.addFields = self.menubar.addMenu('Add') self.addFields.addAction(addAct) self.optionMenu = self.menubar.addMenu('Options') self.optionMenu.addAction(exit_event) self.optionMenu.addAction(showAct) self.optionMenu.addAction(self.toggle_theme) self.optionMenu.addMenu(self.col_sort_index) self.optionMenu.addMenu(self.col_search_index) self.optionMenu.addAction(self.sort) self.fileMenu = self.menubar.addMenu('File') self.fileMenu.addAction(fileOpen) self.fileMenu.addAction(fileSave) self.fileMenu.addMenu(self.fileRecents) self.search_bar = QLineEdit() self.search_bar.setPlaceholderText('Search vocab') self.search_bar.setClearButtonEnabled(True) self.search_bar.setMaxLength(10) self.search_bar.returnPressed.connect(self.search_item) self.status_bar = QStatusBar() status(self.status_bar, self.list_1) grid = QGridLayout() grid.setSpacing(10) grid.addWidget(self.menubar, 0, 0) grid.addWidget(self.list_1, 1, 0) grid.addWidget(self.list_2, 1, 1) grid.addWidget(self.list_3, 1, 2) grid.addWidget(self.search_bar, 0, 1) grid.addWidget(self.status_bar) self.theme() self.setLayout(grid) self.setGeometry(*json_window_size()) self.setWindowTitle(f'{split_name(self.curr_file)}') self.show() self.list_1.scrollToBottom() self.list_2.verticalScrollBar().setHidden(True) self.list_3.verticalScrollBar().setHidden(True) self.list_3.setHidden(True) def sync_scroll(self): scroll_location = self.list_1.verticalScrollBar().value() self.list_2.verticalScrollBar().setValue(scroll_location) self.list_3.verticalScrollBar().setValue(scroll_location) def edit_next_item(self, event): """When an item is added and edited on the first col, starts editing its counterpart on the next col""" if self.list_items == self.list_1.count( ) - 2 or self.list_items != self.list_1.count( ) and self.refresh_file == False: item = self.list_2.item(self.list_2.count() - 1) self.list_2.editItem(item) self.list_items = self.list_1.count() def closeEvent(self, event): """Triggered upon program exit, shows a dialog for unsaved changes using a bool""" if self.show_save == True: reply = QMessageBox.question( self, 'Message', "You may have unsaved changes, are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: try: remove(self.temp_file) except: pass event.accept() else: event.ignore() else: pass def sort_col_choice(self, action): self.curr_col = int(action.text()) def search_col_choice(self, action): self.search_col = int(action.text()) def refresh_list(self): """Refreshes the contents of the lists, when sorting is used""" self.save( mode=1 ) # saves a temp copy, with changes, but irreversable sorting introduced clear_lists(self.all_lists) if self.sort.isChecked() == True: mode = 2 else: mode = 0 try: lister(file=self.temp_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) except: lister(file=self.curr_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) def refresh_recents(self): try: file_1 = QAction(self.curr_file, self) self.fileRecents.addAction(file_1) file_1.triggered.connect(self.clickedFileAct) if type(self.filenames) is list: if self.filenames[1] != None: file_2 = QAction(self.filenames[1], self) self.fileRecents.addAction(file_2) file_2.triggered.connect(self.clickedFileAct) if self.filenames[2] != None: file_3 = QAction(self.filenames[2], self) self.fileRecents.addAction(file_3) file_3.triggered.connect(self.clickedFileAct) except: pass def clickedFileAct(self): self.refresh_file = True file = self.sender().text() self.curr_file = file self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() self.list_1.scrollToBottom() self.list_3.setHidden(True) self.refresh_file = False def eventFilter(self, source, event): """Item (row) deletion""" if (event.type() == QEvent.ContextMenu and source is self.list_1): menu = QMenu() menu.addAction("Delete row") if menu.exec_(event.globalPos()): item = source.itemAt(event.pos()) try: model = self.list_1.indexFromItem(item) row = model.row() self.show_save = True self.list_1.takeItem(row) self.list_2.takeItem(row) self.list_3.takeItem(row) status(self.status_bar, self.list_1, f'Deleted row number: {row+1}.') self.clearSelection() except: pass return True return super(Example, self).eventFilter(source, event) def hide_notes(self): """Toggles showing the note column and stretches the window for clearer reading of it""" self.list_3.setHidden(not self.list_3.isHidden()) def theme(self): """Sets the theme for the window and its widgets""" palette = QPalette() # dark theme if self.toggle_theme.isChecked() == True: palette.setColor(QPalette.Window, QColor(0, 0, 0)) dark = "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);" self.menubar.setStyleSheet(dark) self.addFields.setStyleSheet(dark) self.optionMenu.setStyleSheet(dark) self.fileMenu.setStyleSheet(dark) self.search_bar.setStyleSheet( "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255)" ) # border: 0px; for transparency self.status_bar.setStyleSheet(dark) style_items(self.all_lists, dark_theme=True) # light theme elif self.toggle_theme.isChecked() == False: palette.setColor(QPalette.Window, QColor(255, 255, 255)) light = "background-color: rgb(255, 255, 255); color: rgb(0, 0, 0)" self.menubar.setStyleSheet(light) self.addFields.setStyleSheet(light) self.optionMenu.setStyleSheet(light) self.fileMenu.setStyleSheet(light) self.search_bar.setStyleSheet(light) self.status_bar.setStyleSheet(light) style_items(self.all_lists, dark_theme=False) self.setPalette(palette) self.theme_bool = self.toggle_theme.isChecked( ) # used in the save func def search_item(self): """Takes input from the search bar and matches with an item, gets index and scrolls to it, more reusults being qued with the num class var """ query = self.search_bar.text() search = self.all_lists[self.search_col].findItems( query, Qt.MatchContains) status(self.status_bar, self.list_1, f'Found {len(search)} results.') self.clear_selection() # testing search in all column # search_list =[] # for x in range(3): # search_list.append(self.all_lists[x].findItems(query, Qt.MatchContains)) # parent_list = [] # for x in range(3): # for y in range(len(search_list[x])): # parent_list.append(self.all_lists[x]) # replace with x # import itertools # merged = list(itertools.chain.from_iterable(search_list)) # search_dict = dict(zip(parent_list, merged)) # print(search_dict) # print() # print(len(merged)) # print(len(parent_list)) self.num += 1 for i in search: try: model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) except: self.num = 0 model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) item_index = model_index.row() self.all_lists[self.search_col].item(item_index).setSelected(True) self.list_1.scrollToItem(self.list_1.item(item_index), QAbstractItemView.PositionAtCenter) def add_item(self): self.show_save = True for x in range(3): if x == 0: lister(file=self.curr_file, target=self.list_1, index=x, mode=1) elif x == 1: lister(file=self.curr_file, target=self.list_2, index=x, mode=1) elif x == 2: lister(file=self.curr_file, target=self.list_3, index=x, mode=1) item = self.list_1.item(self.list_1.count() - 1) self.list_1.editItem(item) status(self.status_bar, self.list_1) self.list_1.scrollToBottom() self.list_2.scrollToBottom() self.list_3.scrollToBottom() def clear_selection(self): """Clears all item slections for aesthetical purposes, but only single clicks""" self.list_1.clearSelection() self.list_2.clearSelection() self.list_3.clearSelection() def fileDialog(self): fname = QFileDialog() path = fname.getOpenFileName(self, 'Open file', getcwd(), filter='csv (*.csv);;') if path[0] == '': # failsafe for canceling the dialog return self.curr_file self.curr_file = path[0] self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() def save(self, mode=0): self.show_save = False list1_items = items_text(self.list_1) list2_items = items_text(self.list_2) list3_items = items_text(self.list_3) total_dicts = [] for (a, b, c) in zip(list1_items, list2_items, list3_items): # each letter is a column dictionary = {'word_1': a, 'word_2': b, 'notes': c} total_dicts.append(dictionary) if mode == 0: writer(file=self.curr_file, data=total_dicts) status(self.status_bar, self.list_1, ('Saved current changes.')) try: json_template(theme=self.theme_bool, files=[self.curr_file, None, None], window_size=self.geometry().getRect() ) # current size values of the window except: json_template( ) # bug cannot be avoided, even though used setChecked at the beggining elif mode == 1: self.show_save = True writer(file=self.temp_file, data=total_dicts) # avoids stacking and refreshes recent file actions actions = self.fileRecents.actions() for action in actions: self.fileRecents.removeAction(action)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self._suggester = suggester.Suggester() self._pickedfiles = [] self._targetmidi = [] self.inputlistview = None self.suggestionlistview = None self.target_lbl = None self.initUI() def initUI(self): targetButton = QPushButton('Select Target', self) targetButton.setToolTip('Select midi you want suggestions for') targetButton.resize(targetButton.sizeHint()) targetButton.clicked.connect(self.picktargetmidi) self.inputlistview = QListWidget() self.inputlistview.itemPressed.connect(self.showmidi) inputlistview_lbl = QLabel(self) inputlistview_lbl.setText("Input MIDIs") inputlistview_lbl.adjustSize() suggestionlistview_lbl = QLabel(self) suggestionlistview_lbl.setText("Suggested segments") suggestionlistview_lbl.adjustSize() self.target_lbl = QLabel(self) self.target_lbl.setText("Please select target midi") self.target_lbl.adjustSize() self.suggestionlistview = QListWidget() suggestButton = QPushButton("Suggest", self) suggestButton.setToolTip('Suggest a continuation to target midi') suggestButton.clicked.connect(self.suggestmidi) saveButton = QPushButton("Save Suggestions", self) importAction = QAction(self.tr('Add MIDI file(s)'), self) importAction.setShortcut('Ctrl+O') importAction.setStatusTip('Add MIDI files to pool') importAction.triggered.connect(self.pickfiles) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.target_lbl) hbox.addWidget(targetButton) hbox.addWidget(suggestButton) hbox.addWidget(saveButton) vbox = QVBoxLayout() vbox.addStretch(1) vbox.addWidget(inputlistview_lbl) vbox.addWidget(self.inputlistview) vbox.addLayout(hbox) vbox.addWidget(suggestionlistview_lbl) vbox.addWidget(self.suggestionlistview) cwidget = QWidget() cwidget.setLayout(vbox) self.setCentralWidget(cwidget) self.statusBar().showMessage('Ready') self.statusBar() menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(importAction) self.center() self.setGeometry(150,20,100,100) self.setWindowTitle('Gnotestic') self.show() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def pickfiles(self, point): dialog_result = QFileDialog.getOpenFileNames(self, self.tr("Import Midi"), "", self.tr("Midi Files (*.mid *.midi)")) self._pickedfiles.extend(dialog_result[0]) self.updatemidilist(dialog_result[0]) def picktargetmidi(self, point): dialog_result = QFileDialog.getOpenFileName(self, self.tr("Select Target Midi"), "", self.tr("Midi Files (*.mid *.midi)")) self._targetmidi = dialog_result[0] def updatemidilist(self, items): for item in items: witem = QListWidgetItem(item) self.inputlistview.addItem(witem) def showmidi(self, item): path = item.text() ms = converter.parse(path) ms.plot('pianoroll') def suggestmidi(self, point): #add midis to suggester if self._targetmidi and self.inputlistview.count(): self._suggester.set_target_piece(self._targetmidi) for i in range(self.inputlistview.count()): self._suggester.add_midi(self.inputlistview.item(i).text()) #update suggestion list self.suggestionlistview.clear() for suggestion in self._suggester.get_suggestions(): self.suggestionlistview.addItem(QListWidgetItem(repr(suggestion)))
class NameMan(QWidget): def __init__(self, parent=None): super(NameMan, self).__init__(parent) self.createTop() self.createInfo() mainLayout = QVBoxLayout() mainLayout.addLayout(self.searchLayout) mainLayout.addWidget(self.infoBox) self.setFixedSize(440, 260) self.setLayout(mainLayout) def createTop(self): self.searchLayout = QHBoxLayout() self.searchLine = QLineEdit() search = QPushButton("搜索") search.clicked.connect(self.updateInfoList) self.searchLayout.addWidget(self.searchLine) self.searchLayout.addWidget(search) def updateShow(self): pass def createInfo(self): self.infoBox = QGroupBox("匹配到的域文件") self.infoBox.setFixedSize(430, 200) self.listWidget = QListWidget() layout = QVBoxLayout() self.ok = QPushButton("修改") self.cancel = QPushButton("删除") self.ok.setEnabled(False) self.cancel.setEnabled(False) self.ok.clicked.connect(self.modifi) self.cancel.clicked.connect(self.delete) button = QHBoxLayout() button.addWidget(self.ok) button.addWidget(self.cancel) layout.addWidget(self.listWidget) layout.addLayout(button) self.infoBox.setLayout(layout) def updateInfoList(self): self.listWidget.clear() par = self.searchLine.text() if not par: QMessageBox.about(self, "Error", "没有检索内容!!!") return view = [ i for i in os.listdir("/var/cache/bind/") if os.path.isdir("/var/cache/bind/" + i) ] self.result = [] for i in view: lis = os.listdir("/var/cache/bind/" + i) ret = ["%s-->%s" % (i, a) for a in lis if par in a] self.result.extend(ret) if not self.result: QMessageBox.about(self, "Warn", "没有你想要检索的内容!!!") self.checkButton() return self.addListItem() self.selected = self.listWidget.item(0).text() self.listWidget.currentItemChanged.connect(self.getSelected) #print(self.selected) self.checkButton() def checkButton(self): if not self.result: self.ok.setEnabled(False) self.cancel.setEnabled(False) else: self.ok.setEnabled(True) self.cancel.setEnabled(True) def addListItem(self): lis = self.result for i in lis: item = QListWidgetItem(self.listWidget) item.setText(i) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) def getSelected(self, current, previous): if not current: current = previous self.selected = self.listWidget.item( self.listWidget.row(current)).text() print(self.selected) def modifi(self): flag = self.selected.split("-->") path = "/var/cache/bind/" + flag[0] + "/" + flag[1] self.notepad = Notepad() self.notepad.openFile(path) # self.notepad.show() def delete(self): flag = self.selected.split("-->") conf = "/etc/bind/named.conf." + flag[0] + "-views" path1 = "/var/cache/bind/" + flag[0] + "/" + flag[1] path2 = "/var/cache/bind/" + flag[1] print(conf) psutil.os.popen("rm -f " + path1) psutil.os.popen("rm -f " + path2) psutil.os.popen('sed -i "/' + self.searchLine.text() + '\"/,/\b;};/d" ' + conf) n1 = self.result self.updateInfoList() n2 = self.result if n1 != n2: QMessageBox.about(self, "Succree", "删除文件成功!!!") else: QMessageBox.about(self, "Fail", "发生未知错误导致删除文件失败!!!")
class EgimDownloaderFrame(QFrame): """Frame to download data from EMSODEV servers""" # Signals msg2Statusbar = pyqtSignal(str) wf2plotSplitter = pyqtSignal(WaterFrame) class DownloadParameterThread(QThread): """ The process to download data from the API is very slow. We are going to use this thread to download data without block the app. """ def __init__(self, downloader): QThread.__init__(self) self.downloader = downloader def __del__(self): self.wait() def run(self): """Thread main function""" if self.downloader.instrument_list.currentItem().text() \ == "icListen-1636": date = datetime.datetime.strptime( self.downloader.date_list.currentItem().text(), "%Y-%m-%d").strftime("%d/%m/%Y") self.downloader.download_acoustic( date, self.downloader.hour_minute_list.currentItem().text()) else: parameters = [ item.text() for item in self.downloader.parameter_list.selectedItems() ] for parameter in parameters: self.downloader.download_parameter(parameter) def __init__(self): super().__init__() # Instance variables self.downloader = EGIM() self.wf = WaterFrame() # pylint: disable=C0103 self.metadata = dict() self.dates = [] self.my_thread = None # Save the login of the EMSODEV API self.downloader.login = "******" self.downloader.password = "" self.init_ui() def init_ui(self): """Layout and main functionalities""" # Buttons download_button = QPushButton("Download", self) download_button.clicked.connect(self.download_click) download_button.setEnabled(False) close_button = QPushButton("Close", self) close_button.clicked.connect(self.hide) # Lists self.egim_list = QListWidget(self) self.egim_list.itemClicked.connect(self.load_instruments) self.egim_list.setMaximumWidth(200) self.instrument_list = QListWidget(self) self.instrument_list.itemClicked.connect(self.load_parameters) self.instrument_list.setMaximumWidth(290) self.metadata_list = QListWidget(self) self.parameter_list = QListWidget(self) self.parameter_list.setSelectionMode( QAbstractItemView.ExtendedSelection) self.parameter_list.itemClicked.connect( lambda: download_button.setEnabled(True)) self.date_list = QListWidget(self) self.date_list.itemClicked.connect(self.load_times) self.date_list.setMaximumWidth(150) self.hour_minute_list = QListWidget(self) self.hour_minute_list.itemClicked.connect( lambda: download_button.setEnabled(True)) self.hour_minute_list.setMaximumWidth(150) # Labels egim_label = QLabel("EGIM", self) instrument_label = QLabel("Instrument", self) metadata_label = QLabel("Metadata", self) parameter_label = QLabel("Parameter", self) start_date_label = QLabel("Start date", self) end_date_label = QLabel("End date", self) limit_label = QLabel("Get last X values", self) hour_label = QLabel("Hour and minute (HHmm)", self) date_label = QLabel("Available dates", self) # Date edit self.start_date_edit = QDateEdit(self) self.start_date_edit.setCalendarPopup(True) self.start_date_edit.setDateTime( QDateTime(QDate(2017, 1, 27), QTime(0, 0, 0))) self.start_date_edit.setMinimumDateTime( QDateTime(QDate(2017, 1, 27), QTime(0, 0, 0))) self.end_date_edit = QDateEdit(self) self.end_date_edit.setCalendarPopup(True) self.end_date_edit.setDateTime( QDateTime(QDate(2017, 1, 27), QTime(0, 0, 0))) self.end_date_edit.setMinimumDateTime( QDateTime(QDate(2017, 1, 27), QTime(0, 0, 0))) # Spin box self.limit_spin_box = QSpinBox(self) self.limit_spin_box.setMinimum(0) self.limit_spin_box.setMaximum(9999999999) self.limit_spin_box.setSingleStep(100) self.limit_spin_box.valueChanged.connect(self.enable_date) # Custom Widgets # Widget for dates of the acoustic data self.acoustic_date_widget = QWidget(self) # - Layout v_acoustic_date = QVBoxLayout() v_acoustic_date.addWidget(date_label) v_acoustic_date.addWidget(self.date_list) v_acoustic_date.addWidget(hour_label) v_acoustic_date.addWidget(self.hour_minute_list) self.acoustic_date_widget.setLayout(v_acoustic_date) self.acoustic_date_widget.setMaximumWidth(175) self.acoustic_date_widget.setEnabled(False) # Widget for dates of parameters self.parameter_date_widget = QWidget(self) # - Layout v_parameter_date = QVBoxLayout() v_parameter_date.addWidget(start_date_label) v_parameter_date.addWidget(self.start_date_edit) v_parameter_date.addWidget(end_date_label) v_parameter_date.addWidget(self.end_date_edit) v_parameter_date.addWidget(limit_label) v_parameter_date.addWidget(self.limit_spin_box) v_parameter_date.addStretch() self.parameter_date_widget.setLayout(v_parameter_date) self.parameter_date_widget.setEnabled(False) # Layout # - Vertical layout for EGIM -- v_egim = QVBoxLayout() v_egim.addWidget(egim_label) v_egim.addWidget(self.egim_list) # -- Vertical layout for instruments - v_instrument = QVBoxLayout() v_instrument.addWidget(instrument_label) v_instrument.addWidget(self.instrument_list) # - Vertical layout for parameters - v_parameter = QVBoxLayout() v_parameter.addWidget(metadata_label) v_parameter.addWidget(self.metadata_list) v_parameter.addWidget(parameter_label) v_parameter.addWidget(self.parameter_list) # - Vertical layout for dates and buttons v_button = QVBoxLayout() v_button.addWidget(download_button) v_button.addWidget(close_button) v_button.addStretch() # - Layout of the frame - h_frame = QHBoxLayout() h_frame.addLayout(v_egim) h_frame.addLayout(v_instrument) h_frame.addLayout(v_parameter) h_frame.addWidget(self.parameter_date_widget) h_frame.addWidget(self.acoustic_date_widget) h_frame.addLayout(v_button) self.setLayout(h_frame) def load_observatories(self): """ It asks for the available EGIM observatories and write its names into self.egim_list """ debug = True # For print debug info if debug: print("- In EgimDownloaderFrame.load_observatory() -") # Send a message for the statusbar self.msg2Statusbar.emit("Loading observatories") # Clear self.egim_list self.egim_list.clear() # Ask for the observatories code, observatory_list = self.downloader.observatories() if debug: print("code:", code) print("observatory_list:", observatory_list) if code: if code == 200: # It means that you are going good self.egim_list.addItems(observatory_list) # Send a message for the statusbar self.msg2Statusbar.emit("Ready") elif code == 401: if debug: print( "msg2Statusbar: Unauthorized to use the EMSODEV DMP API", code) self.msg2Statusbar.emit( "Unauthorized to use the EMSODEV DMP API") self.downloader.password = None self.reload() elif code == 404: self.msg2Statusbar.emit("Not Found") elif code == 403: self.msg2Statusbar.emit("Forbidden") elif code == 500: self.msg2Statusbar.emit("EMSODEV API internal error") elif code == 504: self.msg2Statusbar.emit("EMSODEV DMP error: Gateway Time Out") else: self.msg2Statusbar.emit("Unknown EMSODEV DMP API error") else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") def load_instruments(self, observatory): """ It asks for the available instruments and write its names into self.instrument_list Parameters ---------- observatory: item item from self.observatory_list """ # Send a message for the statusbar self.msg2Statusbar.emit("Loading instruments") # Clear self.instrument_list self.instrument_list.clear() # Ask for instruments code, instrument_list_ = self.downloader.instruments( observatory.text()) if code: if code == 200: # It means that you are going good # Obtain all sensor names of instrument_list_ sensor_type = [ instrument['name'] for instrument in instrument_list_ ] self.instrument_list.addItems(sensor_type) # Add tooltip for i in range(self.instrument_list.count()): self.instrument_list.item(i).setToolTip( '<p><b>Sensor Type</b><br>' + '{}</p><p>'.format(instrument_list_[i]['sensorType']) + '<b>Long Name</b><br>' + '{}</p>'.format( instrument_list_[i]['sensorLongName']) + '<p></p><p><b>S/N</b><br>' + '{}</p>'.format(instrument_list_[i]['sn'])) # Send a message for the statusbar self.msg2Statusbar.emit("Ready") elif code == 401: self.msg2Statusbar.emit( "Unauthorized to use the EMSODEV DMP API") self.downloader.password = None self.reload() elif code == 404: self.msg2Statusbar.emit("Not Found") elif code == 403: self.msg2Statusbar.emit("Forbidden") elif code == 500: self.msg2Statusbar.emit("EMSODEV API internal error") else: self.msg2Statusbar.emit("Unknown EMSODEV DMP API error") else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") def load_parameters(self, instrument): """ It asks for the available parameters and metadata and write them into self.parameter_list and self.metadata_list """ # Send a message for the statusbar self.msg2Statusbar.emit("Loading parameters") # Clear self.parameter_list and self.metadata_list self.parameter_list.clear() self.metadata_list.clear() self.parameter_date_widget.setEnabled(False) self.acoustic_date_widget.setEnabled(False) # If instrument is an icListener, check times if instrument.text() == "icListen-1636": self.acoustic_date_widget.setEnabled(True) # Ask for dates code, self.dates = self.downloader.acoustic_date( self.egim_list.currentItem().text(), instrument.text()) if code == 200: date_list = [ date['acousticObservationDate'] for date in self.dates ] self.date_list.addItems(date_list) else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") return return self.parameter_date_widget.setEnabled(True) # Ask for metadata code, self.metadata = self.downloader.metadata( self.egim_list.currentItem().text(), instrument.text()) if code == 200: items = [] for key, value in self.metadata.items(): items.append("{}: {}".format(key, value)) self.metadata_list.addItems(items) else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") return # Ask for parameters code, parameter_list_ = self.downloader.parameters( self.egim_list.currentItem().text(), instrument.text()) if code: if code == 200: # It means that you are going good # Obtain all parameter names of parameter_list_ names = [parameter['name'] for parameter in parameter_list_] self.parameter_list.addItems(names) self.parameter_list.sortItems() # Add tooltip for i in range(self.parameter_list.count()): self.parameter_list.item(i).setToolTip( '<b>Units:</b> {}'.format(parameter_list_[i]['uom'])) # Send a message for the statusbar self.msg2Statusbar.emit("Ready") elif code == 401: self.msg2Statusbar.emit( "Unauthorized to use the EMSODEV DMP API") self.downloader.password = None self.reload() elif code == 404: self.msg2Statusbar.emit("Not Found") elif code == 403: self.msg2Statusbar.emit("Forbidden") elif code == 500: self.msg2Statusbar.emit("EMSODEV API internal error") else: self.msg2Statusbar.emit("Unknown EMSODEV DMP API error") else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") def load_times(self, date_item): """ Write items into self.hour_minute_list QListWidget """ for date in self.dates: if date['acousticObservationDate'] == date_item.text(): time_list = [] for time in date['observationsHourMinuteList']: time_list.append(time['acousticObservationHourMinute']) self.hour_minute_list.addItems(time_list) def reload(self): """It clear all lists and load again the observatories.""" debug = True if debug: print("- In EgimDownloaderFrame.reload() -") print("self.downloader.password:"******"Password is required to download data from EMSODEV") # pylint: disable=C0103 text, ok = QInputDialog.getText(None, "Attention", "Password", QLineEdit.Password) if debug: print("Request for password") print("text:", text) print("ok:", ok) if ok: self.downloader.password = text else: return self.load_observatories() def download_click(self): """Function when user click download""" self.my_thread = self.DownloadParameterThread(self) self.my_thread.start() def download_parameter(self, parameter): """It download data with the observation function of EGIM""" # Send a message for the statusbar self.msg2Statusbar.emit("Downloading {}".format(parameter)) code, df = self.downloader.observation( # pylint: disable=C0103 observatory=self.egim_list.currentItem().text(), instrument=self.instrument_list.currentItem().text(), parameter=parameter, startDate=self.start_date_edit.text(), endDate=self.end_date_edit.text(), limit=self.limit_spin_box.text()) if code: if code == 200: self.msg2Statusbar.emit("Waterframe creation") # It means that you are going good # pylint: disable=C0103 wf = self.downloader.to_waterframe(data=df, metadata=self.metadata) # print(wf.data.head()) # Send a signal with the new WaterFrame self.wf2plotSplitter.emit(wf) self.msg2Statusbar.emit("Ready") elif code == 401: self.msg2Statusbar.emit( "Unauthorized to use the EMSODEV DMP API") self.downloader.password = None self.reload() elif code == 404: self.msg2Statusbar.emit("Not Found") elif code == 403: self.msg2Statusbar.emit("Forbidden") elif code == 500: self.msg2Statusbar.emit("EMSODEV API internal error") else: self.msg2Statusbar.emit("Unknown EMSODEV DMP API error") else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") def download_acoustic(self, date, time): """Download acoustic data from EMSO""" # Send a message for the statusbar self.msg2Statusbar.emit("Downloading acoustic file from {}, {}".format( date, time)) code, df, metadata = self.downloader.acoustic_observation( # pylint: disable=C0103 observatory=self.egim_list.currentItem().text(), instrument=self.instrument_list.currentItem().text(), date=date, hour_minute=time) if code: if code == 200: self.msg2Statusbar.emit("Waterframe creation") # It means that you are going good # pylint: disable=C0103 wf = self.downloader.to_waterframe(data=df, metadata=metadata) # Send a signal with the new WaterFrame self.wf2plotSplitter.emit(wf) self.msg2Statusbar.emit("Ready") elif code == 401: self.msg2Statusbar.emit( "Unauthorized to use the EMSODEV DMP API") self.downloader.password = None self.reload() elif code == 404: self.msg2Statusbar.emit("Not Found") elif code == 403: self.msg2Statusbar.emit("Forbidden") elif code == 500: self.msg2Statusbar.emit("EMSODEV API internal error") else: self.msg2Statusbar.emit("Unknown EMSODEV DMP API error") else: self.msg2Statusbar.emit( "Impossible to connect to the EMSODEV DMP API") def enable_date(self): """Enable or disable date elements""" if int(self.limit_spin_box.text()) > 0: self.start_date_edit.setEnabled(False) self.end_date_edit.setEnabled(False) else: self.start_date_edit.setEnabled(True) self.end_date_edit.setEnabled(True)
class MainWindow(QMainWindow): """This is the main window of the ERCreator app. Args: manager (ERmanager): The class which handles organization of the Extra reflectance database locally an online.""" def __init__(self, manager: ERManager): super().__init__() self.explorerWindow = manager.createManagerWindow(self) self.setWindowTitle("Extra Reflectance Creator") self.setWindowIcon(QtGui.QIcon(os.path.join(resources, 'cellLogo.png'))) widg = QWidget() layout = QGridLayout() self.listWidg = QListWidget(self) self.selListWidg = QListWidget(self) self.binningCombo = QComboBox() self.binningCombo.addItems(['Auto', '1x1', '2x2', '3x3']) self.parallelCheckBox = QCheckBox("Parallel Process", self) self.parallelCheckBox.setToolTip( "Uses significantly more ram but may be significantly faster") self.compareDatesButton = QPushButton("Compare Dates") self.plotButton = QPushButton("Plot Details") self.saveButton = QPushButton("Save Checked Dates") self.deleteFigsButton = QPushButton("Close Figures") self.viewFilesButton = QPushButton("View Files") self.viewFilesButton.released.connect(self.viewFiles) self.numericalAperture = QDoubleSpinBox() self.numericalAperture.setRange(0, 2) self.numericalAperture.setSingleStep(0.01) self.numericalAperture.setValue(0.52) row = 0 layout.addWidget(self.listWidg, row, 0, 4, 4) layout.addWidget(self.selListWidg, row, 4, 4, 4) row += 4 layout.addWidget(self.compareDatesButton, row, 0, 1, 1) layout.addWidget(self.plotButton, row, 1, 1, 1) layout.addWidget(self.deleteFigsButton, row, 2, 1, 1) layout.addWidget(QLabel("Binning"), row, 4, 1, 1) layout.addWidget(self.binningCombo, row, 5, 1, 1) layout.addWidget(self.parallelCheckBox, row, 6, 1, 1) row += 1 layout.addWidget(self.saveButton, row, 0, 1, 1) layout.addWidget(self.viewFilesButton, row, 1, 1, 1) layout.addWidget(QLabel("NA"), row, 4, 1, 1) layout.addWidget(self.numericalAperture, row, 5, 1, 1) widg.setLayout(layout) self.setCentralWidget(widg) self.buttons = [ self.compareDatesButton, self.plotButton, self.saveButton ] self.show() @property def binning(self) -> int: num = self.binningCombo.currentIndex() return num if num != 0 else None @property def parallelProcessing(self) -> bool: return self.parallelCheckBox.isChecked() @property def checkedSettings(self): dateItems = [ self.selListWidg.item(i) for i in range(self.selListWidg.count()) ] return [i.text() for i in dateItems if i.checkState()] def setEnabled(self, en: bool): [ i.setEnabled(en) for i in [ self.binningCombo, self.saveButton, self.compareDatesButton, self.plotButton ] ] def viewFiles(self): self.explorerWindow.refresh() self.explorerWindow.show()
class cronotipy(QMainWindow, QDialog): def __init__(self): super(cronotipy, self).__init__() # Get Window attributes from ui file self.cronotipy_ui = Ui_cronotipy_mw() self.cronotipy_ui.setupUi(self) self.setWindowIcon(QIcon(path_logo)) cronotipy.initUI(self) #Another init, this makes code clean self.title_and_time = {} #get titles names and yours respectively time self.titles_typed = [] self.unfreeze = QGuiApplication.processEvents #unfreeze self.startcronotipy = True #bool to while in start (start/stop) self.isOnOff_Notify = True #state of notify checkable menu self.isOnOff_Sound = True #state of sound checkable menu self.isOnOff_loop = False def initUI(self): #Create the QListWidget self.listwd = QListWidget() self.listwd.setSelectionMode( QAbstractItemView.ExtendedSelection ) #This Method enable ctrl and click selection self.listwd.doubleClicked.connect(self.renameActualize) #Buttons self.cronotipy_ui.btn_create.clicked.connect(self.addLines) self.cronotipy_ui.btn_create.setStyleSheet(style_buttons) self.cronotipy_ui.btn_remove.clicked.connect(self.removeLines) self.cronotipy_ui.btn_remove.setStyleSheet(style_buttons) self.btn_start = QPushButton('Start') self.btn_start.setStyleSheet(style_buttons) self.btn_stop = QPushButton('Stop') self.btn_stop.setStyleSheet(style_buttons) self.btn_start.clicked.connect(self.start) self.btn_stop.clicked.connect(self.stop) #Create widget inside QDockWidget self.dock = cronotipy.createDockwidget(self) self.dock.layout().addWidget(self.cronotipy_ui.btn_create, alignment=Qt.AlignCenter) #align button self.dock.layout().addWidget(self.cronotipy_ui.btn_remove, alignment=Qt.AlignCenter) #align button self.dock.layout().addWidget(self.btn_start, alignment=Qt.AlignCenter) self.dock.layout().addWidget(self.btn_stop, alignment=Qt.AlignCenter) self.dock.layout().addWidget(self.listwd) #QList stay below buttons #QScrollBar self.vbox_top = QFormLayout() #vbox, but the true is formbox lol self.cronotipy_ui.scrollAWD_top.setLayout( self.vbox_top) #put the vbox layout inside scrollbars widget self.cronotipy_ui.scrollA_top.setStyleSheet(style_scrollbar) self.vbox_bot = QFormLayout() self.cronotipy_ui.scrollAWD_bot.setLayout( self.vbox_bot) #put the vbox layout inside scrollbars widget self.cronotipy_ui.scrollA_bot.setStyleSheet(style_scrollbar) #MenuBar self.cronotipy_ui.menu_Notify_On_Off.triggered.connect( self.turnOnOffnotify) self.cronotipy_ui.menu_Sound_On_Off.triggered.connect( self.turnOnOffsound) self.cronotipy_ui.menu_Loop_On_Off.triggered.connect( self.turnOnOffloop) self.cronotipy_ui.menu_Quit.triggered.connect(self.quitMenu) self.cronotipy_ui.menu_Save.triggered.connect(self.saveProfile) self.cronotipy_ui.menu_Open.triggered.connect(self.openProfile) def createDockwidget(self): #create widget Inside QDock self.layoutdock = QVBoxLayout() self.dockedwidget = QWidget() self.cronotipy_ui.dockwd.setWidget(self.dockedwidget) self.cronotipy_ui.dockwd.setStyleSheet(style) #change appearance self.dockedwidget.setLayout(self.layoutdock) return self.dockedwidget def start(self): self.startcronotipy = True #for while if self.cronotipy_ui.menu_Loop_On_Off.isChecked(): #for loop self.isOnOff_loop = True get_titles = [ self.listwd.item(item).text() for item in range(self.listwd.count()) ] title_message = {} for title in get_titles: title_message[title] = self.findChild(QLineEdit, title).text() #timeinseconds = cronotipy.time2seconds(self, self.title_and_time[title]) #print(timeinseconds) seconds_title = {} cnt = 0 cnt_finish = 0 while self.startcronotipy: #start counts cnt += 1 try: #try for when to delete the title at the time of counting for i, title in enumerate(get_titles): time = cronotipy.time2seconds( self, self.title_and_time[title]) #calc time in seconds if cnt == 1: #get dictionary just in first iteration seconds_title[time] = title self.findChild(QProgressBar, get_titles[i]).setMaximum( time) #set max value of progbar self.findChild(QProgressBar, get_titles[i]).setTextVisible(True) self.findChild(QProgressBar, get_titles[i]).setValue( cnt) #change value of progbar except KeyError: #QUANDO DELETAMOS UM TITULO ENQUANTO A CONTAGEM TA ROLANDO DA ESSE KEYERROR #ESSE TRY RESOLVE ISSO, MAS PRECISO TIRAR ESSE TITULO DO DICIONARIO PARA NAO APARECER NOTIFICAÇÃO pass if cnt in seconds_title.keys(): #when cnt pass through time if self.isOnOff_Notify: Notify.Notification.new(seconds_title[cnt], title_message[seconds_title[cnt]], path_logo).show() if self.isOnOff_Sound: subprocess.run('play {} &'.format(path_sound_notify), shell=True) cnt_finish += 1 elif cnt_finish == len( seconds_title): #when the last notify finish self.startcronotipy = False if self.isOnOff_Notify: Notify.Notification.new('Done!', 'All notifications are over.', path_logo).show() QTest.qWait(1000) #loop if self.isOnOff_loop: self.startcronotipy = True cronotipy.start(self) def stop(self): self.startcronotipy = False self.isOnOff_loop = False def renameActualize(self): #cronotipy.actualizeEditables(self) dialog = AddNotifiy() dialog.setWindowTitle('Titles Rename') dialog.setStyleSheet("background-color: rgba(84, 84, 84, 0.5);") state = dialog.exec_() time = [ dialog.spinbox_hours.value(), dialog.spinbox_minutes.value(), dialog.spinbox_seconds.value() ] get_title = dialog.edit.text() #get the typed #get_title = cronotipy._testTitle(self, title=get_title) if get_title == '' or state == 0: pass else: cronotipy.removeLines(self) self.listwd.addItem(get_title) #add a item in QList "title - left" cronotipy.addText2ScrollBar( self, title=get_title) #add a QLine in QScroll "message - right" cronotipy.addProgressbar2ScrollBar( self, title=get_title, time=time) #add a QProgressBar in QSCROLL bot self.title_and_time[ get_title] = time #update title and time in dictionary def addLines(self): get = AddNotifiy() #instancing dialog get.setWindowTitle('Add a notifier') get.setStyleSheet("background-color: rgba(84, 84, 84, 0.5);") state = get.exec_() # show dialog #get time from spinboxes time = [ get.spinbox_hours.value(), get.spinbox_minutes.value(), get.spinbox_seconds.value() ] get_title = get.edit.text() #get the typed self.titles_typed.append( get_title) #store titles to check equal titles get_title = cronotipy._testTitle(self, title=get_title) if get_title == '' or state == 0: pass else: self.listwd.addItem(get_title) #add a item in QList "title - left" cronotipy.addText2ScrollBar( self, title=get_title) #add a QLine in QScroll "message - right" cronotipy.addProgressbar2ScrollBar( self, title=get_title, time=time) #add a QProgressBar in QSCROLL bot self.title_and_time[get_title] = time def removeLines(self): getselected = self.listwd.selectedItems() #take the selected items for i in getselected: item2remove = self.listwd.takeItem(self.listwd.row(i)).text( ) #str, name of object selected is the same name of QLine ObjectName #child = self.findChild(QLineEdit, item2remove.text()).objectName() #this lines returns the objectName of QLine child = self.findChild(QLineEdit, item2remove).deleteLater( ) #remove the QLine with objectname "item2remove" self.findChild(QProgressBar, item2remove).deleteLater() self.findChild(QLabel, item2remove).deleteLater() try: #this is for rename and next remove item self.titles_typed.remove(item2remove) except: pass self.title_and_time.pop(item2remove) def addText2ScrollBar(self, title): #Add QLine in TOP SCROLLBAR line = QLineEdit(self) line.setPlaceholderText('Type a message for {}'.format(title)) line.setObjectName(title) self.vbox_top.addWidget(line) #put line object inside vbox def addProgressbar2ScrollBar(self, title, time=[]): #Add progressbar in BOT SCROLLBAR progressbar = QProgressBar(self) progressbar.setObjectName(title) progressbar.setValue(100) progressbar.setTextVisible(False) progressbar.setStyleSheet(style_progbar) progresslabel = QLabel( title + ' ({:02d}:{:02d}:{:02d}):'.format(time[0], time[1], time[2])) progresslabel.setObjectName(title) self.vbox_bot.addRow(progresslabel, progressbar) def time2seconds(self, time=[]): hour2seconds = time[0] * 3600 min2seconds = time[1] * 60 seconds = time[2] * 1 return hour2seconds + min2seconds + seconds def turnOnOffnotify(self): if self.cronotipy_ui.menu_Notify_On_Off.isChecked(): self.isOnOff_Notify = True if not self.cronotipy_ui.menu_Notify_On_Off.isChecked(): self.isOnOff_Notify = False def turnOnOffsound(self): if self.cronotipy_ui.menu_Sound_On_Off.isChecked(): self.isOnOff_Sound = True if not self.cronotipy_ui.menu_Sound_On_Off.isChecked(): self.isOnOff_Sound = False def turnOnOffloop(self): if self.cronotipy_ui.menu_Loop_On_Off.isChecked(): self.isOnOff_loop = True if not self.cronotipy_ui.menu_Loop_On_Off.isChecked(): self.isOnOff_loop = False print(self.cronotipy_ui.menu_Loop_On_Off.isChecked()) def quitMenu(self): cronotipy.stop(self) self.close() def closeEvent(self, event): reply = QMessageBox.question( self, 'Closing Window', 'Do you really want to close this application?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: cronotipy.stop(self) event.accept() else: event.ignore() def _testTitle(self, title): n_titles_contained = self.titles_typed.count( title) #number of times that title appears if n_titles_contained == 1: return title elif title in self.titles_typed: self.titles_typed.append(title + '{}'.format(n_titles_contained)) return title + '{}'.format(n_titles_contained) def openProfile(self): get_dir, _ = QFileDialog.getOpenFileName( self, 'Open Profile for Cronotipy', '', '(*.cnp) ;; all*') #open dir/namefile as a string with open(get_dir, 'r') as fileopen: get_filetext = fileopen.readlines() #print(get_filetext) time = [] for i in get_filetext: #each iterations have the three information title_time_text = i.split('-') print(type(title_time_text[1])) for t in title_time_text[1].split(','): #geting time time.append(int(t.replace('[', '').replace( ']', ''))) #formating time's list str to int and store cronotipy._addLinesFromOpen( self, title_time_text[0], time, title_time_text[2]) #Add this lines time = [] def _addLinesFromOpen(self, title, times, text): #get time from spinboxes time = times get_title = title #get the typed self.titles_typed.append( get_title) #store titles to check equal titles get_title = cronotipy._testTitle(self, title=get_title) if get_title == '': pass else: self.listwd.addItem(get_title) #add a item in QList "title - left" cronotipy.addText2ScrollBar( self, title=get_title) #add a QLine in QScroll "message - right" self.findChild(QLineEdit, get_title).setText(text) cronotipy.addProgressbar2ScrollBar( self, title=get_title, time=time) #add a QProgressBar in QSCROLL bot self.title_and_time[get_title] = time def saveProfile(self): get_dir, _ = QFileDialog.getSaveFileName( self, 'Save Profile', '') #open a Dir with files name if get_dir.endswith('.cnp'): with open(get_dir, 'w') as filesave: for key, value in self.title_and_time.items(): get_text = self.findChild(QLineEdit, key).text() filesave.write( str(key) + '-' + str(value) + '-' + get_text + '\n') else: with open(get_dir + '.cnp', 'w') as filesave: for key, value in self.title_and_time.items(): get_text = self.findChild(QLineEdit, key).text() filesave.write( str(key) + '-' + str(value) + '-' + get_text + '\n')
class EditUsersWindow(QWidget): def __init__(self, userDetails): super().__init__() self.setGeometry(200, 50, 500, 500) self.setWindowTitle("Edit User") self.userDetails = userDetails self.layouts() self.widgets() def layouts(self): self.mainLayout = QVBoxLayout() self.topLayout = QVBoxLayout() self.middleLayout = QHBoxLayout() self.topLayout.setContentsMargins(20, 20, 20, 20) self.bottomLayout = QHBoxLayout() self.text = QLabel('') self.progeesBar = QProgressBar() self.progeesBar.setHidden(True) self.submitBtn = QPushButton("Submit") self.submitBtn.clicked.connect(self.submitAction) self.cancelBtn = QPushButton("Cancel") self.cancelBtn.clicked.connect(self.cancelAction) self.okBtn = QPushButton("Ok") self.okBtn.clicked.connect(self.okAction) self.okBtn.setHidden(True) self.submitBtn.setFixedHeight(30) self.cancelBtn.setFixedHeight(30) self.okBtn.setFixedHeight(30) self.submitBtn.setStyleSheet( "color: #ecf0f1; background-color: #27ae60 ; border: 0px") self.okBtn.setStyleSheet( "color: #ecf0f1; background-color: #27ae60 ; border: 0px") self.cancelBtn.setStyleSheet( "color: #ecf0f1; background-color: #e74c3c; border: 0px") self.bottomLayout.addWidget(self.submitBtn) self.bottomLayout.addWidget(self.cancelBtn) self.bottomLayout.addWidget(self.okBtn) self.mainLayout.addLayout(self.topLayout) self.mainLayout.addStretch() self.mainLayout.addLayout(self.bottomLayout) self.setLayout(self.mainLayout) def widgets(self): self.form = QFormLayout() print(self.userDetails) self.username = QLineEdit(self.userDetails[0]) self.form.addRow(QLabel('Username :'******'User ID :'), self.id) self.primaryGroup = self.userDetails[2].split('(')[1].split(')')[0] self.priGroup = QLineEdit(self.primaryGroup) self.form.addRow(QLabel('Primary Group :'), self.priGroup) self.comment = QLineEdit(self.userDetails[4]) self.form.addRow(QLabel('Comment :'), self.comment) self.homeDir = QLineEdit(self.userDetails[5]) self.form.addRow(QLabel('Home Directory :'), self.homeDir) self.shell = QLineEdit(self.userDetails[6]) self.form.addRow(QLabel('Shell :'), self.shell) if self.userDetails[7] == 'never': self.expirationDate = QLineEdit() else: import dateutil.parser as parser self.expirationDate_adapted = datetime.strptime( self.userDetails[7], '%b %d, %Y').strftime('%Y-%m-%d') date = parser.parse(self.expirationDate_adapted) self.expirationDate = QLineEdit(date.isoformat().split('T')[0]) self.form.addRow(QLabel('Expiration Date :'), self.expirationDate) self.groupsBtns = QVBoxLayout() self.lineEditAddGroup = QLineEdit() self.lineEditAddGroup.setPlaceholderText('enter group name') self.addGroupBtn = QPushButton('Add') self.addGroupBtn.clicked.connect(self.addGroup) self.deleteGroupBtn = QPushButton('Delete') self.deleteGroupBtn.clicked.connect(self.deleteGroup) self.deleteAllGroupsBtn = QPushButton('Delete All') self.deleteAllGroupsBtn.clicked.connect(self.deleteAllGroups) self.groupsBtns.addWidget(self.lineEditAddGroup) self.groupsBtns.addWidget(self.addGroupBtn) self.groupsBtns.addWidget(self.deleteGroupBtn) self.groupsBtns.addWidget(self.deleteAllGroupsBtn) self.groupsBtns.addStretch() self.listGroups = QListWidget() self.form.addRow(QLabel('Groups :'), self.middleLayout) groups = self.userDetails[3].split(',') for group in groups: grp = group.split('(')[1].split(')')[0] if grp == self.primaryGroup: continue else: self.listGroups.addItem(grp) self.middleLayout.addWidget(self.listGroups) self.middleLayout.addLayout(self.groupsBtns) self.topLayout.addLayout(self.form) self.topLayout.addWidget(self.text) self.topLayout.addWidget(self.progeesBar) def addGroup(self): group = self.lineEditAddGroup.text() if group == "": pass else: self.listGroups.addItem(group) def deleteGroup(self): listGroups = self.listGroups.selectedItems() if not listGroups: return for group in listGroups: self.listGroups.takeItem(self.listGroups.row(group)) def deleteAllGroups(self): self.listGroups.clear() def submitAction(self): try: self.setCursor(Qt.WaitCursor) self.progeesBar.setHidden(False) self.progeesBar.setMaximum(1) self.progeesBar.setValue(0) self.edituser() except subprocess.CalledProcessError: QMessageBox.warning(self, 'warning', f"error occured during editing this user\n") else: self.setCursor(Qt.ArrowCursor) self.submitBtn.setHidden(True) self.cancelBtn.setHidden(True) self.okBtn.setHidden(False) def okAction(self): self.close() def edituser(self): usernamee = self.username.text() idd = self.id.text() priGroupp = self.priGroup.text() commentt = self.comment.text() homeDirr = self.homeDir.text() shelll = self.shell.text() expirationDatee = self.expirationDate.text() txt = '' groupsitems = [] for index in range(self.listGroups.count()): groupsitems.append(str(self.listGroups.item(index).text())) groupsitemsstring = ",".join(groupsitems) print(groupsitemsstring) if expirationDatee == "never": QMessageBox.warning(self, 'expiration field error', "expiration field can't be 'never' ") return 0 elif expirationDatee == '': pass else: try: subprocess.run( f'usermod -e {expirationDatee} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing expiration date for this user\n" self.text.setText(txt) else: txt = txt + "expiration date edited succesfully\n" self.text.setText(txt) try: subprocess.run(f'usermod -g {priGroupp} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing primary group for this user\n" self.text.setText(txt) else: txt = txt + "primary group edited succesfully\n" self.text.setText(txt) try: subprocess.run( f'usermod -G {groupsitemsstring} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing supplementary groups for this user\n" self.text.setText(txt) else: txt = txt + "supplementary groups edited succesfully\n" self.text.setText(txt) try: subprocess.run(f'usermod -s {shelll} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing shell for this user\n" self.text.setText(txt) else: txt = txt + "shell edited succesfully\n" self.text.setText(txt) try: subprocess.run(f'usermod -d {homeDirr} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing home directory for this user\n" self.text.setText(txt) else: txt = txt + "home directory edited succesfully\n" self.text.setText(txt) try: subprocess.run(f"usermod -c '{commentt}' {self.userDetails[0]}", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing comment for this user\n" self.text.setText(txt) else: txt = txt + "comment edited succesfully\n" self.text.setText(txt) try: subprocess.run(f"usermod -u {idd} {self.userDetails[0]}", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing user id for this user\n" self.text.setText(txt) else: txt = txt + "user id edited succesfully\n" self.text.setText(txt) try: subprocess.run(f'usermod -l {usernamee} {self.userDetails[0]}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True) except subprocess.CalledProcessError: txt = txt + "error occured during editing username for this user\n" self.text.setText(txt) else: txt = txt + "username edited succesfully\n" self.text.setText(txt) self.progeesBar.setValue(1) def cancelAction(self): self.close()
class ExtensionsSettingsPanel(SettingsPanel): def __init__(self, parent=None): super(ExtensionsSettingsPanel, self).__init__(parent) self.thread = ExtensionsUpdateThread(self) self.thread.finished.connect(self.notifyFinish) self.thread.finished.connect(self.loadSettings) # List row listRow = custom_widgets.Row(self) self.layout().addWidget(listRow) # Extensions whitelist. whitelistColumn = custom_widgets.Column(self) listRow.addWidget(whitelistColumn) whitelistColumn.addWidget(QLabel(tr("Enabled extensions:"), self)) self.whitelist = QListWidget(self) self.whitelist.currentTextChanged.connect(self.changeAboutText) self.whitelist.itemActivated.connect(self.disableExtension) whitelistColumn.addWidget(self.whitelist) # Extensions blacklist. blacklistColumn = custom_widgets.Column(self) listRow.addWidget(blacklistColumn) blacklistColumn.addWidget(QLabel(tr("Disabled extensions:"), self)) self.blacklist = QListWidget(self) self.blacklist.currentTextChanged.connect(self.changeAboutText) self.blacklist.itemActivated.connect(self.enableExtension) blacklistColumn.addWidget(self.blacklist) # About text self.aboutText = custom_widgets.ReadOnlyTextEdit(self) self.aboutText.setMaximumHeight(92) self.layout().addWidget(self.aboutText) updateExtensionsButton = QPushButton(tr("&Update extensions"), self) updateExtensionsButton.clicked.connect(self.updateExtensions) self.layout().addWidget(updateExtensionsButton) def changeAboutText(self, name): aboutpath = os.path.join(settings.extensions_folder, name, "about.txt") try: f = open(aboutpath, "r") except: self.aboutText.setText(tr("This extension has no description.")) return aboutText = f.read().replace("\n", "") f.close() self.aboutText.setText(aboutText) def disableExtension(self, item): name = item.text() self.blacklist.addItem(name) self.blacklist.sortItems(Qt.AscendingOrder) self.whitelist.takeItem(self.whitelist.row(item)) self.whitelist.sortItems(Qt.AscendingOrder) def enableExtension(self, item): name = item.text() self.whitelist.addItem(name) self.whitelist.sortItems(Qt.AscendingOrder) self.blacklist.takeItem(self.blacklist.row(item)) self.blacklist.sortItems(Qt.AscendingOrder) def notifyFinish(self): common.trayIcon.showMessage(tr("Extensions updated"), tr("All extensions are up to date.")) def updateExtensions(self): if not self.thread.isRunning(): common.trayIcon.showMessage(tr("Updating extensions"), tr("This may take some time.")) self.thread.start() else: common.trayIcon.dontBeImpatient() def loadSettings(self): settings.reload_extensions() self.whitelist.clear() for extension in settings.extensions_whitelist: self.whitelist.addItem(extension) self.blacklist.clear() for extension in settings.extensions_blacklist: self.blacklist.addItem(extension) self.whitelist.sortItems(Qt.AscendingOrder) self.blacklist.sortItems(Qt.AscendingOrder) def saveSettings(self): settings.settings.setValue("extensions/Whitelist", json.dumps([self.whitelist.item(extension).text() for extension in range(0, self.whitelist.count())])) settings.reload_extensions() settings.settings.sync()
class GstPipeEdit(QWidget): def __init__(self, pipe, app_mode=False, **kwargs): super().__init__(**kwargs) self.setLayout(QGridLayout()) self.layout().setAlignment(Qt.AlignTop) self._app_mode = app_mode # Input selection self.inputBox = QComboBox(self) self.layout().addWidget(self.inputBox, 0, 0, 1, 3) self.__init_inputs() # Current plugins list self.currentList = QListWidget(self) self.currentList.setDragEnabled(True) self.currentList.setDragDropMode(QAbstractItemView.InternalMove) self.layout().addWidget(self.currentList, 1, 0) # Available plugins list self.availableList = QListWidget(self) self.layout().addWidget(self.availableList, 1, 2) # Output selection self.outputBox = QComboBox(self) self.layout().addWidget(self.outputBox, 4, 0, 1, 3) self.__init_outputs() # Add/Remove plugins buttons self.buttonsLayout = QVBoxLayout() self.layout().addLayout(self.buttonsLayout, 1, 1) self.layout().setAlignment(self.buttonsLayout, Qt.AlignHCenter) self.addButton = QPushButton(self) self.addButton.setIcon(QIcon.fromTheme('go-previous')) self.addButton.clicked.connect(self.__add_plugin) self.buttonsLayout.addWidget(self.addButton) self.buttonsLayout.setAlignment(self.addButton, Qt.AlignHCenter) self.delButton = QPushButton(self) self.delButton.setIcon(QIcon.fromTheme('go-next')) self.delButton.clicked.connect(self.__remove_plugin) self.buttonsLayout.addWidget(self.delButton) self.buttonsLayout.setAlignment(self.delButton, Qt.AlignHCenter) # Load the pipeline self.set_pipe(pipe) def set_pipe(self, pipe): if pipe: if not self._app_mode: inputs = sorted(elements.inputs()) self.inputBox.setCurrentIndex(inputs.index(pipe[0])) outputs = sorted(elements.outputs()) self.outputBox.setCurrentIndex(outputs.index(pipe[-1])) self.__init_current_plugins(pipe) self.__init_available_plugins(pipe) def get_pipe(self): pipe = [] if self._app_mode else [self.inputBox.currentText()] for n in range(self.currentList.count()): pipe.append(self.currentList.item(n).text()) pipe.append(self.outputBox.currentText()) return tuple(pipe) def __init_inputs(self): if self._app_mode: self.inputBox.setEnabled(False) else: inputs = sorted(elements.inputs()) self.inputBox.addItems(inputs) self.inputBox.setEnabled(len(inputs) > 1) def __init_outputs(self): outputs = sorted(elements.outputs()) self.outputBox.addItems(outputs) self.outputBox.setEnabled(len(outputs) > 1) def __init_current_plugins(self, pipe): self.currentList.clear() start = 0 if self._app_mode else 1 for plugin in pipe[start:-1]: self.currentList.addItem(plugin) def __init_available_plugins(self, pipe): self.availableList.clear() for plugin in elements.plugins().values(): if plugin.Name not in pipe: self.availableList.addItem(plugin.Name) def __add_plugin(self): item = self.availableList.takeItem(self.availableList.currentRow()) self.currentList.addItem(item) def __remove_plugin(self): item = self.currentList.takeItem(self.currentList.currentRow()) self.availableList.addItem(item)
class Form(QWidget): def __init__(self, parent=None): super(Form, self).__init__(parent) # Set up the first list (elements that can be used) elements = QListWidget() elements.viewport().setAcceptDrops(True) elements.setDragEnabled(True) elements.setDefaultDropAction(Qt.MoveAction) elements.setSelectionMode(QAbstractItemView.ExtendedSelection) elements.addItem(u"Last Name") elements.addItem(u"First Name") elements.addItem(u"Middle Initial") elements.addItem(u"Full Name") elements.addItem(u"DMIS ID") elements.addItem(u"SPIN Number") elements.addItem(u"SSN Prefix") elements.addItem(u"Social Security Number") elements.addItem(u"Date of Birth") elements.addItem(u"Gender") elements.addItem(u"Accession") elements.addItem(u"Isolation Date") elements.addItem(u"Culture Type") elements.addItem(u"Source") elements.addItem(u"Location Type") elements.addItem(u"Location") elements.addItem(u"Isolate Number") elements.addItem(u"Organism Name") elements.addItem(u"Alternate Organism Name") elements.addItem(u"Equipment") elements.addItem(u"Drug Info") elements.addItem(u"ESBL") elements.addItem(u"AMPC") # Set up the second list (elements to generate a config file from) self.selected_elements = QListWidget() self.selected_elements.viewport().setAcceptDrops(True) self.selected_elements.setDragEnabled(True) self.selected_elements.setDefaultDropAction(Qt.MoveAction) self.selected_elements.setSelectionMode(QAbstractItemView.ExtendedSelection) ok_button = QPushButton(u"OK") cancel_button = QPushButton(u"Cancel") about_button = QPushButton(u"About") insert_blank_line_button = QPushButton(u"Insert blank line") drug_validator = QIntValidator(1, 999) self.drugs = QLineEdit("1") self.drugs.setValidator(drug_validator) drugs_label = QLabel("Number of drugs per line") self.drugformat = QLineEdit("MIC,Call") drugformat_label = QLabel("Format of the drug information") self.date = QLineEdit("MM/dd/yyyy") date_label = QLabel("Date format") self.machine = QLineEdit("Machine name") machine_label = QLabel("Machine the file is from") buttons = QHBoxLayout() ok_button.clicked.connect(self.export_parser) cancel_button.clicked.connect(self.close_program) about_button.clicked.connect(self.about) insert_blank_line_button.clicked.connect(self.insert_blank_line) self.drugs.setMaxLength(3) buttons.addWidget(ok_button) buttons.addWidget(cancel_button) buttons.addWidget(about_button) buttons.addWidget(insert_blank_line_button) mainLayout = QGridLayout() mainLayout.addWidget(elements, 0, 0) mainLayout.addWidget(self.selected_elements, 0, 1) mainLayout.addLayout(buttons, 1, 0) mainLayout.addWidget(self.drugs, 2, 0) mainLayout.addWidget(drugs_label, 2, 1) mainLayout.addWidget(self.drugformat, 3, 0) mainLayout.addWidget(drugformat_label, 3, 1) mainLayout.addWidget(self.date, 4, 0) mainLayout.addWidget(date_label, 4, 1) mainLayout.addWidget(self.machine, 5, 0) mainLayout.addWidget(machine_label, 5, 1) self.setLayout(mainLayout) self.setWindowTitle(u"RevealerParserWizard") def export_parser(self): u''' Extract the text of the elements in selected_elements, then pass them to write_output so they can be fully converted and written to a parser file. ''' extracted_elements = [] element_num = self.selected_elements.count() if element_num < 1: no_entries = QMessageBox() no_entries.setIcon(QMessageBox.Warning) no_entries.setText(u"No elements selected!") no_entries.exec_() return for i in xrange(0, element_num): extracted_elements.append(self.selected_elements.item(i).text() + 1) # Act like a clown and get knocked down if int(self.drugs.text()) < 1: too_small = QMessageBox() too_small.setIcon(QMessageBox.Warning) too_small.setText(u"'Drugs per line' must be between 1 and 999") too_small.exec_() return else: extracted_elements.append("Drugs per line" + '\t' + self.drugs.text()) if (len(self.drugformat.text()) < 1): too_small = QMessageBox() too_small.setIcon(QMessageBox.Warning) too_small.setText(u"'Drug Info Format' must not be empty") too_small.exec_() return else: extracted_elements.append("Drug Info Format" + '\t' + self.drugformat.text()) extracted_elements.append("Date Format" + '\t' + self.date.text()) if os.path.isfile(u"my_parser.txt"): output_exists = QMessageBox() output_exists.setIcon(QMessageBox.Warning) output_exists.setText(u"my_parser.txt already exists! Overwrite?") output_exists.setStandardButtons(QMessageBox.Yes | QMessageBox.No) overwrite = output_exists.exec_() if overwrite == QMessageBox.Yes: with open(u"my_parser.txt", u'w') as output: self.write_output(output, extracted_elements) return else: sys.exit() else: with open(u"my_parser.txt", u'w') as output: self.write_output(output, extracted_elements) return def write_output(self, output, elements): u''' Given a List of strings, convert them into an MDRevealer custom parser file and write them to my_parser.txt. ''' i = 0 for e in elements: if not "Drugs per" in e: output.write(e + u'\t' + unicode(i) + u'\n') else: output.write(e + u'\n') i += 1 success = QMessageBox() success.setText(u"my_parser.txt written successfully.") success.exec_() sys.exit() def close_program(self): u''' Close the program. ''' sys.exit() def about(self): u''' Provide information about the program in a QMessageBox. ''' QMessageBox.information(self, u"About RevealerParserWizard", u"RevealerParserWizard 1.0b" + "\n" + u"A program to generate custom parser files for MDRevealer" + "\n" + u"Copyright (C) 2015 Sean Clifford" + "\n" + "Available under GPLv3+") def insert_blank_line(self): u''' Adds a blank line to selected_elements, for handling fields in the file to parse that Revealer doesn't read. ''' self.selected_elements.addItem(u"(skip)")
class HeaderEditDialog(QDialog): # name of the current preset, whether to set this preset as default, list of Columns header_changed = pyqtSignal(str, bool, list) def __init__(self, parent, table_header): super().__init__(parent) self.table_header = table_header self.default_preset_name = None self.preset_name = table_header.preset_name self.columns = copy.deepcopy(table_header.columns) self.setupUi() def setupUi(self): self.resize(200, 400) self.vbox = QVBoxLayout(self) self.presetLabel = QLabel("Preset: {}".format(self.preset_name), self) self.columnList = QListWidget(self) self.setAsDefaultCheckbox = QCheckBox("Set as default preset", self) self.vbox.addWidget(self.presetLabel) self.vbox.addWidget(self.columnList) self.vbox.addWidget(self.setAsDefaultCheckbox) self.columnList.setDragDropMode(QListWidget.InternalMove) self.columnList.setDefaultDropAction(Qt.MoveAction) self.columnList.setSelectionMode(QListWidget.ExtendedSelection) self.columnList.setAlternatingRowColors(True) self.columnList.installEventFilter(self) self.columnList.setContextMenuPolicy(Qt.CustomContextMenu) self.columnList.customContextMenuRequested.connect(self.open_menu) self.columnList.model().rowsMoved.connect(self.read_columns_from_list) # for a dumb qss hack to make selected checkboxes not white on a light theme self.columnList.setObjectName("ColumnList") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel, self) self.vbox.addWidget(self.buttonBox) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.fill_column_list() self.set_default_checkbox() def eventFilter(self, object, event): if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return: self.toggle_selected_columns() return True return False def fill_column_list(self): self.columnList.clear() for column in self.columns: ColumnListItem(self.columnList, column) def accept(self): self.read_columns_from_list() self.header_changed.emit(self.preset_name, self.setAsDefaultCheckbox.isChecked(), self.columns) self.done(0) def reject(self): self.done(0) def read_columns_from_list(self): new_columns = [] for i in range(self.columnList.count()): item = self.columnList.item(i) new_columns.append(item.column) self.columns = new_columns def toggle_selected_columns(self): selected = self.columnList.selectedItems() for item in selected: value_now = item.data(Qt.CheckStateRole) item.setData(Qt.CheckStateRole, not value_now) self.columnList.reset() # @Improvement: is there a better way to update QListWidget? 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_header_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 column...', self.create_new_column_dialog) if len(self.columnList.selectedIndexes()) > 0: menu.addAction('Delete selected', self.delete_selected) menu.popup(self.columnList.viewport().mapToGlobal(position)) def load_preset(self, name): new_columns = CONFIG.load_header_preset(name) if not new_columns: return self.columns = new_columns self.preset_name = name self.fill_column_list() self.presetLabel.setText("Preset: {}".format(name)) self.set_default_checkbox() 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_header_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.presetLabel.setText("Preset: {}".format(name)) CONFIG.save_header_preset(name, self.columns) self.setAsDefaultCheckbox.setChecked(False) def delete_preset(self, name): CONFIG.delete_header_preset(name) if name == self.preset_name: self.columns = copy.deepcopy(DEFAULT_COLUMNS) self.fill_column_list() def create_new_column_dialog(self): d = CreateNewColumnDialog(self) d.add_new_column.connect(self.add_new_column) d.setWindowTitle('Create new column') d.open() def add_new_column(self, name, title): new_column = Column(name, title) # if the last column is message, insert this column before it (i think it makes sense?) if self.columns[-1].name == 'message': self.columns.insert(-1, new_column) else: self.columns.append(new_column) self.fill_column_list() def set_default_checkbox(self): self.setAsDefaultCheckbox.setChecked(CONFIG['default_header_preset'] == self.preset_name) def delete_selected(self): selected = self.columnList.selectedItems() for item in selected: self.columnList.takeItem(self.columnList.row(item)) self.read_columns_from_list() self.fill_column_list()
class StringListDlg(QDialog): def __init__(self, name, stringlist=None, parent=None): super(StringListDlg, self).__init__(parent) self.name = name self.listWidget = QListWidget() if stringlist is not None: self.listWidget.addItems(stringlist) self.listWidget.setCurrentRow(0) buttonLayout = QVBoxLayout() for text, slot in (("&Add...", self.add), ("&Edit...", self.edit), ("&Remove...", self.remove), ("&Up", self.up), ("&Down", self.down), ("&Sort", self.listWidget.sortItems), ("Close", self.accept)): button = QPushButton(text) if not MAC: button.setFocusPolicy(Qt.NoFocus) if text == "Close": buttonLayout.addStretch() buttonLayout.addWidget(button) button.clicked.connect(slot) # self.connect(button, SIGNAL("clicked()"), slot) layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) self.setLayout(layout) self.setWindowTitle("Edit {0} List".format(self.name)) def add(self): row = self.listWidget.currentRow() title = "Add {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title) if ok and string is not None: self.listWidget.insertItem(row, string) def edit(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is not None: title = "Edit {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title, QLineEdit.Normal, item.text()) if ok and string is not None: item.setText(string) def remove(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return reply = QMessageBox.question(self, "Remove {0}".format( self.name), "Remove {0} `{1}'?".format( self.name, unicode(item.text())), QMessageBox.Yes|QMessageBox.No) if reply == QMessageBox.Yes: item = self.listWidget.takeItem(row) del item def up(self): row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def reject(self): self.accept() def accept(self): self.stringlist = QStringListModel().stringList() for row in range(self.listWidget.count()): self.stringlist.append(self.listWidget.item(row).text()) #self.stringlist.acceptedList.emit(self.stringlist) QDialog.accept(self)
class SubwindowMisc(QWidget): """Show subwindow with miscellaneous settings.""" def createWindow(self, mainWindow, tab=''): """Create subwindow with miscellaneous settings.""" try: parent = None super().__init__(parent) # self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowIcon( QIcon(scctool.settings.getResFile('settings.png'))) self.setWindowModality(Qt.ApplicationModal) self.mainWindow = mainWindow self.passEvent = False self.controller = mainWindow.controller self.__dataChanged = False self.createButtonGroup() self.createTabs(tab) mainLayout = QVBoxLayout() mainLayout.addWidget(self.tabs) mainLayout.addLayout(self.buttonGroup) self.setLayout(mainLayout) self.resize( QSize(mainWindow.size().width() * .80, self.sizeHint().height())) relativeChange = QPoint(mainWindow.size().width() / 2, mainWindow.size().height() / 3)\ - QPoint(self.size().width() / 2, self.size().height() / 3) self.move(mainWindow.pos() + relativeChange) self.setWindowTitle(_("Miscellaneous Settings")) except Exception as e: module_logger.exception("message") def createTabs(self, tab=''): """Create tabs.""" self.tabs = QTabWidget() self.createMapsBox() self.createFavBox() self.createAliasBox() self.createOcrBox() self.createAlphaBox() # Add tabs self.tabs.addTab(self.mapsBox, _("Map Manager")) self.tabs.addTab(self.favBox, _("Favorites")) self.tabs.addTab(self.aliasBox, _("Alias")) self.tabs.addTab(self.ocrBox, _("OCR")) self.tabs.addTab(self.alphaBox, _("AlphaTL && Ingame Score")) table = dict() table['mapmanager'] = 0 table['favorites'] = 1 table['alias'] = 2 table['ocr'] = 3 table['alphatl'] = 4 self.tabs.setCurrentIndex(table.get(tab, -1)) def changed(self): """Handle changes.""" self.__dataChanged = True def createAlphaBox(self): """Create Alpha QWidget.""" self.alphaBox = QWidget() mainLayout = QVBoxLayout() box = QGroupBox(_("AlphaTL")) layout = QHBoxLayout() self.cb_trans_banner = QCheckBox( " " + _("Download transparent Banner of the Match")) self.cb_trans_banner.setChecked( scctool.settings.config.parser.getboolean( "SCT", "transparent_match_banner")) self.cb_trans_banner.stateChanged.connect(self.changed) layout.addWidget(self.cb_trans_banner) box.setLayout(layout) mainLayout.addWidget(box) box = QGroupBox(_("Set Ingame Score Task")) layout = QVBoxLayout() self.cb_ctrlx = QCheckBox(" " + _('Automatically press Ctrl+X to apply the' ' correct player order ingame')) self.cb_ctrlx.setToolTip( _("This will ensure that the player of the first team is always" " on the left/top in the ingame Observer UI.")) self.cb_ctrlx.setChecked( scctool.settings.config.parser.getboolean("SCT", "CtrlX")) self.cb_ctrlx.stateChanged.connect(self.changed) layout.addWidget(self.cb_ctrlx) self.cb_ctrln = QCheckBox(" " + _('Automatically press Ctrl+N before' ' OCR to display player names')) self.cb_ctrln.setToolTip( _("This is recommended for Standard and Gawliq Observer UI.")) self.cb_ctrln.setChecked( scctool.settings.config.parser.getboolean("SCT", "CtrlN")) self.cb_ctrln.stateChanged.connect(self.changed) layout.addWidget(self.cb_ctrln) self.cb_ctrlshifts = QCheckBox( " " + _('Automatically press Ctrl+Shift+S to display' ' the ingame score')) self.cb_ctrlshifts.setToolTip( _("Ctrl+Shift+S is needed for the WCS-Gameheart Oberserver" " Overlay, but disables the sound for other overlays.")) self.cb_ctrlshifts.setChecked( scctool.settings.config.parser.getboolean("SCT", "CtrlShiftS")) self.cb_ctrlshifts.stateChanged.connect(self.changed) layout.addWidget(self.cb_ctrlshifts) self.cb_ctrlshiftc = QCheckBox( " " + _('Automatically press Ctrl+Shift+C to toogle the clan tag')) self.cb_ctrlshiftc.setChecked( scctool.settings.config.parser.getboolean("SCT", "CtrlShiftC")) self.cb_ctrlshiftc.stateChanged.connect(self.changed) layout.addWidget(self.cb_ctrlshiftc) container = QHBoxLayout() self.cb_ctrlshiftr = QComboBox() self.cb_ctrlshiftr.addItem("0") self.cb_ctrlshiftr.addItem("1") self.cb_ctrlshiftr.addItem("2") try: self.cb_ctrlshiftr.setCurrentIndex( scctool.settings.config.parser.getint("SCT", "CtrlShiftR")) except Exception: self.cb_ctrlshiftr.setCurrentIndex(0) self.cb_ctrlshiftr.setMaximumWidth(40) self.cb_ctrlshiftr.currentIndexChanged.connect(self.changed) container.addWidget( QLabel( _('Automatically press Ctrl+Shift+R to toogle the race icon ')) ) container.addWidget(self.cb_ctrlshiftr) container.addWidget(QLabel(_(' time(s)'))) layout.addLayout(container) self.cb_blacklist = QCheckBox(" " + _('Activate Blacklist for' ' Ingame Score')) self.cb_blacklist.setChecked( scctool.settings.config.parser.getboolean("SCT", "blacklist_on")) self.cb_blacklist.stateChanged.connect(self.changed) layout.addWidget(self.cb_blacklist) box.setLayout(layout) mainLayout.addWidget(box) box = QGroupBox(_("Blacklist for Ingame Score")) layout = QVBoxLayout() blacklistDesc = _("Enter your SC2 client usernames to deactivate" " automatically setting the ingame score and" " toogling the production tab when you are playing" " yourself. Replays are exempt.") label = QLabel(blacklistDesc) label.setAlignment(Qt.AlignJustify) label.setWordWrap(True) layout.addWidget(label) self.list_blacklist = ListTable(4, scctool.settings.config.getBlacklist()) self.list_blacklist.dataModified.connect(self.changed) self.list_blacklist.setFixedHeight(50) layout.addWidget(self.list_blacklist) box.setLayout(layout) mainLayout.addWidget(box) mainLayout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.alphaBox.setLayout(mainLayout) def createFavBox(self): """Create favorites box.""" self.favBox = QWidget() mainLayout = QVBoxLayout() box = QGroupBox(_("Players")) layout = QHBoxLayout() self.list_favPlayers = ListTable( 4, scctool.settings.config.getMyPlayers()) self.list_favPlayers.dataModified.connect(self.changed) self.list_favPlayers.setFixedHeight(150) layout.addWidget(self.list_favPlayers) box.setLayout(layout) mainLayout.addWidget(box) box = QGroupBox(_("Teams")) layout = QVBoxLayout() self.list_favTeams = ListTable(3, scctool.settings.config.getMyTeams()) self.list_favTeams.dataModified.connect(self.changed) self.list_favTeams.setFixedHeight(100) layout.addWidget(self.list_favTeams) self.cb_swapTeams = QCheckBox( _('Swap my favorite team always to the left')) self.cb_swapTeams.setChecked( scctool.settings.config.parser.getboolean("SCT", "swap_myteam")) self.cb_swapTeams.stateChanged.connect(self.changed) layout.addWidget(self.cb_swapTeams) box.setLayout(layout) mainLayout.addWidget(box) mainLayout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.favBox.setLayout(mainLayout) def createAliasBox(self): """Create favorites box.""" self.aliasBox = QWidget() mainLayout = QGridLayout() aliasDesc = _( 'Player and team aliases are replaced by the actual name when' + ' encountered by the match grabber. Additionally, SC2 player' + ' names listed as aliases are replaced in the intros' + ' and used to identify players by the automatic' + ' background tasks "Auto Score Update" and "Set Ingame Score".') label = QLabel(aliasDesc) label.setAlignment(Qt.AlignJustify) label.setWordWrap(True) mainLayout.addWidget(label, 1, 0, 1, 2) box = QGroupBox(_("Player Aliases")) layout = QVBoxLayout() self.list_aliasPlayers = AliasTreeView(self) self.list_aliasPlayers.aliasRemoved.connect( self.controller.aliasManager.removePlayerAlias) layout.addWidget(self.list_aliasPlayers) addButton = QPushButton(_("Add Alias")) addButton.clicked.connect( lambda: self.addAlias(self.list_aliasPlayers, _('Player Name'))) layout.addWidget(addButton) box.setLayout(layout) mainLayout.addWidget(box, 0, 0) box = QGroupBox(_("Team Aliases")) layout = QVBoxLayout() self.list_aliasTeams = AliasTreeView(self) self.list_aliasTeams.aliasRemoved.connect( self.controller.aliasManager.removeTeamAlias) layout.addWidget(self.list_aliasTeams) addButton = QPushButton(_("Add Alias")) addButton.clicked.connect( lambda: self.addAlias(self.list_aliasTeams, _('Team Name'))) layout.addWidget(addButton) box.setLayout(layout) mainLayout.addWidget(box, 0, 1) list = self.controller.aliasManager.playerAliasList() for player, aliases in list.items(): self.list_aliasPlayers.insertAliasList(player, aliases) list = self.controller.aliasManager.teamAliasList() for team, aliases in list.items(): self.list_aliasTeams.insertAliasList(team, aliases) self.aliasBox.setLayout(mainLayout) def addAlias(self, widget, scope, name=""): name, ok = QInputDialog.getText(self, scope, scope + ':', text=name) if not ok: return name = name.strip() alias, ok = QInputDialog.getText(self, _('Alias'), _('Alias of {}').format(name) + ':', text="") alias = alias.strip() if not ok: return try: if widget == self.list_aliasPlayers: self.controller.aliasManager.addPlayerAlias(name, alias) elif widget == self.list_aliasTeams: self.controller.aliasManager.addTeamAlias(name, alias) widget.insertAlias(name, alias, True) except Exception as e: module_logger.exception("message") QMessageBox.critical(self, _("Error"), str(e)) def createOcrBox(self): """Create forms for OCR.""" self.ocrBox = QWidget() mainLayout = QVBoxLayout() box = QGroupBox( _("Optical Character Recognition for" " Automatic Setting of Ingame Score")) layout = QGridLayout() self.cb_useocr = QCheckBox(" " + _("Activate Optical Character Recognition")) self.cb_useocr.setChecked( scctool.settings.config.parser.getboolean("SCT", "use_ocr")) self.cb_useocr.stateChanged.connect(self.changed) self.tesseract = MonitoredLineEdit() self.tesseract.setText( scctool.settings.config.parser.get("SCT", "tesseract")) self.tesseract.textModified.connect(self.changed) # self.tesseract.setAlignment(Qt.AlignCenter) self.tesseract.setPlaceholderText( "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract") self.tesseract.setReadOnly(True) self.tesseract.setToolTip(_('Tesseract-OCR Executable')) self.browse = QPushButton(_("Browse...")) self.browse.clicked.connect(self.selectTesseract) text = _( "Sometimes the order of players given by the SC2-Client-API" " differs from the order in the Observer-UI resulting in a" " swapped match score. To correct this via Optical Character" " Recognition you have to download {} and install and select the" " exectuable below, if it is not detected automatically.") url = 'https://github.com/UB-Mannheim/tesseract' + \ '/wiki#tesseract-at-ub-mannheim' href = "<a href='{}'>" + "Tesseract-OCR" + "</a>" href = href.format(url) label = QLabel(text.format(href)) label.setAlignment(Qt.AlignJustify) label.setOpenExternalLinks(True) label.setWordWrap(True) label.setMargin(5) layout.addWidget(label, 1, 0, 1, 2) layout.addWidget(self.cb_useocr, 0, 0, 1, 2) layout.addWidget(QLabel(_("Tesseract-OCR Executable") + ":"), 2, 0) layout.addWidget(self.tesseract, 3, 0) layout.addWidget(self.browse, 3, 1) box.setLayout(layout) mainLayout.addWidget(box) mainLayout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.ocrBox.setLayout(mainLayout) if (not scctool.settings.windows): self.cb_useocr.setEnabled(False) self.cb_useocr.setAttribute(Qt.WA_AlwaysShowToolTips) self.cb_useocr.setToolTip( _("This feature is only available in Windows.")) self.tesseract.setEnabled(False) self.tesseract.setAttribute(Qt.WA_AlwaysShowToolTips) self.tesseract.setToolTip( _("This feature is only available in Windows.")) self.browse.setEnabled(False) self.browse.setAttribute(Qt.WA_AlwaysShowToolTips) self.browse.setToolTip( _("This feature is only available in Windows.")) def selectTesseract(self): """Create forms for tesseract.""" old_exe = self.tesseract.text() default = scctool.settings.config.findTesserAct(old_exe) exe, ok = QFileDialog.getOpenFileName( self, _("Select Tesseract-OCR Executable"), default, _("Tesseract-OCR Executable") + " (tesseract.exe);; " + _("Executable") + " (*.exe);; " + _("All files") + " (*)") if (ok and exe != old_exe): self.tesseract.setText(exe) self.changed() def createMapsBox(self): """Create box for map manager.""" self.mapsize = 300 self.mapsBox = QWidget() layout = QGridLayout() self.maplist = QListWidget() self.maplist.setSortingEnabled(True) for map in scctool.settings.maps: self.maplist.addItem(QListWidgetItem(map)) self.maplist.setCurrentItem(self.maplist.item(0)) self.maplist.currentItemChanged.connect(self.changePreview) # self.maplist.setFixedHeight(self.mapsize) self.maplist.setMinimumWidth(150) layout.addWidget(self.maplist, 0, 1, 2, 1) self.mapPreview = QLabel() self.mapPreview.setFixedWidth(self.mapsize) self.mapPreview.setFixedHeight(self.mapsize) self.mapPreview.setAlignment(Qt.AlignCenter) layout.addWidget(self.mapPreview, 0, 0) self.mapInfo = QLabel() self.mapInfo.setIndent(10) layout.addWidget(self.mapInfo, 1, 0) self.pb_addMapLiquipedia = QPushButton(_("Add from Liquipedia")) self.pb_addMapLiquipedia.clicked.connect(self.addFromLquipedia) self.pb_addMap = QPushButton(_("Add from File")) self.pb_addMap.clicked.connect(self.addMap) self.pb_renameMap = QPushButton(_("Rename")) self.pb_renameMap.clicked.connect(self.renameMap) self.pb_changeMap = QPushButton(_("Change Image")) self.pb_changeMap.clicked.connect(self.changeMap) self.pb_removeMap = QPushButton(_("Remove")) self.pb_removeMap.clicked.connect(self.deleteMap) self.sc_removeMap = QShortcut(QKeySequence("Del"), self) self.sc_removeMap.setAutoRepeat(False) self.sc_removeMap.activated.connect(self.deleteMap) box = QWidget() container = QHBoxLayout() container.addWidget(self.pb_addMapLiquipedia, 0) container.addWidget(self.pb_addMap, 0) container.addWidget(QLabel(), 4) container.addWidget(self.pb_renameMap, 0) container.addWidget(self.pb_changeMap, 0) container.addWidget(self.pb_removeMap, 0) box.setLayout(container) layout.addWidget(box, 2, 0, 1, 2) layout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 3, 0, 1, 2) self.changePreview() self.mapsBox.setLayout(layout) def renameMap(self): """Rename maps.""" item = self.maplist.currentItem() map = item.text() text, ok = QInputDialog.getText(self, _('Map Name'), _('Map Name') + ':', text=map) if not ok: return text = text.strip() if (text == map): return if text.lower() == 'tbd': QMessageBox.critical( self, _("Error"), _('"{}" is not a valid map name.').format(text)) return if (text in scctool.settings.maps): buttonReply = QMessageBox.warning( self, _("Duplicate Entry"), _("Map is already in list! Overwrite?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.No: return self.controller.addMap(self.controller.getMapImg(map, True), text) self.controller.deleteMap(map) item.setText(text) def changeMap(self): """Change a map.""" map = self.maplist.currentItem().text() fileName, ok = QFileDialog.getOpenFileName( self, _("Select Map Image (> 500x500px recommended)"), "", _("Supported Images") + " (*.png *.jpg)") if ok: base = os.path.basename(fileName) name, ext = os.path.splitext(base) name = name.replace("_", " ") self.controller.deleteMap(map) self.controller.addMap(fileName, map) self.changePreview() def addMap(self): """Add a map.""" fileName, ok = QFileDialog.getOpenFileName( self, _("Select Map Image (> 500x500px recommended)"), "", _("Supported Images") + " (*.png *.jpg)") if ok: base = os.path.basename(fileName) name, ext = os.path.splitext(base) name = name.replace("_", " ") map_name, ok = QInputDialog.getText(self, _('Map Name'), _('Map Name') + ':', text=name) map_name = map_name.strip() if ok: if map_name.lower() == 'tbd': QMessageBox.critical( self, _("Error"), _('"{}" is not a valid map name.').format(map_name)) return if (map_name in scctool.settings.maps): buttonReply = QMessageBox.warning( self, _("Duplicate Entry"), _("Map is already in list! Overwrite?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.No: return else: self.controller.deleteMap(map_name) self.controller.addMap(fileName, map_name) items = self.maplist.findItems(map_name, Qt.MatchExactly) if len(items) == 0: item = QListWidgetItem(map_name) self.maplist.addItem(item) self.maplist.setCurrentItem(item) else: self.maplist.setCurrentItem(items[0]) self.changePreview() def addFromLquipedia(self): grabber = LiquipediaGrabber() search_str = '' while True: search_str, ok = QInputDialog.getText(self, _('Map Name'), _('Map Name') + ':', text=search_str) search_str.strip() try: if ok and search_str: if search_str.lower() == 'tbd': QMessageBox.critical( self, _("Error"), _('"{}" is not a valid map name.').format( search_str)) continue try: map = grabber.get_map(search_str) except MapNotFound: QMessageBox.critical( self, _("Map not found"), _('"{}" was not found on Liquipedia.').format( search_str)) continue map_name = map.get_name() if (map_name in scctool.settings.maps): buttonReply = QMessageBox.warning( self, _("Duplicate Entry"), _("Map {} is already in list! Overwrite?".format( map_name)), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.No: break else: self.controller.deleteMap(map_name) images = grabber.get_images(map.get_map_images()) image = "" for size in sorted(images): if not image or size <= 2500 * 2500: image = images[size] url = grabber._base_url + image downloader = MapDownloader(self, map_name, url) downloader.download() if map_name not in scctool.settings.maps: scctool.settings.maps.append(map_name) items = self.maplist.findItems(map_name, Qt.MatchExactly) if len(items) == 0: item = QListWidgetItem(map_name) self.maplist.addItem(item) self.maplist.setCurrentItem(item) else: self.maplist.setCurrentItem(items[0]) self.changePreview() except Exception as e: module_logger.exception("message") QMessageBox.critical(self, _("Error"), str(e)) finally: break def deleteMap(self): """Delete a map.""" item = self.maplist.currentItem() map = item.text() buttonReply = QMessageBox.question( self, _('Delete map?'), _("Delete '{}' permanently?").format(map), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.controller.deleteMap(map) self.maplist.takeItem(self.maplist.currentRow()) def changePreview(self): """Change the map preview.""" if self.maplist.count() < 1: return map = self.maplist.currentItem().text() if (map == "TBD"): self.pb_renameMap.setEnabled(False) self.pb_removeMap.setEnabled(False) self.sc_removeMap.setEnabled(False) else: self.pb_removeMap.setEnabled(True) self.pb_renameMap.setEnabled(True) self.sc_removeMap.setEnabled(True) file = self.controller.getMapImg(map, True) map = QPixmap(file) width = map.height() height = map.width() ext = os.path.splitext(file)[1].replace(".", "").upper() size = humanize.naturalsize(os.path.getsize(file)) map = QPixmap(file).scaled(self.mapsize, self.mapsize, Qt.KeepAspectRatio) self.mapPreview.setPixmap(map) text = "{}x{}px, {}, {}".format(width, height, str(size), ext) self.mapInfo.setText(text) def createButtonGroup(self): """Create buttons.""" try: layout = QHBoxLayout() layout.addWidget(QLabel("")) buttonCancel = QPushButton(_('Cancel')) buttonCancel.clicked.connect(self.closeWindow) layout.addWidget(buttonCancel) buttonSave = QPushButton(_('&Save && Close')) buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S")) self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self) self.shortcut.setAutoRepeat(False) self.shortcut.activated.connect(self.saveCloseWindow) buttonSave.clicked.connect(self.saveCloseWindow) layout.addWidget(buttonSave) self.buttonGroup = layout except Exception as e: module_logger.exception("message") def saveData(self): """Save the data.""" if (self.__dataChanged): scctool.settings.config.parser.set( "SCT", "myteams", ", ".join(self.list_favTeams.getData())) scctool.settings.config.parser.set( "SCT", "commonplayers", ", ".join(self.list_favPlayers.getData())) scctool.settings.config.parser.set("SCT", "tesseract", self.tesseract.text().strip()) scctool.settings.config.parser.set("SCT", "use_ocr", str(self.cb_useocr.isChecked())) scctool.settings.config.parser.set( "SCT", "transparent_match_banner", str(self.cb_trans_banner.isChecked())) scctool.settings.config.parser.set( "SCT", "CtrlShiftS", str(self.cb_ctrlshifts.isChecked())) scctool.settings.config.parser.set( "SCT", "CtrlShiftC", str(self.cb_ctrlshiftc.isChecked())) scctool.settings.config.parser.set( "SCT", "swap_myteam", str(self.cb_swapTeams.isChecked())) scctool.settings.config.parser.set("SCT", "CtrlN", str(self.cb_ctrln.isChecked())) scctool.settings.config.parser.set("SCT", "CtrlX", str(self.cb_ctrlx.isChecked())) scctool.settings.config.parser.set( "SCT", "CtrlShiftR", str(self.cb_ctrlshiftr.currentText())) scctool.settings.config.parser.set( "SCT", "blacklist_on", str(self.cb_blacklist.isChecked())) scctool.settings.config.parser.set( "SCT", "blacklist", ", ".join(self.list_blacklist.getData())) self.__dataChanged = False def saveCloseWindow(self): """Save and close window.""" self.saveData() self.passEvent = True self.close() def closeWindow(self): """Close window.""" self.passEvent = True self.close() def closeEvent(self, event): """Handle close event.""" try: self.mainWindow.updateMapCompleters() if (not self.__dataChanged): event.accept() return if (not self.passEvent): if (self.isMinimized()): self.showNormal() buttonReply = QMessageBox.question( self, _('Save data?'), _("Save data?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.saveData() event.accept() except Exception as e: module_logger.exception("message")
class Ui_MainWindow(object): def setupUi(self, MainWindow): self.defaultError = "Welcome to EZ Audio! Expand your audio library by entering a valid Youtube URL above." self.defaultURL = "Enter URL Here..." MainWindow.setObjectName("MainWindow") MainWindow.resize(421, 500) font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") font.setPointSize(9) MainWindow.setFont(font) MainWindow.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) MainWindow.setStyleSheet("background-color: rgb(74, 74, 74);") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.PrimaryLayout = QtWidgets.QVBoxLayout() self.PrimaryLayout.setContentsMargins(10, 10, 10, 90) self.PrimaryLayout.setSpacing(30) self.PrimaryLayout.setObjectName("PrimaryLayout") self.TopLayout = QtWidgets.QHBoxLayout() self.TopLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) self.TopLayout.setContentsMargins(0, -1, 0, 0) self.TopLayout.setSpacing(0) self.TopLayout.setObjectName("TopLayout") self.TitleLayout = QtWidgets.QVBoxLayout() self.TitleLayout.setContentsMargins(0, 0, 0, -1) self.TitleLayout.setSpacing(0) self.TitleLayout.setObjectName("TitleLayout") self.EZAudioLogo = QtWidgets.QLabel(self.centralwidget) self.EZAudioLogo.setMinimumSize(QtCore.QSize(20, 100)) font = QtGui.QFont() font.setFamily("Poor Richard") font.setPointSize(26) font.setBold(False) font.setWeight(50) self.EZAudioLogo.setFont(font) self.EZAudioLogo.setStyleSheet("color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));\n" "color: rgb(255, 255, 255);") self.EZAudioLogo.setObjectName("EZAudioLogo") self.TitleLayout.addWidget(self.EZAudioLogo, 0, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter) self.TopLayout.addLayout(self.TitleLayout) self.QueueLayout = QtWidgets.QVBoxLayout() self.QueueLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize) self.QueueLayout.setContentsMargins(0, 0, 0, 0) self.QueueLayout.setSpacing(0) self.QueueLayout.setObjectName("QueueLayout") self.LibraryButton = QtWidgets.QPushButton(self.centralwidget) font = QtGui.QFont() font.setFamily("Bookman Old Style") font.setPointSize(11) self.LibraryButton.setFont(font) self.LibraryButton.setStyleSheet("color: rgb(255, 255, 255);\n" "background-color: rgb(103, 103, 103);") self.LibraryButton.setObjectName("LibraryButton") self.QueueLayout.addWidget(self.LibraryButton) self.QueueButton = QtWidgets.QPushButton(self.centralwidget) font = QtGui.QFont() font.setFamily("Bookman Old Style") font.setPointSize(11) self.QueueButton.setFont(font) self.QueueButton.setStyleSheet("color: rgb(255, 255, 255);\n" "background-color: rgb(103, 103, 103);") self.QueueButton.setObjectName("QueueButton") self.QueueLayout.addWidget(self.QueueButton) self.TopLayout.addLayout(self.QueueLayout) self.TopLayout.setStretch(0, 1) self.PrimaryLayout.addLayout(self.TopLayout) self.BottomLayout = QtWidgets.QVBoxLayout() self.BottomLayout.setSpacing(30) self.BottomLayout.setObjectName("BottomLayout") self.ConvertLayout = QtWidgets.QHBoxLayout() self.ConvertLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) self.ConvertLayout.setContentsMargins(-1, -1, 0, 0) self.ConvertLayout.setSpacing(5) self.ConvertLayout.setObjectName("ConvertLayout") self.URLBox = QtWidgets.QLineEdit(self.centralwidget) font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") font.setPointSize(12) self.URLBox.setFont(font) self.URLBox.setStyleSheet("background-color: rgb(168, 168, 168);") self.URLBox.setObjectName("URLBox") self.ConvertLayout.addWidget(self.URLBox) self.ConvertButton = QtWidgets.QToolButton(self.centralwidget) self.ConvertButton.setMinimumSize(QtCore.QSize(101, 0)) font = QtGui.QFont() font.setFamily("Bookman Old Style") font.setPointSize(10) font.setUnderline(False) self.ConvertButton.setFont(font) self.ConvertButton.setStyleSheet("color: rgb(255, 255, 255);\n" "background-color: rgb(103, 103, 103);") self.ConvertButton.setObjectName("ConvertButton") self.ConvertLayout.addWidget(self.ConvertButton) self.BottomLayout.addLayout(self.ConvertLayout) self.ErrorBox = QtWidgets.QLabel(self.centralwidget) self.ErrorBox.setMinimumSize(QtCore.QSize(361, 61)) font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") # self.ErrorBox.setFont(font) # self.ErrorBox.setStyleSheet("background-color: rgb(168, 168, 168);") # self.ErrorBox.setFrameShape(QtWidgets.QFrame.NoFrame) # self.ErrorBox.setFrameShadow(QtWidgets.QFrame.Raised) # self.ErrorBox.setLineWidth(0) # self.ErrorBox.setMidLineWidth(0) # self.ErrorBox.setTextFormat(QtCore.Qt.AutoText) # self.ErrorBox.setScaledContents(False) # self.ErrorBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) # self.ErrorBox.setWordWrap(True) # self.ErrorBox.setObjectName("ErrorBox") self.BottomLayout.addWidget(self.ErrorBox) self.PrimaryLayout.addLayout(self.BottomLayout) self.horizontalLayout_3.addLayout(self.PrimaryLayout) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 401, 21)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.formatCombo = QtWidgets.QComboBox(self.centralwidget) self.formatCombo.setGeometry(QtCore.QRect(19, 190, 70, 20)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(15) sizePolicy.setVerticalStretch(32) sizePolicy.setHeightForWidth(self.formatCombo.sizePolicy().hasHeightForWidth()) self.formatCombo.setSizePolicy(sizePolicy) self.formatCombo.setMinimumSize(QtCore.QSize(70, 25)) self.formatCombo.setMaximumSize(QtCore.QSize(70, 25)) self.formatCombo.setStyleSheet("background-color: rgb(168, 168, 168);") font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") self.formatCombo.setFont(font) self.formatCombo.setObjectName("formatCombo") self.formatCombo.addItem("") self.formatCombo.addItem("") self.bitRateCombo = QtWidgets.QComboBox(self.centralwidget) self.bitRateCombo.setGeometry(QtCore.QRect(100, 190, 70, 20)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(15) sizePolicy.setVerticalStretch(32) sizePolicy.setHeightForWidth(self.bitRateCombo.sizePolicy().hasHeightForWidth()) self.bitRateCombo.setSizePolicy(sizePolicy) self.bitRateCombo.setMinimumSize(QtCore.QSize(70, 25)) self.bitRateCombo.setMaximumSize(QtCore.QSize(70, 25)) self.bitRateCombo.setStyleSheet("background-color: rgb(168, 168, 168);") font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") self.bitRateCombo.setFont(font) self.bitRateCombo.setObjectName("bitRateCombo") self.bitRateCombo.addItem("") self.bitRateCombo.addItem("") self.bitRateCombo.addItem("") self.convert_Button = QPushButton('Convert', MainWindow) font = QtGui.QFont() font.setFamily("Bookman Old Style") font.setPointSize(10) font.setUnderline(False) self.convert_Button.setFont(font) self.convert_Button.setStyleSheet("color: rgb(255, 255, 255);\n" "background-color: rgb(103, 103, 103);") self.convert_Button.resize(100, 25) self.convert_Button.move(299, 190) self.initUI() self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def changeError(self): self.inputURL = self.URLBox.text() _translate = QtCore.QCoreApplication.translate self.ErrorBox.setText(_translate("MainWindow", "Hold on...")) conversionEngine.converter.transcodeURL(self.inputURL) self.ErrorBox.setText(_translate("MainWindow", "Download done!")) def openLibrary(self): self.ex = List() self.ex.show() def openQueue(self): #Cloud Function self.cal = Window() self.cal.show() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "EZ Audio")) self.URLBox.setText(_translate("MainWindow", self.defaultURL)) self.EZAudioLogo.setText(_translate("MainWindow", "<html><head/><body><p>EZ Audio</p><p><span style=\" font-size:11pt;\">By Yousif, Omar, Jack, and Khaled</span></p></body></html>")) self.LibraryButton.setText(_translate("MainWindow", "Library")) self.QueueButton.setText(_translate("MainWindow", "Cloud")) self.ConvertButton.setText(_translate("MainWindow", "Add Url")) # self.ErrorBox.setText(_translate("MainWindow", self.defaultError)) # self.ConvertButton.clicked.connect(self.changeError) self.ConvertButton.clicked.connect(self.addUrl) self.URL_List.clicked.connect(self.removeURL) self.formatCombo.setItemText(0, _translate("MainWindow", "mp3")) self.formatCombo.setItemText(1, _translate("MainWindow", "m4a")) self.bitRateCombo.setItemText(0, _translate("MainWindow", "48")) self.bitRateCombo.setItemText(1, _translate("MainWindow", "96")) self.bitRateCombo.setItemText(2, _translate("MainWindow", "128")) self.convert_Button.clicked.connect(self.covertURL) self.LibraryButton.clicked.connect(self.openLibrary) self.QueueButton.clicked.connect(self.openQueue) @pyqtSlot() def initUI(self): self.textbox = QLineEdit(MainWindow) self.textbox.move(19, 460) self.textbox.resize(200, 20) self.textbox.setStyleSheet("background-color: rgb(168, 168, 168);") self.textbox.setText("(To delete a URL double click on it)") font = QtGui.QFont() font.setFamily("Leelawadee UI Semilight") self.textbox.setFont(font) #self.textbox.fornt.setPointSize(50) self.URL_List = QListWidget(MainWindow) self.URL_List.move(19, 230) self.URL_List.resize(350, 220) self.URL_List.setStyleSheet("""QListWidget{ background: rgb(168, 168, 168); }""") def addUrl(self): textboxValue = self.URLBox.text() subString = textboxValue[0:23] print(subString) if subString == "https://www.youtube.com": self.URL_List.addItem(textboxValue) else: # self.message = QMessageBox(MainWindow) QMessageBox(MainWindow).question(MainWindow, 'Error', "The Url you entered is not a valid ", QMessageBox.Ok, QMessageBox.Ok) print(subString) def removeURL(self): index = self.URL_List.row(self.URL_List.currentItem()) buttonReply = QMessageBox(MainWindow).question(MainWindow, 'Message', "Do you want to delete?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.URL_List.takeItem(index) else: return def covertURL(self): index = self.URL_List.item(0) if index is None: QMessageBox(MainWindow).question(MainWindow, 'Message', "No Url to convert ", QMessageBox.Ok, QMessageBox.Ok) else: while index is not None: # print(self.URL_List.item(0).text()) self.inputURL = self.URL_List.item(0).text() x = str(self.formatCombo.currentText()) y = int(self.bitRateCombo.currentText()) conversionEngine.ytdlExec(self.inputURL, y, x) self.URL_List.takeItem(0) index = self.URL_List.item(0) QMessageBox(MainWindow).question(MainWindow, 'Message', "All url have been converted ", QMessageBox.Ok, QMessageBox.Ok)
class ListEdit(QWidget): """A widget to edit a list of items (e.g. a list of directories).""" # emitted when anything changed in the listbox. changed = pyqtSignal() def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) layout = QGridLayout(self) self.setLayout(layout) self.addButton = QPushButton(icons.get('list-add'), '') self.editButton = QPushButton(icons.get('document-edit'), '') self.removeButton = QPushButton(icons.get('list-remove'), '') self.listBox = QListWidget() layout.setContentsMargins(1, 1, 1, 1) layout.setSpacing(0) layout.addWidget(self.listBox, 0, 0, 8, 1) layout.addWidget(self.addButton, 0, 1) layout.addWidget(self.editButton, 1, 1) layout.addWidget(self.removeButton, 2, 1) self.changed.connect(self.updateSelection) self.listBox.itemSelectionChanged.connect(self.updateSelection) self.updateSelection() self.connectSlots() app.translateUI(self) def connectSlots(self): self.addButton.clicked.connect(self.addClicked) self.editButton.clicked.connect(self.editClicked) self.removeButton.clicked.connect(self.removeClicked) self.listBox.itemDoubleClicked.connect(self.itemDoubleClicked) self.listBox.model().layoutChanged.connect(self.changed) def translateUI(self): self.addButton.setText(_("&Add...")) self.editButton.setText(_("&Edit...")) self.removeButton.setText(_("&Remove")) def addClicked(self, button): item = self.createItem() if self.openEditor(item): self.addItem(item) def editClicked(self, button): item = self.listBox.currentItem() item and self.editItem(item) def removeClicked(self, button): item = self.listBox.currentItem() if item: self.removeItem(item) def updateSelection(self): selected = bool(self.listBox.currentItem()) self.editButton.setEnabled(selected) self.removeButton.setEnabled(selected) def itemDoubleClicked(self, item): item and self.editItem(item) def createItem(self): return QListWidgetItem() def addItem(self, item): self.listBox.addItem(item) self.itemChanged(item) self.changed.emit() def removeItem(self, item): self.listBox.takeItem(self.listBox.row(item)) self.changed.emit() def editItem(self, item): if self.openEditor(item): self.itemChanged(item) self.changed.emit() def setCurrentItem(self, item): self.listBox.setCurrentItem(item) def setCurrentRow(self, row): self.listBox.setCurrentRow(row) def openEditor(self, item): """Opens an editor (dialog) for the item. Returns True if the dialog was accepted and the item edited. Returns False if the dialog was cancelled (the item must be left unedited). """ pass def itemChanged(self, item): """Called after an item has been added or edited. Re-implement to do something at this moment if needed, e.g. alter the text or display of other items. """ pass def setValue(self, strings): """Sets the listbox to a list of strings.""" self.listBox.clear() self.listBox.addItems(strings) self.changed.emit() def value(self): """Returns the list of paths in the listbox.""" return [self.listBox.item(i).text() for i in range(self.listBox.count())] def setItems(self, items): """Sets the listbox to a list of items.""" self.listBox.clear() for item in items: self.listBox.addItem(item) self.itemChanged(item) self.changed.emit() def items(self): """Returns the list of items in the listbox.""" return [self.listBox.item(i) for i in range(self.listBox.count())] def clear(self): """Clears the listbox.""" self.listBox.clear() self.changed.emit()
class plotWidget(QWidget): NextId = 1 FONT_MAX_SIZE = 30 FONT_MIN_SIZE = 10 TEXTCHANGED = 0 customDataChanged = pyqtSignal() def __init__(self, filename=None, parent=None): """ Creates an Instance of QWidget Args: filename (str): for opening a parameter file parent (object) """ super(plotWidget, self).__init__(parent) self.parent = parent self.setAttribute(Qt.WA_DeleteOnClose) self.filename = filename # VIT self.save_title = '' self.setWindowTitle(QFileInfo(self.filename).fileName()) hbox = QHBoxLayout(self) self.plotToolWidget = showPlot(self, width=5, height=4, dpi=100) self.listWidget = QListWidget(self) self.listWidget.currentRowChanged.connect(self.updatePlot) # self.listWidget.setMinimumWidth(200) self.listWidget.setMaximumWidth(200) self.splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal) # This is Yeah self.splitter.addWidget(self.plotToolWidget) # This is Yeah self.splitter.addWidget(self.listWidget) # This is Yeah hbox.addWidget(self.splitter) # hbox.addWidget(listWidget) self.readData() def updatePlot(self): print("Update Plot Yahooooo ") row = self.listWidget.currentRow() self.title = self.listWidget.item(row).text() print('Row ', row) print('----------------------------') x, y = self.edrObject.dataExtractFromRow(row) unit = self.edrObject.getUnits(row) print('Unit is ', unit) # print('x ',len(x)) # print('y ',len(y)) self.plotToolWidget.plotFigure(x, y, 'ps', unit, self.title) def readData(self): self.edrObject = EdrIO(self.filename, 'float') # for now print('edrObject ', self.edrObject) self.populateList() def populateList(self): self.props = self.edrObject.read('avail quantities') print('props ', self.props) index = 0 for i in self.props: self.listWidget.addItem(str(index) + '. ' + i) index += 1 def save(self): #: So self.title has to be modified self.save_title = self.title.split(' ')[1] + '.png' print('save_title is ', self.save_title) if "edr" in self.filename: filename = QFileDialog.getSaveFileName(self, "G.R.O.M. Editor -- Save File As", self.save_title, "png (*.png *.*)") print('filename is ', filename) if len(filename[0]) == 0: return self.filenameSave = filename[0] print('Save graph ', self.filenameSave) # self.setWindowTitle(QFileInfo(self.filename).fileName()) exception = None fh = None try: # fh = QFile(self.filenameSave) # if not fh.open(QIODevice.WriteOnly): # raise IOError(str(fh.errorString())) self.plotToolWidget.saveFig(self.filenameSave) except EnvironmentError as e: exception = e print('error in saving ', e)
class VariableSettingWindowDemo(QDialog): def __init__(self, data_list): super(VariableSettingWindowDemo, self).__init__() self.initUI(data_list) def initUI(self, data_list): self.resize(800, 800) self.setWindowTitle('变量设置') self.signal = MySignal() icon = QIcon() icon.addPixmap(QPixmap('./image/设置.png')) self.setWindowIcon(icon) self.label1 = QLabel('变量') self.label2 = QLabel('自变量') self.label3 = QLabel('因变量') self.list_widget1 = QListWidget() self.list_widget2 = QListWidget() self.list_widget3 = QListWidget() self.list_widget1.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget2.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget3.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget1.addItems(data_list) self.button12 = QPushButton('-->') self.button21 = QPushButton('<--') self.button13 = QPushButton('-->') self.button31 = QPushButton('<--') self.button1 = QPushButton('确认') self.button2 = QPushButton('取消') vlayout1 = QVBoxLayout() vlayout1.addWidget(self.button12) vlayout1.addWidget(self.button21) hlayout1 = QHBoxLayout() hlayout1.addItem(vlayout1) vlayout = QVBoxLayout() # vlayout.addWidget(QLabel('')) vlayout.addWidget(self.label2) vlayout.addWidget(self.list_widget2) vlayout.setSpacing(10) hlayout1.addItem(vlayout) vlayout2 = QVBoxLayout() vlayout2.addWidget(self.button13) vlayout2.addWidget(self.button31) hlayout2 = QHBoxLayout() hlayout2.addItem(vlayout2) vlayout = QVBoxLayout() # vlayout.addWidget(QLabel('')) vlayout.addWidget(self.label3) vlayout.addWidget(self.list_widget3) vlayout.setSpacing(10) hlayout2.addItem(vlayout) gridlayout = QGridLayout() hlayout = QHBoxLayout() # hlayout.addWidget(self.button1) # hlayout.addWidget(self.button2) hlayout.setSpacing(20) vlayout = QVBoxLayout() vlayout.addItem(hlayout) vlayout.addWidget(self.label1) vlayout.addWidget(self.list_widget1) vlayout.setSpacing(10) gridlayout.addItem(vlayout, 1, 0, 2, 1) hlayout1.setSpacing(10) hlayout2.setSpacing(10) gridlayout.addItem(hlayout1, 1, 1, 1, 1) gridlayout.addItem(hlayout2, 2, 1, 1, 1) hlayout = QHBoxLayout() hlayout.addWidget(self.button1) hlayout.addWidget(self.button2) hlayout.setSpacing(10) gridlayout.addItem(hlayout, 3, 0, 1, 1) self.setLayout(gridlayout) #绑定信号 self.button12.clicked.connect( lambda: self.onClickButton(self.list_widget1, self.list_widget2)) self.button21.clicked.connect( lambda: self.onClickButton(self.list_widget2, self.list_widget1)) self.button13.clicked.connect( lambda: self.onClickButton(self.list_widget1, self.list_widget3)) self.button31.clicked.connect( lambda: self.onClickButton(self.list_widget3, self.list_widget1)) self.button1.clicked.connect(self.sendSignal) self.button2.clicked.connect(self.close) def onClickButton(self, sender, reciever): try: item_list = sender.selectedItems() for item in item_list: reciever.addItem(item.text()) sender.takeItem(sender.row(item)) except Exception as e: print(e) def sendSignal(self): count1 = self.list_widget2.count() count2 = self.list_widget3.count() independ_var = [ self.list_widget2.item(i).text() for i in range(count1) ] depend_var = [self.list_widget3.item(i).text() for i in range(count2)] var_list = [independ_var, depend_var] self.signal.send(var_list) self.close()
class UnsavedFilesDialog(QDialog): def __init__(self, unsaved_files, parent=None): super().__init__(parent) self._ninja = parent self.setWindowTitle(translations.TR_IDE_CONFIRM_EXIT_TITLE) vbox = QVBoxLayout(self) self._unsave_files_list = QListWidget() self._unsave_files_list.setSelectionMode(QListWidget.ExtendedSelection) vbox.addWidget(QLabel(translations.TR_IDE_CONFIRM_EXIT_BODY)) vbox.addWidget(self._unsave_files_list) button_box = QDialogButtonBox(self) standard_icon = self.style().standardIcon btn = button_box.addButton( translations.TR_CANCEL, QDialogButtonBox.RejectRole) btn.setIcon(standard_icon(self.style().SP_DialogCloseButton)) self._btn_save_selected = button_box.addButton( translations.TR_SAVE_SELECTED, QDialogButtonBox.AcceptRole) self._btn_save_selected.setIcon( standard_icon(self.style().SP_DialogSaveButton)) btn_save_all = button_box.addButton( translations.TR_SAVE_ALL, QDialogButtonBox.AcceptRole) btn_save_all.setIcon(standard_icon(self.style().SP_DialogApplyButton)) btn_donot_save = button_box.addButton( translations.TR_DONOT_SAVE, QDialogButtonBox.DestructiveRole) btn_donot_save.setIcon(standard_icon(self.style().SP_DialogNoButton)) vbox.addWidget(button_box) for nfile in unsaved_files: item = QListWidgetItem(nfile.display_name) item.setData(Qt.UserRole, nfile) item.setToolTip(nfile.file_path) self._unsave_files_list.addItem(item) # Connections button_box.rejected.connect(self.reject) button_box.accepted.connect(self._save_selected) btn_donot_save.clicked.connect(self._discard) btn_save_all.clicked.connect(self._save_all) self._unsave_files_list.itemSelectionChanged.connect( self._on_selection_changed) self._unsave_files_list.selectAll() def _on_selection_changed(self): value = True if not self._unsave_files_list.selectedItems(): value = False self._btn_save_selected.setEnabled(value) def _save_selected(self): logger.debug("Saving selected unsaved files") self.__save() def __save(self): """Collect all selected items and save""" items_to_save = [] for item in self._unsave_files_list.selectedItems(): nfile = item.data(Qt.UserRole) items_to_save.append(nfile) self._ninja._save_unsaved_files(items_to_save) self.accept() def _save_all(self): """Select all items in the list and save""" logger.debug("Saving all unsaved files") for index in range(self._unsave_files_list.count()): item = self._unsave_files_list.item(index) item.setSelected(True) self.__save() def _discard(self): logger.debug("Discarding all unsaved files") self.accept()
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) citiesdict_signal = pyqtSignal([dict]) def __init__( self, citylist, accurate_url, appid, trans_cities_dict, parent=None ): super(CityListDlg, self).__init__(parent) self.settings = QSettings() self.citylist = citylist self.trans_cities_dict = trans_cities_dict self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.translate) cities_list = [] for i in self.citylist: cities_list.append(self.trans_cities_dict.get(i, i)) self.listWidget.addItems(cities_list) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Ok | QDialogButtonBox.Cancel ) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) self.translate_button = QPushButton( QCoreApplication.translate( 'Button', '&Translate', 'Edit cities name' ) ) buttonLayout.addWidget(self.translate_button) self.translate_button.clicked.connect(self.translate) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.setWindowTitle( QCoreApplication.translate( 'Window title', 'Cities', 'Cities list dialogue' ) ) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = ( self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd ) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText( QCoreApplication.translate( 'Status bar message', 'The city already exists in the list', 'Cities list dialogue' ) ) return else: self.listWidget.addItem(newitem) self.checklength() self.status.setText( 'ℹ ' + QCoreApplication.translate( 'Status bar message', 'Toggle cities with mouse scroll on the weather window', 'Cities list dialogue' ) ) def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 1: self.status.setText( QCoreApplication.translate( 'Message when trying to remove the' 'last and unique city in the list', 'This is the default city !', 'Cities list dialogue' ) ) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): if self.listWidget.count() == 1: # After adding the first city the entry is not activated self.listWidget.setCurrentRow(0) if self.listWidget.count() > 0: self.translate_button.setEnabled(True) self.listWidget.setMinimumWidth( self.listWidget.sizeHintForColumn(0) ) else: self.translate_button.setEnabled(False) def translate(self): city = self.listWidget.currentItem().text() dialog = citytranslate.CityTranslate( city, self.trans_cities_dict, self ) dialog.city_signal.connect(self.current_translation) if dialog.exec_() == 1: row = self.listWidget.currentRow() item = self.listWidget.takeItem(row) del item self.listWidget.insertItem(row, self.current_translated_city) self.listWidget.setCurrentRow(row) def current_translation(self, translated_city): for city, translated in translated_city.items(): if translated == '': translated = city self.trans_cities_dict[city] = translated self.current_translated_city = translated def accept(self): listtosend = [] for row in range(self.listWidget.count()): city = self.find_city_key(self.listWidget.item(row).text()) listtosend.append(city) if self.listWidget.count() == 0: return self.citieslist_signal[list].emit(listtosend) self.citiesdict_signal[dict].emit(self.trans_cities_dict) QDialog.accept(self) def find_city_key(self, city): for key, value in self.trans_cities_dict.items(): if value == city: return key return city
class SubSheet(SimpleBlackbox): author = "DrLuke" name = "Subsheet" modulename = "subsheet" Category = ["Builtin"] placeable = True implementation = SubSheetImplementation def __init__(self, *args, **kwargs): self.ownsheet = None self.sheets = None self.selectedSheet = None self.listSheetItems = {} super(SubSheet, self).__init__(*args, **kwargs) self.propertiesWidget = QWidget() self.vlayout = QVBoxLayout() self.listWidget = QListWidget() self.listWidget.itemClicked.connect(self.listClicked) self.vlayout.addWidget(self.listWidget) self.vlayout.addItem(QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.propertiesWidget.setLayout(self.vlayout) def getPropertiesWidget(self): return self.propertiesWidget def updateSheets(self): if self.sheets is not None and self.ownsheet is not None: self.listSheetItems = {} self.listWidget.clear() for sheetId in self.sheets: if not sheetId == self.ownsheet: newItem = QListWidgetItem(self.sheets[sheetId]) newItem.setToolTip(str(sheetId)) newItem.setData(Qt.UserRole, sheetId) self.listSheetItems[sheetId] = newItem self.listWidget.addItem(newItem) if sheetId == self.selectedSheet: boldFont = QFont() boldFont.setBold(True) newItem.setFont(boldFont) def listClicked(self, item): normalFont = QFont() boldFont = QFont() boldFont.setBold(True) for i in range(self.listWidget.count()): itemnormal = self.listWidget.item(i) itemnormal.setFont(normalFont) self.selectedSheet = item.data(Qt.UserRole) self.sendDataToImplementations({"subsheetid": self.selectedSheet}) item.setFont(boldFont) def serialize(self): return {"subsheetid": self.selectedSheet} def deserialize(self, data): if data is not None: if "subsheetid" in data: self.selectedSheet = data["subsheetid"] self.sendDataToImplementations({"subsheetid": self.selectedSheet}) def selectedChanged(self, state): if state: self.mainRect.setPen(QPen(Qt.red)) else: self.mainRect.setPen(QPen(Qt.blue)) def defineIO(self): self.addInput(execType, "execInit", "Execute Init") self.addInput(execType, "execLoop", "Execute Loop") self.addOutput(execType, "ExecInitOut", "Init Done") self.addOutput(execType, "ExecLoopOut", "Loop Done")
class ListEntry(BaseParamWidget): def __init__(self, layout_dir: str, title: str, initial_value: List[List[str]], new_id_func: Callable): super().__init__(layout_dir) self.choices = [] self.text_list = [] lab_title = QLabel(text=title) layout = QHBoxLayout() self.central_layout.addWidget(lab_title) self.on_check_callback = None self.list_widget = QListWidget() self.set_value(initial_value) layout_tools = QVBoxLayout() self.button_add_item = QPushButton('+') self.button_delete_item = QPushButton('-') self.button_delete_item.clicked.connect(self.delete_row) self.button_add_item.clicked.connect(self.add_row) self.button_add_item.setMaximumWidth(20) self.button_delete_item.setMaximumWidth(20) layout_tools.addWidget(self.button_add_item) layout_tools.addWidget(self.button_delete_item) layout.addLayout(layout_tools) layout.addWidget(self.list_widget) self.central_layout.addLayout(layout) self.data = initial_value self.new_id_func = new_id_func def new_id(self): if callable(self.new_id_func): return self.new_id_func() else: return None def add_row(self): self.data[0].append(self.new_id()) self.data[1].append('Unnamed') self.set_value(self.data) def delete_row(self): # item = self.list_widget.currentItem() index = self.list_widget.currentIndex().row() self.data[0].pop(index) self.data[1].pop(index) self.set_value(self.data) def on_listwidget_double_cicked(self): print('edit') item = self.list_widget.currentItem() self.list_widget.editItem(item) def get_value(self): text = [] for i in range(self.list_widget.count()): text.append(self.list_widget.item(i).text()) self.data[1] = text return self.data def set_value(self, data: List[List[str]]): self.list_widget.clear() self.list_widget.addItems(data[1]) self.data = data for index in range(self.list_widget.count()): item = self.list_widget.item(index) item.setFlags(item.flags() | Qt.ItemIsEditable)
class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowTitle("application main window") self.file_menu = QtWidgets.QMenu('&File', self) self.file_menu.addAction('&Quit', self.fileQuit, QtCore.Qt.CTRL + QtCore.Qt.Key_Q) self.menuBar().addMenu(self.file_menu) self.help_menu = QtWidgets.QMenu('&Help', self) self.menuBar().addSeparator() self.menuBar().addMenu(self.help_menu) self.help_menu.addAction('&About', self.about) self.main_widget = QtWidgets.QWidget(self) mainLayout = QtWidgets.QVBoxLayout(self.main_widget) # convas # dc = MyDynamicMplCanvas(self.main_widget, width=8, height=4, dpi=100) # mainLayout.addWidget(dc) hBoxLayout = QtWidgets.QHBoxLayout() # self.tableWidget = QTableWidget(self) # self.tableWidget.setRowCount(1) # self.tableWidget.setColumnCount(100) # self.tableWidget.resizeRowToContents(0) #self.tableWidget.setItem(0,0, QTableWidgetItem("aa")) #self.tableWidget.insertRow(1) self.listWidget = QListWidget(self) # self.listWidget.addItem(QListWidgetItem("3")) self.factorValue = QLineEdit(self) self.factorValue.setText("0.98") self.factorValue.setFixedSize(100, 30) self.factorButton = QPushButton(self) self.factorButton.setText('Change emissivity') self.factorButton.clicked.connect(self.factorButtonOnClicked) self.factorButton.setFixedSize(150, 30) self.stopButton = QPushButton(self) self.stopButton.setText('Stop') self.stopButton.clicked.connect(self.stopButtonOnClicked) self.stopButton.setFixedSize(100, 30) self.copyData = QPushButton(self) self.copyData.setText('Copy') self.copyData.clicked.connect(self.onCopyButtonCliced) self.oneMeasureButton = QPushButton(self) self.oneMeasureButton.setText('One Measure') self.oneMeasureButton.clicked.connect(self.onMeasureButtonOnClicked) # self.oneMeasureButton.setFixedSize(5,5) # self.factorMenu = QMenu(self) # self.factorMenu.frameSize() # self.factorSubMenu = self.factorMenu.addMenu("material") # self.factorSubMenu.addAction("new") # self.factorSubMenu.addAction("new2") spacer = QSpacerItem(500, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) hBoxLayout.addItem(spacer) # hBoxLayout.addWidget(self.factorMenu) mainLayout.addWidget(self.listWidget) # mainLayout.addWidget(self.tableWidget) hBoxLayout.addWidget(self.copyData) hBoxLayout.addWidget(self.factorValue) hBoxLayout.addWidget(self.factorButton) hBoxLayout.addWidget(self.stopButton) hBoxLayout.addWidget(self.oneMeasureButton) mainLayout.addLayout(hBoxLayout) self.main_widget.setFocus() self.setCentralWidget(self.main_widget) #self.statusBar().showMessage("All hail matplotlib!", 2000) def stopButtonOnClicked(self): print("\tStop button clicked") connMan = PirConnManager() connMan.sendStopCommand() def factorButtonOnClicked(self): print("\tChange emissivity factor button clicked") e = float(self.factorValue.text()) # save only when e is in range if e >= 0.1 and e <= 1.0: connMan = PirConnManager() if True == connMan.saveEmissivity(e): print("Emmisivity changed to ", e) else: print("Error whiel saving emmisivity") else: print("Aborting. Wrong emmisivity value!") def onMeasureButtonOnClicked(self): print("\tOne Measuere button clicked") connMan = PirConnManager() temp = connMan.readOneSample("object") self.listWidget.addItem(str(round(temp[0],2))+ " °C") self.listWidget.setCurrentRow( self.listWidget.count() - 1 ) # copy to clipboard # cl = QApplication.clipboard() # cl.setText("TestTest") print(temp) def onCopyButtonCliced(self): # copy to clipboard cl = QApplication.clipboard() items = [] for index in range( self.listWidget.count()): items.append( self.listWidget.item(index).text()) # print(self.listWidget.item(index).text()) cl.setText(str(items)) #cl.setText("TestTest") def fileQuit(self): self.close() def closeEvent(self, ce): print("exiting") self.fileQuit() def about(self): QtWidgets.QMessageBox.about(self, "")
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) def __init__(self, citylist, accurate_url, appid, parent=None): super(CityListDlg, self).__init__(parent) self.citylist = citylist self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.addItems(self.citylist) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = (self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText(QCoreApplication.translate('Status bar message', 'The city already exists in the list', 'Cities list dialogue')) return else: self.listWidget.addItem(newitem) self.checklength() def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 0: self.status.setText(self.tr('The list is empty')) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): listtosend = [] for row in range(self.listWidget.count()): listtosend.append(self.listWidget.item(row).text()) if len(listtosend) == 0: return self.listWidget.setMinimumWidth(self.listWidget.sizeHintForColumn(0)) def accept(self): listtosend = [] for row in range(self.listWidget.count()): listtosend.append(self.listWidget.item(row).text()) self.citieslist_signal[list].emit(listtosend) QDialog.accept(self)
class CreateChatDialog(QDialog): def __init__(self): super().__init__() self.initUI() self.created_chat = False self.participants = [] self.chat_name = None for friend in config.friends: self.unadded_friends.addItem(QListWidgetItem(friend)) def initUI(self): self.setWindowModality(Qt.ApplicationModal) self.setWindowTitle("Create Chat") self.grid = QGridLayout(self) self.chat_name_label = QLabel("Chat Name:") self.grid.addWidget(self.chat_name_label, 0, 0, 1, 1) self.chat_name_field = QLineEdit() self.grid.addWidget(self.chat_name_field, 0, 1, 1, 3) self.unadded_friends = QListWidget() self.unadded_friends.setSortingEnabled(True) self.grid.addWidget(self.unadded_friends, 1, 0, 4, 2) self.added_friends = QListWidget() self.added_friends.setSortingEnabled(True) self.grid.addWidget(self.added_friends, 1, 3, 4, 1) self.add_friend_button = QPushButton(">>") self.add_friend_button.pressed.connect(self.addFriend) self.grid.addWidget(self.add_friend_button, 2, 2, 1, 1) self.remove_friend_button = QPushButton("<<") self.remove_friend_button.pressed.connect(self.removeFriend) self.grid.addWidget(self.remove_friend_button, 3, 2, 1, 1) self.create_button = QPushButton("Create") self.create_button.pressed.connect(self.createChat) self.grid.addWidget(self.create_button, 5, 0, 1, 4) self.cancel_button = QPushButton("Cancel") self.cancel_button.pressed.connect(self.cancel) self.grid.addWidget(self.cancel_button, 6, 0, 1, 4) def addFriend(self): selected_friends = self.unadded_friends.selectedItems() for friend in selected_friends: self.unadded_friends.takeItem(self.unadded_friends.row(friend)) self.added_friends.addItem(friend) def removeFriend(self): selected_friends = self.added_friends.selectedItems() for friend in selected_friends: self.added_friends.takeItem(self.added_friends.row(friend)) self.unadded_friends.addItem(friend) def createChat(self): self.created_chat = True self.chat_name = self.chat_name_field.text() for i in range(self.added_friends.count()): self.participants.append(self.added_friends.item(i).text()) config.chats[self.chat_name] = {"participants": self.participants} self.close() def cancel(self): self.close()
class MainScreen(QMainWindow): """The Main GUI layout and callbacks. """ def __init__(self) -> None: logging.info("Initialize MainScreen") super().__init__(parent=None) self._traffic: Optional[Traffic] = None self._tview: Optional[Traffic] = None self.decoder: Optional[ModeS_Decoder] = None self.updateTraffic: Optional[UpdateTraffic] = None self.airspace_ready: bool = False self.last_interact: datetime = datetime.now() airspace_init = AirspaceInitCache(self) airspace_init.start() self.setWindowTitle("traffic") self.setGeometry(10, 10, 920, 720) self.set_icons() self.set_layout() self.set_design() self.set_callbacks() def __del__(self) -> None: if self.updateTraffic is not None: self.updateTraffic.terminate() if self.decoder is not None: self.decoder.stop() @property def traffic(self) -> Optional[Traffic]: self.last_interact = datetime.now() if self.decoder is not None: self._traffic = self.decoder.traffic if self._traffic is None: return None self.set_time_range() # self.set_float_columns() # max_alt = 100 * self.altitude_slider.value() # max_time = self.dates[self.time_slider.value()] # self.on_filter(max_alt, max_time) return self._traffic def set_callbacks(self) -> None: self.airport_button.clicked.connect(self.on_plot_airport) self.altitude_slider.sliderMoved.connect(self.on_altitude_moved) self.altitude_slider.sliderReleased.connect(self.on_select) self.area_input.textEdited.connect(self.on_area_input) self.area_select.itemSelectionChanged.connect(self.on_area_select) self.extent_button.clicked.connect(self.on_extent_button) self.identifier_input.textEdited.connect(self.on_id_input) self.identifier_select.itemSelectionChanged.connect(self.on_id_change) self.open_dropdown.activated.connect(self.on_open) self.plot_button.clicked.connect(self.on_plot_button) self.projection_dropdown.activated.connect(self.make_map) self.reset_button.clicked.connect(self.on_clear_button) self.time_slider.sliderMoved.connect(self.on_time_moved) self.time_slider.sliderReleased.connect(self.on_select) self.y_selector.itemSelectionChanged.connect(self.on_id_change) self.sec_y_selector.itemSelectionChanged.connect(self.on_id_change) def set_design(self) -> None: self.open_dropdown.setMaximumWidth(400) self.projection_dropdown.setMaximumWidth(400) self.altitude_description.setMinimumWidth(100) self.altitude_description.setMaximumWidth(100) self.altitude_slider_info.setMinimumWidth(50) self.altitude_slider_info.setMaximumWidth(50) self.altitude_slider.setMaximumWidth(240) self.area_input_description.setMinimumWidth(100) self.area_input_description.setMaximumWidth(100) self.area_input.setMaximumWidth(290) self.area_select.setMaximumHeight(100) self.area_select.setMaximumWidth(400) self.identifier_description.setMinimumWidth(100) self.identifier_description.setMaximumWidth(100) self.identifier_input.setMaximumWidth(290) self.identifier_select.setMaximumWidth(400) self.time_description.setMinimumWidth(100) self.time_description.setMaximumWidth(100) self.time_slider_info.setMinimumWidth(50) self.time_slider_info.setMaximumWidth(50) self.time_slider.setMaximumWidth(240) self.y_selector.setMaximumHeight(100) self.sec_y_selector.setMaximumHeight(100) def set_layout(self) -> None: self.plot_tabs = QTabWidget() map_tab = QWidget() map_layout = QVBoxLayout() map_tab.setLayout(map_layout) self.map_plot = MapCanvas(parent=self, width=5, height=4) self.map_plot.move(0, 0) self.time_plot = TimeCanvas(parent=self, width=5, height=4) self.time_plot.move(0, 0) map_toolbar = NavigationToolbar2QT(self.map_plot, map_tab) map_toolbar.setVisible(False) map_layout.addWidget(map_toolbar) map_layout.addWidget(self.map_plot) map_toolbar.pan() time_tab = QWidget() time_layout = QVBoxLayout() time_tab.setLayout(time_layout) self.y_selector = QListWidget() self.sec_y_selector = QListWidget() self.y_selector.setSelectionMode(3) # extended selection self.sec_y_selector.setSelectionMode(3) # extended selection selector = QHBoxLayout() selector.addWidget(self.y_selector) selector.addWidget(self.sec_y_selector) time_layout.addLayout(selector) time_layout.addWidget(self.time_plot) self.plot_tabs.addTab(map_tab, "Map") self.plot_tabs.addTab(time_tab, "Plots") plot_column = QVBoxLayout() plot_column.addWidget(self.plot_tabs) self.interact_column = QVBoxLayout() self.open_options = ["Open file", "dump1090"] if "decoders" in config: self.open_options += list(config["decoders"]) self.open_dropdown = QComboBox() for option in self.open_options: self.open_dropdown.addItem(option) self.interact_column.addWidget(self.open_dropdown) self.projections = ["EuroPP", "Lambert93", "Mercator"] self.projection_dropdown = QComboBox() more_projs = config.get("projections", "extra", fallback="") if more_projs != "": proj_list = more_projs.split(";") self.projections += list(x.strip() for x in proj_list) for proj in self.projections: self.projection_dropdown.addItem(proj) self.interact_column.addWidget(self.projection_dropdown) button_grid = QGridLayout() self.extent_button = QPushButton("Extent") button_grid.addWidget(self.extent_button, 0, 0) self.plot_button = QPushButton("Plot") button_grid.addWidget(self.plot_button, 0, 1) self.airport_button = QPushButton("Airport") button_grid.addWidget(self.airport_button, 1, 0) self.reset_button = QPushButton("Reset") button_grid.addWidget(self.reset_button, 1, 1) self.interact_column.addLayout(button_grid) self.area_input_description = QLabel("Area") self.area_input = QLineEdit() area_input_layout = QHBoxLayout() area_input_layout.addWidget(self.area_input_description) area_input_layout.addWidget(self.area_input) self.interact_column.addLayout(area_input_layout) self.area_select = QListWidget() self.interact_column.addWidget(self.area_select) self.time_slider = QSlider(QtCore.Qt.Horizontal) self.time_description = QLabel("Date max.") self.time_slider_info = QLabel() time_layout = QHBoxLayout() time_layout.addWidget(self.time_description) time_layout.addWidget(self.time_slider) time_layout.addWidget(self.time_slider_info) self.time_slider.setMinimum(0) self.time_slider.setMaximum(99) self.time_slider.setValue(99) self.time_slider.setEnabled(False) self.interact_column.addLayout(time_layout) self.altitude_slider = QSlider(QtCore.Qt.Horizontal) self.altitude_description = QLabel("Altitude max.") self.altitude_slider_info = QLabel("60000") self.altitude_slider.setSingleStep(5) self.altitude_slider.setPageStep(100) self.altitude_slider.setMinimum(0) self.altitude_slider.setMaximum(600) self.altitude_slider.setValue(600) altitude_layout = QHBoxLayout() altitude_layout.addWidget(self.altitude_description) altitude_layout.addWidget(self.altitude_slider) altitude_layout.addWidget(self.altitude_slider_info) self.interact_column.addLayout(altitude_layout) self.identifier_description = QLabel("Callsign/ID") self.identifier_input = QLineEdit() identifier_layout = QHBoxLayout() identifier_layout.addWidget(self.identifier_description) identifier_layout.addWidget(self.identifier_input) self.interact_column.addLayout(identifier_layout) self.identifier_select = QListWidget() self.identifier_select.setSelectionMode(3) # extended selection self.interact_column.addWidget(self.identifier_select) mainLayout = QGridLayout() mainLayout.addLayout(plot_column, 0, 0) mainLayout.addLayout(self.interact_column, 0, 1) mainWidget = QWidget() mainWidget.setLayout(mainLayout) self.setCentralWidget(mainWidget) # -- Callbacks -- @dont_crash def on_time_moved(self, value: int, *args, **kwargs) -> None: self.last_interact = datetime.now() self.time_slider_info.setText(self.date_options[value]) @dont_crash def on_altitude_moved(self, value: int, *args, **kwargs) -> None: self.last_interact = datetime.now() self.altitude_slider_info.setText(f"{100*value}") @dont_crash def on_select(self, *args, **kwargs) -> None: self.last_interact = datetime.now() if self.traffic is not None: max_alt = 100 * self.altitude_slider.value() max_time = self.dates[self.time_slider.value()] self.on_filter(max_alt, max_time) @dont_crash def on_filter(self, max_alt: int, max_time: datetime) -> None: assert self._traffic is not None west, east, south, north = self.map_plot.ax.get_extent( crs=PlateCarree()) self._tview = self._traffic.before(max_time).sort_values("timestamp") if self._tview is None: return filtered = Traffic.from_flights( Flight(f.data.ffill().bfill()) for f in self._tview) if "altitude" in filtered.data.columns: filtered = filtered.query( f"altitude != altitude or altitude <= {max_alt}") if "latitude" in self._tview.data.columns: filtered = filtered.query("latitude != latitude or " f"({west} <= longitude <= {east} and " f"{south} <= latitude <= {north})") self.identifier_select.clear() text = self.identifier_input.text() # cast is necessary because of the @lru_cache on callsigns which hides # the type annotation for callsign in sorted(cast(Set[str], filtered.callsigns)): if re.match(text, callsign, flags=re.IGNORECASE): self.identifier_select.addItem(callsign) callsigns = cast(Set[str], filtered.callsigns) self.map_plot.default_plot(self._tview[callsigns]) self.set_float_columns() @dont_crash def on_extent_button(self, *args, **kwargs) -> None: self.last_interact = datetime.now() if self.area_select.count() == 0: if len(self.area_input.text()) == 0: self.map_plot.ax.set_global() else: self.map_plot.ax.set_extent(location(self.area_input.text())) else: if self.airspace_ready: self.map_plot.ax.set_extent( aixm_airspaces[self.area_select.item(0).text()]) self.map_plot.draw() if self.traffic is not None: max_alt = 100 * self.altitude_slider.value() max_time = self.dates[self.time_slider.value()] self.on_filter(max_alt, max_time) @dont_crash def on_id_change(self, *args, **kwargs) -> None: assert self._tview is not None self.last_interact = datetime.now() list_callsigns = list( item.text() for item in self.identifier_select.selectedItems()) selected_y = list(item.text() for item in self.y_selector.selectedItems()) selected_sec_y = list(item.text() for item in self.sec_y_selector.selectedItems()) self.map_plot.plot_callsigns(self._tview, list_callsigns) self.time_plot.create_plot() self.time_plot.plot_callsigns( self._tview, list_callsigns, y=selected_y + selected_sec_y, secondary_y=selected_sec_y, ) @dont_crash def on_id_input(self, text, *args, **kwargs) -> None: assert self._tview is not None self.last_interact = datetime.now() # segfault prone when interactive (decoder) # selected = list( # item.text() for item in self.identifier_select.selectedItems() # ) self.identifier_select.clear() for callsign in sorted(cast(Set[str], self._tview.callsigns)): if re.match(text, callsign, flags=re.IGNORECASE): self.identifier_select.addItem(callsign) # if callsign in selected: # curItem = self.identifier_select.item( # self.identifier_select.count() - 1 # ) # self.identifier_select.setItemSelected(curItem, True) @dont_crash def on_plot_button(self, *args, **kwargs) -> None: assert self._tview is not None self.last_interact = datetime.now() if self.area_select.count() == 0: if len(self.area_input.text()) == 0: self.map_plot.default_plot(self._tview) else: location(self.area_input.text()).plot( self.map_plot.ax, color="#524c50", linestyle="dotted", linewidth=0.5, ) else: if self.airspace_ready: selected = self.area_select.selectedItems() if len(selected) == 0: return airspace = aixm_airspaces[selected[0].text()] if airspace is not None: airspace.plot(self.map_plot.ax) self.map_plot.draw() @dont_crash def on_area_input(self, text: str, *args, **kwargs) -> None: self.last_interact = datetime.now() self.area_select.clear() if len(text) > 0 and self.airspace_ready: for airspace_info in aixm_airspaces.parse(text): self.area_select.addItem(airspace_info.name) @dont_crash def on_area_select(self, *args, **kwargs) -> None: self.last_interact = datetime.now() selected = self.area_select.selectedItems() if len(selected) == 0: return if self.airspace_ready: airspace = aixm_airspaces[selected[0].text()] if airspace is not None: self.map_plot.ax.set_extent(airspace) self.map_plot.draw() @dont_crash def on_plot_airport(self, *args, **kwargs) -> None: self.last_interact = datetime.now() if len(self.area_input.text()) == 0: from cartotools.osm import request, tags west, east, south, north = self.map_plot.ax.get_extent( crs=PlateCarree()) if abs(east - west) > 1 or abs(north - south) > 1: # that would be a too big request return request((west, south, east, north), **tags.airport).plot(self.map_plot.ax) else: from traffic.data import airports airport = airports[self.area_input.text()] if airport is not None: airport.plot(self.map_plot.ax) self.map_plot.draw() @dont_crash def on_clear_button(self, *args, **kwargs) -> None: self.last_interact = datetime.now() if self.traffic is not None: assert self._traffic is not None self._tview = self._traffic.sort_values("timestamp") self.altitude_slider.setValue(600) self.make_map(self.projection_dropdown.currentIndex()) self.time_plot.create_plot() self.set_float_columns() @dont_crash def make_map(self, index_projection: int, *args, **kwargs) -> None: self.last_interact = datetime.now() self.map_plot.create_map(self.projections[index_projection]) if self._tview is not None: self.map_plot.default_plot(self._tview) @dont_crash def on_open(self, index: int, *args, **kwargs) -> None: if self.decoder is not None and self.updateTraffic is not None: self.updateTraffic.terminate() self.decoder.stop() if index == 0: self.openFile() elif index == 1: self.openDump1090() else: address = config.get("decoders", self.open_options[index]) host_port, reference = address.split("/") host, port = host_port.split(":") self.decoder = ModeS_Decoder.from_address(host, int(port), reference) refresh_time = config.getint("decoders", "refresh_time", fallback=30) self.updateTraffic = UpdateTraffic(self, refresh_time) self.updateTraffic.start() # -- Basic setters -- def set_icons(self) -> None: logging.info("Setting options") icon_path = Path(traffic.__file__).absolute().parent.parent / "icons" if sys.platform == "linux": icon_full = QtGui.QIcon( (icon_path / "travel-beige.svg").as_posix()) elif sys.platform == "darwin": icon_full = QtGui.QIcon((icon_path / "travel-grey.svg").as_posix()) else: icon_full = QtGui.QIcon( (icon_path / "travel-orange.svg").as_posix()) self.setWindowIcon(icon_full) def set_time_range(self) -> None: assert self._traffic is not None self.time_slider.setEnabled(True) start_time = cast(pd.Timestamp, self._traffic.start_time) end_time = cast(pd.Timestamp, self._traffic.end_time) self.dates = [ start_time + i * (end_time - start_time) / 99 for i in range(100) ] tz_now = datetime.now().astimezone().tzinfo if cast(pd.Timestamp, self._traffic.start_time).tzinfo is not None: self.date_options = [ t.tz_convert("utc").strftime("%H:%M") for t in self.dates ] else: self.date_options = [ t.tz_localize(tz_now).tz_convert("utc").strftime("%H:%M") for t in self.dates ] self.time_slider_info.setText(self.date_options[-1]) def set_float_columns(self) -> None: assert self._traffic is not None self.y_selector.clear() self.sec_y_selector.clear() for column, dtype in self._traffic.data.dtypes.items(): if column not in ("latitude", "longitude"): if dtype in ["float64", "int64"]: self.y_selector.addItem(column) self.sec_y_selector.addItem(column) def openDump1090(self) -> None: reference, ok = QInputDialog.getText(self, "dump1090 reference", "Reference airport:") if ok: self.open_dropdown.setItemText(1, f"dump1090 ({reference})") self.decoder = ModeS_Decoder.from_dump1090(reference) refresh_time = config.getint("decoders", "refresh_time", fallback=30) self.updateTraffic = UpdateTraffic(self, refresh_time) self.updateTraffic.start() @dont_crash def openFile(self, *args, **kwargs) -> None: options = { "caption": "Open file", "filter": ("Pandas DataFrame (*.pkl);;" "CSV files (*.csv);;" "Sqlite3 files (*.db)"), # "filter": "Data files (*.csv *.pkl)", "directory": os.path.expanduser("~"), } self.filename = QFileDialog.getOpenFileName(self, **options)[0] if self.filename == "": return self.filename = Path(self.filename) self._traffic = Traffic.from_file(self.filename) assert self._traffic is not None self._tview = self._traffic.sort_values("timestamp") assert self._tview is not None self.open_dropdown.setItemText(0, self.filename.name) self.map_plot.default_plot(self._tview) self.identifier_select.clear() for callsign in sorted(cast(Set[str], self._tview.callsigns)): self.identifier_select.addItem(callsign) self.set_time_range() self.set_float_columns()
class SettingsDialog(QDialog): worker = None config = None configfile = None saved = QtCore.pyqtSignal() def __init__(self, parent, worker, config, configfile): QDialog.__init__(self, parent) self.worker = worker self.config = config self.configfile = configfile self.setStyleSheet("QGroupBox { font-weight: bold; } ") self.setWindowTitle('Settings') layout = QGridLayout() # Categories self.category_list = QListWidget() category_media = QListWidgetItem(getIcon('media-playback-start'), 'Media', self.category_list) category_sync = QListWidgetItem(getIcon('view-refresh'), 'Sync', self.category_list) category_ui = QListWidgetItem(getIcon('window-new'), 'User Interface', self.category_list) category_theme = QListWidgetItem(getIcon('applications-graphics'), 'Theme', self.category_list) self.category_list.setSelectionMode(QAbstractItemView.SingleSelection) self.category_list.setCurrentRow(0) self.category_list.setMaximumWidth(self.category_list.sizeHintForColumn(0) + 15) self.category_list.setFocus() self.category_list.currentItemChanged.connect(self.s_switch_page) # Media tab page_media = QWidget() page_media_layout = QVBoxLayout() page_media_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Media settings g_media = QGroupBox('Media settings') g_media.setFlat(True) g_media_layout = QFormLayout() self.tracker_enabled = QCheckBox() self.tracker_enabled.toggled.connect(self.tracker_type_change) self.tracker_type = QComboBox() for (n, label) in utils.available_trackers: self.tracker_type.addItem(label, n) self.tracker_type.currentIndexChanged.connect(self.tracker_type_change) self.tracker_interval = QSpinBox() self.tracker_interval.setRange(5, 1000) self.tracker_interval.setMaximumWidth(60) self.tracker_process = QLineEdit() self.tracker_update_wait = QSpinBox() self.tracker_update_wait.setRange(0, 1000) self.tracker_update_wait.setMaximumWidth(60) self.tracker_update_close = QCheckBox() self.tracker_update_prompt = QCheckBox() self.tracker_not_found_prompt = QCheckBox() g_media_layout.addRow('Enable tracker', self.tracker_enabled) g_media_layout.addRow('Tracker type', self.tracker_type) g_media_layout.addRow('Tracker interval (seconds)', self.tracker_interval) g_media_layout.addRow('Process name (regex)', self.tracker_process) g_media_layout.addRow('Wait before updating (seconds)', self.tracker_update_wait) g_media_layout.addRow('Wait until the player is closed', self.tracker_update_close) g_media_layout.addRow('Ask before updating', self.tracker_update_prompt) g_media_layout.addRow('Ask to add new shows', self.tracker_not_found_prompt) g_media.setLayout(g_media_layout) # Group: Plex settings g_plex = QGroupBox('Plex Media Server') g_plex.setFlat(True) self.plex_host = QLineEdit() self.plex_port = QLineEdit() self.plex_user = QLineEdit() self.plex_passw = QLineEdit() self.plex_passw.setEchoMode(QLineEdit.Password) self.plex_obey_wait = QCheckBox() g_plex_layout = QGridLayout() g_plex_layout.addWidget(QLabel('Host and Port'), 0, 0, 1, 1) g_plex_layout.addWidget(self.plex_host, 0, 1, 1, 1) g_plex_layout.addWidget(self.plex_port, 0, 2, 1, 2) g_plex_layout.addWidget(QLabel('Use "wait before updating" time'), 1, 0, 1, 1) g_plex_layout.addWidget(self.plex_obey_wait, 1, 2, 1, 1) g_plex_layout.addWidget(QLabel('myPlex login (claimed server)'), 2, 0, 1, 1) g_plex_layout.addWidget(self.plex_user, 2, 1, 1, 1) g_plex_layout.addWidget(self.plex_passw, 2, 2, 1, 2) g_plex.setLayout(g_plex_layout) # Group: Library g_playnext = QGroupBox('Library') g_playnext.setFlat(True) self.player = QLineEdit() self.player_browse = QPushButton('Browse...') self.player_browse.clicked.connect(self.s_player_browse) lbl_searchdirs = QLabel('Media directories') lbl_searchdirs.setAlignment(QtCore.Qt.AlignTop) self.searchdirs = QListWidget() self.searchdirs_add = QPushButton('Add...') self.searchdirs_add.clicked.connect(self.s_searchdirs_add) self.searchdirs_remove = QPushButton('Remove') self.searchdirs_remove.clicked.connect(self.s_searchdirs_remove) self.searchdirs_buttons = QVBoxLayout() self.searchdirs_buttons.setAlignment(QtCore.Qt.AlignTop) self.searchdirs_buttons.addWidget(self.searchdirs_add) self.searchdirs_buttons.addWidget(self.searchdirs_remove) self.searchdirs_buttons.addWidget(QSplitter()) self.library_autoscan = QCheckBox() self.scan_whole_list = QCheckBox() self.library_full_path = QCheckBox() g_playnext_layout = QGridLayout() g_playnext_layout.addWidget(QLabel('Player'), 0, 0, 1, 1) g_playnext_layout.addWidget(self.player, 0, 1, 1, 1) g_playnext_layout.addWidget(self.player_browse, 0, 2, 1, 1) g_playnext_layout.addWidget(lbl_searchdirs, 1, 0, 1, 1) g_playnext_layout.addWidget(self.searchdirs, 1, 1, 1, 1) g_playnext_layout.addLayout(self.searchdirs_buttons, 1, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Rescan Library at startup'), 2, 0, 1, 2) g_playnext_layout.addWidget(self.library_autoscan, 2, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Scan through whole list'), 3, 0, 1, 2) g_playnext_layout.addWidget(self.scan_whole_list, 3, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Take subdirectory name into account'), 4, 0, 1, 2) g_playnext_layout.addWidget(self.library_full_path, 4, 2, 1, 1) g_playnext.setLayout(g_playnext_layout) # Media form page_media_layout.addWidget(g_media) page_media_layout.addWidget(g_plex) page_media_layout.addWidget(g_playnext) page_media.setLayout(page_media_layout) # Sync tab page_sync = QWidget() page_sync_layout = QVBoxLayout() page_sync_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Autoretrieve g_autoretrieve = QGroupBox('Autoretrieve') g_autoretrieve.setFlat(True) self.autoretrieve_off = QRadioButton('Disabled') self.autoretrieve_always = QRadioButton('Always at start') self.autoretrieve_days = QRadioButton('After n days') self.autoretrieve_days.toggled.connect(self.s_autoretrieve_days) self.autoretrieve_days_n = QSpinBox() self.autoretrieve_days_n.setRange(1, 100) g_autoretrieve_layout = QGridLayout() g_autoretrieve_layout.setColumnStretch(0, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_off, 0, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_always, 1, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_days, 2, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_days_n, 2, 1, 1, 1) g_autoretrieve.setLayout(g_autoretrieve_layout) # Group: Autosend g_autosend = QGroupBox('Autosend') g_autosend.setFlat(True) self.autosend_off = QRadioButton('Disabled') self.autosend_always = QRadioButton('Immediately after every change') self.autosend_minutes = QRadioButton('After n minutes') self.autosend_minutes.toggled.connect(self.s_autosend_minutes) self.autosend_minutes_n = QSpinBox() self.autosend_minutes_n.setRange(1, 1000) self.autosend_size = QRadioButton('After the queue reaches n items') self.autosend_size.toggled.connect(self.s_autosend_size) self.autosend_size_n = QSpinBox() self.autosend_size_n.setRange(2, 20) self.autosend_at_exit = QCheckBox('At exit') g_autosend_layout = QGridLayout() g_autosend_layout.setColumnStretch(0, 1) g_autosend_layout.addWidget(self.autosend_off, 0, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_always, 1, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_minutes, 2, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_minutes_n, 2, 1, 1, 1) g_autosend_layout.addWidget(self.autosend_size, 3, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_size_n, 3, 1, 1, 1) g_autosend_layout.addWidget(self.autosend_at_exit, 4, 0, 1, 1) g_autosend.setLayout(g_autosend_layout) # Group: Extra g_extra = QGroupBox('Additional options') g_extra.setFlat(True) self.auto_status_change = QCheckBox('Change status automatically') self.auto_status_change.toggled.connect(self.s_auto_status_change) self.auto_status_change_if_scored = QCheckBox('Change status automatically only if scored') self.auto_date_change = QCheckBox('Change start and finish dates automatically') g_extra_layout = QVBoxLayout() g_extra_layout.addWidget(self.auto_status_change) g_extra_layout.addWidget(self.auto_status_change_if_scored) g_extra_layout.addWidget(self.auto_date_change) g_extra.setLayout(g_extra_layout) # Sync layout page_sync_layout.addWidget(g_autoretrieve) page_sync_layout.addWidget(g_autosend) page_sync_layout.addWidget(g_extra) page_sync.setLayout(page_sync_layout) # UI tab page_ui = QWidget() page_ui_layout = QFormLayout() page_ui_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Icon g_icon = QGroupBox('Notification Icon') g_icon.setFlat(True) self.tray_icon = QCheckBox('Show tray icon') self.tray_icon.toggled.connect(self.s_tray_icon) self.close_to_tray = QCheckBox('Close to tray') self.start_in_tray = QCheckBox('Start minimized to tray') self.tray_api_icon = QCheckBox('Use API icon as tray icon') self.notifications = QCheckBox('Show notification when tracker detects new media') g_icon_layout = QVBoxLayout() g_icon_layout.addWidget(self.tray_icon) g_icon_layout.addWidget(self.close_to_tray) g_icon_layout.addWidget(self.start_in_tray) g_icon_layout.addWidget(self.tray_api_icon) g_icon_layout.addWidget(self.notifications) g_icon.setLayout(g_icon_layout) # Group: Window g_window = QGroupBox('Window') g_window.setFlat(True) self.remember_geometry = QCheckBox('Remember window size and position') self.remember_columns = QCheckBox('Remember column layouts and widths') self.columns_per_api = QCheckBox('Use different visible columns per API') g_window_layout = QVBoxLayout() g_window_layout.addWidget(self.remember_geometry) g_window_layout.addWidget(self.remember_columns) g_window_layout.addWidget(self.columns_per_api) g_window.setLayout(g_window_layout) # Group: Lists g_lists = QGroupBox('Lists') g_lists.setFlat(True) self.filter_bar_position = QComboBox() filter_bar_positions = [(FilterBar.PositionHidden, 'Hidden'), (FilterBar.PositionAboveLists, 'Above lists'), (FilterBar.PositionBelowLists, 'Below lists')] for (n, label) in filter_bar_positions: self.filter_bar_position.addItem(label, n) self.inline_edit = QCheckBox('Enable in-line editing') g_lists_layout = QFormLayout() g_lists_layout.addRow('Filter bar position:', self.filter_bar_position) g_lists_layout.addRow(self.inline_edit) g_lists.setLayout(g_lists_layout) # UI layout page_ui_layout.addWidget(g_icon) page_ui_layout.addWidget(g_window) page_ui_layout.addWidget(g_lists) page_ui.setLayout(page_ui_layout) # Theming tab page_theme = QWidget() page_theme_layout = QFormLayout() page_theme_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Episode Bar g_ep_bar = QGroupBox('Episode Bar') g_ep_bar.setFlat(True) self.ep_bar_style = QComboBox() ep_bar_styles = [(ShowsTableDelegate.BarStyleBasic, 'Basic'), (ShowsTableDelegate.BarStyle04, 'Trackma'), (ShowsTableDelegate.BarStyleHybrid, 'Hybrid')] for (n, label) in ep_bar_styles: self.ep_bar_style.addItem(label, n) self.ep_bar_style.currentIndexChanged.connect(self.s_ep_bar_style) self.ep_bar_text = QCheckBox('Show text label') g_ep_bar_layout = QFormLayout() g_ep_bar_layout.addRow('Style:', self.ep_bar_style) g_ep_bar_layout.addRow(self.ep_bar_text) g_ep_bar.setLayout(g_ep_bar_layout) # Group: Colour scheme g_scheme = QGroupBox('Color Scheme') g_scheme.setFlat(True) col_tabs = [('rows', '&Row highlights'), ('progress', '&Progress widget')] self.colors = {} self.colors['rows'] = [('is_playing', 'Playing'), ('is_queued', 'Queued'), ('new_episode', 'New Episode'), ('is_airing', 'Airing'), ('not_aired', 'Unaired')] self.colors['progress'] = [('progress_bg', 'Background'), ('progress_fg', 'Watched bar'), ('progress_sub_bg', 'Aired episodes'), ('progress_sub_fg', 'Stored episodes'), ('progress_complete', 'Complete')] self.color_buttons = [] self.syscolor_buttons = [] g_scheme_layout = QGridLayout() tw_scheme = QTabWidget() for (key, tab_title) in col_tabs: page = QFrame() page_layout = QGridLayout() col = 0 # Generate widgets from the keys and values for (key, label) in self.colors[key]: self.color_buttons.append(QPushButton()) # self.color_buttons[-1].setStyleSheet('background-color: ' + getColor(self.config['colors'][key]).name()) self.color_buttons[-1].setFocusPolicy(QtCore.Qt.NoFocus) self.color_buttons[-1].clicked.connect(self.s_color_picker(key, False)) self.syscolor_buttons.append(QPushButton('System Colors')) self.syscolor_buttons[-1].clicked.connect(self.s_color_picker(key, True)) page_layout.addWidget(QLabel(label), col, 0, 1, 1) page_layout.addWidget(self.color_buttons[-1], col, 1, 1, 1) page_layout.addWidget(self.syscolor_buttons[-1], col, 2, 1, 1) col += 1 page.setLayout(page_layout) tw_scheme.addTab(page, tab_title) g_scheme_layout.addWidget(tw_scheme) g_scheme.setLayout(g_scheme_layout) # UI layout page_theme_layout.addWidget(g_ep_bar) page_theme_layout.addWidget(g_scheme) page_theme.setLayout(page_theme_layout) # Content self.contents = QStackedWidget() self.contents.addWidget(page_media) self.contents.addWidget(page_sync) self.contents.addWidget(page_ui) self.contents.addWidget(page_theme) if pyqt_version is not 5: self.contents.layout().setMargin(0) # Bottom buttons bottombox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel ) bottombox.accepted.connect(self.s_save) bottombox.button(QDialogButtonBox.Apply).clicked.connect(self._save) bottombox.rejected.connect(self.reject) # Main layout finish layout.addWidget(self.category_list, 0, 0, 1, 1) layout.addWidget(self.contents, 0, 1, 1, 1) layout.addWidget(bottombox, 1, 0, 1, 2) layout.setColumnStretch(1, 1) self._load() self.update_colors() self.setLayout(layout) def _add_dir(self, path): self.searchdirs.addItem(path) def _load(self): engine = self.worker.engine tracker_type = self.tracker_type.findData(engine.get_config('tracker_type')) autoretrieve = engine.get_config('autoretrieve') autosend = engine.get_config('autosend') self.tracker_enabled.setChecked(engine.get_config('tracker_enabled')) self.tracker_type.setCurrentIndex(max(0, tracker_type)) self.tracker_interval.setValue(engine.get_config('tracker_interval')) self.tracker_process.setText(engine.get_config('tracker_process')) self.tracker_update_wait.setValue(engine.get_config('tracker_update_wait_s')) self.tracker_update_close.setChecked(engine.get_config('tracker_update_close')) self.tracker_update_prompt.setChecked(engine.get_config('tracker_update_prompt')) self.tracker_not_found_prompt.setChecked(engine.get_config('tracker_not_found_prompt')) self.player.setText(engine.get_config('player')) self.library_autoscan.setChecked(engine.get_config('library_autoscan')) self.scan_whole_list.setChecked(engine.get_config('scan_whole_list')) self.library_full_path.setChecked(engine.get_config('library_full_path')) self.plex_host.setText(engine.get_config('plex_host')) self.plex_port.setText(engine.get_config('plex_port')) self.plex_obey_wait.setChecked(engine.get_config('plex_obey_update_wait_s')) self.plex_user.setText(engine.get_config('plex_user')) self.plex_passw.setText(engine.get_config('plex_passwd')) for path in engine.get_config('searchdir'): self._add_dir(path) if autoretrieve == 'always': self.autoretrieve_always.setChecked(True) elif autoretrieve == 'days': self.autoretrieve_days.setChecked(True) else: self.autoretrieve_off.setChecked(True) self.autoretrieve_days_n.setValue(engine.get_config('autoretrieve_days')) if autosend == 'always': self.autosend_always.setChecked(True) elif autosend in ('minutes', 'hours'): self.autosend_minutes.setChecked(True) elif autosend == 'size': self.autosend_size.setChecked(True) else: self.autosend_off.setChecked(True) self.autosend_minutes_n.setValue(engine.get_config('autosend_minutes')) self.autosend_size_n.setValue(engine.get_config('autosend_size')) self.autosend_at_exit.setChecked(engine.get_config('autosend_at_exit')) self.auto_status_change.setChecked(engine.get_config('auto_status_change')) self.auto_status_change_if_scored.setChecked(engine.get_config('auto_status_change_if_scored')) self.auto_date_change.setChecked(engine.get_config('auto_date_change')) self.tray_icon.setChecked(self.config['show_tray']) self.close_to_tray.setChecked(self.config['close_to_tray']) self.start_in_tray.setChecked(self.config['start_in_tray']) self.tray_api_icon.setChecked(self.config['tray_api_icon']) self.notifications.setChecked(self.config['notifications']) self.remember_geometry.setChecked(self.config['remember_geometry']) self.remember_columns.setChecked(self.config['remember_columns']) self.columns_per_api.setChecked(self.config['columns_per_api']) self.filter_bar_position.setCurrentIndex(self.filter_bar_position.findData(self.config['filter_bar_position'])) self.inline_edit.setChecked(self.config['inline_edit']) self.ep_bar_style.setCurrentIndex(self.ep_bar_style.findData(self.config['episodebar_style'])) self.ep_bar_text.setChecked(self.config['episodebar_text']) self.autoretrieve_days_n.setEnabled(self.autoretrieve_days.isChecked()) self.autosend_minutes_n.setEnabled(self.autosend_minutes.isChecked()) self.autosend_size_n.setEnabled(self.autosend_size.isChecked()) self.close_to_tray.setEnabled(self.tray_icon.isChecked()) self.start_in_tray.setEnabled(self.tray_icon.isChecked()) self.notifications.setEnabled(self.tray_icon.isChecked()) self.color_values = self.config['colors'].copy() self.tracker_type_change(None) def _save(self): engine = self.worker.engine engine.set_config('tracker_enabled', self.tracker_enabled.isChecked()) engine.set_config('tracker_type', self.tracker_type.itemData(self.tracker_type.currentIndex())) engine.set_config('tracker_interval', self.tracker_interval.value()) engine.set_config('tracker_process', str(self.tracker_process.text())) engine.set_config('tracker_update_wait_s', self.tracker_update_wait.value()) engine.set_config('tracker_update_close', self.tracker_update_close.isChecked()) engine.set_config('tracker_update_prompt', self.tracker_update_prompt.isChecked()) engine.set_config('tracker_not_found_prompt', self.tracker_not_found_prompt.isChecked()) engine.set_config('player', self.player.text()) engine.set_config('library_autoscan', self.library_autoscan.isChecked()) engine.set_config('scan_whole_list', self.scan_whole_list.isChecked()) engine.set_config('library_full_path', self.library_full_path.isChecked()) engine.set_config('plex_host', self.plex_host.text()) engine.set_config('plex_port', self.plex_port.text()) engine.set_config('plex_obey_update_wait_s', self.plex_obey_wait.isChecked()) engine.set_config('plex_user', self.plex_user.text()) engine.set_config('plex_passwd', self.plex_passw.text()) engine.set_config('searchdir', [self.searchdirs.item(i).text() for i in range(self.searchdirs.count())]) if self.autoretrieve_always.isChecked(): engine.set_config('autoretrieve', 'always') elif self.autoretrieve_days.isChecked(): engine.set_config('autoretrieve', 'days') else: engine.set_config('autoretrieve', 'off') engine.set_config('autoretrieve_days', self.autoretrieve_days_n.value()) if self.autosend_always.isChecked(): engine.set_config('autosend', 'always') elif self.autosend_minutes.isChecked(): engine.set_config('autosend', 'minutes') elif self.autosend_size.isChecked(): engine.set_config('autosend', 'size') else: engine.set_config('autosend', 'off') engine.set_config('autosend_minutes', self.autosend_minutes_n.value()) engine.set_config('autosend_size', self.autosend_size_n.value()) engine.set_config('autosend_at_exit', self.autosend_at_exit.isChecked()) engine.set_config('auto_status_change', self.auto_status_change.isChecked()) engine.set_config('auto_status_change_if_scored', self.auto_status_change_if_scored.isChecked()) engine.set_config('auto_date_change', self.auto_date_change.isChecked()) engine.save_config() self.config['show_tray'] = self.tray_icon.isChecked() self.config['close_to_tray'] = self.close_to_tray.isChecked() self.config['start_in_tray'] = self.start_in_tray.isChecked() self.config['tray_api_icon'] = self.tray_api_icon.isChecked() self.config['notifications'] = self.notifications.isChecked() self.config['remember_geometry'] = self.remember_geometry.isChecked() self.config['remember_columns'] = self.remember_columns.isChecked() self.config['columns_per_api'] = self.columns_per_api.isChecked() self.config['filter_bar_position'] = self.filter_bar_position.itemData(self.filter_bar_position.currentIndex()) self.config['inline_edit'] = self.inline_edit.isChecked() self.config['episodebar_style'] = self.ep_bar_style.itemData(self.ep_bar_style.currentIndex()) self.config['episodebar_text'] = self.ep_bar_text.isChecked() self.config['colors'] = self.color_values utils.save_config(self.config, self.configfile) self.saved.emit() def s_save(self): self._save() self.accept() def tracker_type_change(self, checked): if self.tracker_enabled.isChecked(): self.tracker_interval.setEnabled(True) self.tracker_update_wait.setEnabled(True) self.tracker_type.setEnabled(True) if self.tracker_type.itemData(self.tracker_type.currentIndex()) == 'plex': self.plex_host.setEnabled(True) self.plex_port.setEnabled(True) self.plex_obey_wait.setEnabled(True) self.plex_user.setEnabled(True) self.plex_passw.setEnabled(True) self.tracker_process.setEnabled(False) else: self.tracker_process.setEnabled(True) self.plex_host.setEnabled(False) self.plex_port.setEnabled(False) self.plex_user.setEnabled(False) self.plex_passw.setEnabled(False) self.plex_obey_wait.setEnabled(False) else: self.tracker_type.setEnabled(False) self.plex_host.setEnabled(False) self.plex_port.setEnabled(False) self.plex_user.setEnabled(False) self.plex_passw.setEnabled(False) self.plex_obey_wait.setEnabled(False) self.tracker_process.setEnabled(False) self.tracker_interval.setEnabled(False) self.tracker_update_wait.setEnabled(False) def s_autoretrieve_days(self, checked): self.autoretrieve_days_n.setEnabled(checked) def s_autosend_minutes(self, checked): self.autosend_minutes_n.setEnabled(checked) def s_autosend_size(self, checked): self.autosend_size_n.setEnabled(checked) def s_tray_icon(self, checked): self.close_to_tray.setEnabled(checked) self.start_in_tray.setEnabled(checked) self.tray_api_icon.setEnabled(checked) self.notifications.setEnabled(checked) def s_ep_bar_style(self, index): if self.ep_bar_style.itemData(index) == ShowsTableDelegate.BarStyle04: self.ep_bar_text.setEnabled(False) else: self.ep_bar_text.setEnabled(True) def s_auto_status_change(self, checked): self.auto_status_change_if_scored.setEnabled(checked) def s_player_browse(self): if pyqt_version is 5: self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')[0]) else: self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')) def s_searchdirs_add(self): self._add_dir(QFileDialog.getExistingDirectory(caption='Choose media directory')) def s_searchdirs_remove(self): row = self.searchdirs.currentRow() if row != -1: self.searchdirs.takeItem(row) def s_switch_page(self, new, old): if not new: new = old self.contents.setCurrentIndex(self.category_list.row(new)) def s_color_picker(self, key, system): return lambda: self.color_picker(key, system) def color_picker(self, key, system): if system is True: current = self.color_values[key] result = ThemedColorPicker.do() if result is not None and result is not current: self.color_values[key] = result self.update_colors() else: current = getColor(self.color_values[key]) result = QColorDialog.getColor(current) if result.isValid() and result is not current: self.color_values[key] = str(result.name()) self.update_colors() def update_colors(self): for ((key, label), color) in zip(self.colors['rows']+self.colors['progress'], self.color_buttons): color.setStyleSheet('background-color: ' + getColor(self.color_values[key]).name())
class _HistoryDialog: record_label = "Save..." execute_label = "Execute" def __init__(self, controller, typed_only): # make dialog hidden initially self.controller = controller self.typed_only = typed_only self.window = controller.tool_window.create_child_window( "Command History", close_destroys=False) self.window.fill_context_menu = self.fill_context_menu parent = self.window.ui_area from PyQt5.QtWidgets import QListWidget, QVBoxLayout, QFrame, QHBoxLayout, QPushButton, QLabel self.listbox = QListWidget(parent) self.listbox.setSelectionMode(QListWidget.ExtendedSelection) self.listbox.itemSelectionChanged.connect(self.select) main_layout = QVBoxLayout(parent) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(self.listbox) num_cmd_frame = QFrame(parent) main_layout.addWidget(num_cmd_frame) num_cmd_layout = QHBoxLayout(num_cmd_frame) num_cmd_layout.setContentsMargins(0, 0, 0, 0) remem_label = QLabel("Remember") from PyQt5.QtCore import Qt remem_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) num_cmd_layout.addWidget(remem_label, 1) from PyQt5.QtWidgets import QSpinBox, QSizePolicy class ShorterQSpinBox(QSpinBox): max_val = 1000000 def textFromValue(self, val): # kludge to make the damn entry field shorter if val == self.max_val: return "1 mil" return str(val) spin_box = ShorterQSpinBox() spin_box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) spin_box.setRange(100, spin_box.max_val) spin_box.setSingleStep(100) spin_box.setValue(controller.settings.num_remembered) spin_box.valueChanged.connect(self._num_remembered_changed) num_cmd_layout.addWidget(spin_box, 0) num_cmd_layout.addWidget(QLabel("commands"), 1) num_cmd_frame.setLayout(num_cmd_layout) button_frame = QFrame(parent) main_layout.addWidget(button_frame) button_layout = QHBoxLayout(button_frame) button_layout.setContentsMargins(0, 0, 0, 0) for but_name in [ self.record_label, self.execute_label, "Delete", "Copy", "Help" ]: but = QPushButton(but_name, button_frame) but.setAutoDefault(False) but.clicked.connect( lambda arg, txt=but_name: self.button_clicked(txt)) button_layout.addWidget(but) button_frame.setLayout(button_layout) self.window.manage(placement=None, initially_hidden=True) from chimerax.core.history import FIFOHistory self._history = FIFOHistory(controller.settings.num_remembered, controller.session, "commands") self._record_dialog = None self._search_cache = (False, None) def add(self, item, *, typed=False): if len(self._history) >= self.controller.settings.num_remembered: if not self.typed_only or self._history[0][1]: self.listbox.takeItem(0) if typed or not self.typed_only: self.listbox.addItem(item) self._history.enqueue((item, typed)) # 'if typed:' to avoid clearing any partially entered command text if typed: self.listbox.clearSelection() self.listbox.setCurrentRow(len(self.history()) - 1) self.update_list() def button_clicked(self, label): session = self.controller.session if label == self.record_label: from chimerax.ui.open_save import SaveDialog if self._record_dialog is None: fmt = session.data_formats["ChimeraX commands"] self._record_dialog = dlg = SaveDialog(session, self.window.ui_area, "Save Commands", data_formats=[fmt]) from PyQt5.QtWidgets import QFrame, QLabel, QHBoxLayout, QVBoxLayout, QComboBox from PyQt5.QtWidgets import QCheckBox from PyQt5.QtCore import Qt options_frame = dlg.custom_area options_layout = QVBoxLayout(options_frame) options_frame.setLayout(options_layout) amount_frame = QFrame(options_frame) options_layout.addWidget(amount_frame, Qt.AlignCenter) amount_layout = QHBoxLayout(amount_frame) amount_layout.addWidget(QLabel("Save", amount_frame)) self.save_amount_widget = saw = QComboBox(amount_frame) saw.addItems(["all", "selected"]) amount_layout.addWidget(saw) amount_layout.addWidget(QLabel("commands", amount_frame)) amount_frame.setLayout(amount_layout) self.append_checkbox = QCheckBox("Append to file", options_frame) self.append_checkbox.stateChanged.connect(self.append_changed) options_layout.addWidget(self.append_checkbox, Qt.AlignCenter) self.overwrite_disclaimer = disclaimer = QLabel( "<small><i>(ignore overwrite warning)</i></small>", options_frame) options_layout.addWidget(disclaimer, Qt.AlignCenter) disclaimer.hide() else: dlg = self._record_dialog if not dlg.exec(): return path = dlg.selectedFiles()[0] if not path: from chimerax.core.errors import UserError raise UserError("No file specified for saving command history") if self.save_amount_widget.currentText() == "all": cmds = [cmd for cmd in self.history()] else: # listbox.selectedItems() may not be in order, so... items = [ self.listbox.item(i) for i in range(self.listbox.count()) if self.listbox.item(i).isSelected() ] cmds = [item.text() for item in items] from chimerax.io import open_output f = open_output(path, encoding='utf-8', append=self.append_checkbox.isChecked()) for cmd in cmds: print(cmd, file=f) f.close() return if label == self.execute_label: for item in self.listbox.selectedItems(): self.controller.cmd_replace(item.text()) self.controller.execute() return if label == "Delete": retain = [] listbox_index = 0 for h_item in self._history: if self.typed_only and not h_item[1]: retain.append(h_item) continue if not self.listbox.item(listbox_index).isSelected(): # not selected for deletion retain.append(h_item) listbox_index += 1 self._history.replace(retain) self.populate() return if label == "Copy": clipboard = session.ui.clipboard() clipboard.setText("\n".join( [item.text() for item in self.listbox.selectedItems()])) return if label == "Help": from chimerax.core.commands import run run(session, 'help help:user/tools/cli.html#history') return def down(self, shifted): sels = self.listbox.selectedIndexes() if len(sels) != 1: self._search_cache = (False, None) return sel = sels[0].row() orig_text = self.controller.text.currentText() match_against = None if shifted: was_searching, prev_search = self._search_cache if was_searching: match_against = prev_search else: words = orig_text.strip().split() if words: match_against = words[0] self._search_cache = (True, match_against) else: self._search_cache = (False, None) else: self._search_cache = (False, None) if match_against: last = self.listbox.count() - 1 while sel < last: if self.listbox.item(sel + 1).text().startswith(match_against): break sel += 1 if sel == self.listbox.count() - 1: return self.listbox.clearSelection() self.listbox.setCurrentRow(sel + 1) new_text = self.listbox.item(sel + 1).text() self.controller.cmd_replace(new_text) if orig_text == new_text: self.down(shifted) def fill_context_menu(self, menu, x, y): # avoid having actions destroyed when this routine returns # by stowing a reference in the menu itself from PyQt5.QtWidgets import QAction filter_action = QAction("Typed commands only", menu) filter_action.setCheckable(True) filter_action.setChecked(self.controller.settings.typed_only) filter_action.toggled.connect( lambda arg, f=self.controller._set_typed_only: f(arg)) menu.addAction(filter_action) def on_append_change(self, event): self.overwrite_disclaimer.Show(self.save_append_CheckBox.Value) def append_changed(self, append): if append: self.overwrite_disclaimer.show() else: self.overwrite_disclaimer.hide() def on_listbox(self, event): self.select() def populate(self): self.listbox.clear() history = self.history() self.listbox.addItems([cmd for cmd in history]) self.listbox.setCurrentRow(len(history) - 1) self.update_list() self.select() self.controller.text.lineEdit().setFocus() self.controller.text.lineEdit().selectAll() cursels = self.listbox.scrollToBottom() def search_reset(self): searching, target = self._search_cache if searching: self._search_cache = (False, None) self.listbox.blockSignals(True) self.listbox.clearSelection() self.listbox.setCurrentRow(self.listbox.count() - 1) self.listbox.blockSignals(False) def select(self): sels = self.listbox.selectedItems() if len(sels) != 1: return self.controller.cmd_replace(sels[0].text()) def up(self, shifted): sels = self.listbox.selectedIndexes() if len(sels) != 1: self._search_cache = (False, None) return sel = sels[0].row() orig_text = self.controller.text.currentText() match_against = None if shifted: was_searching, prev_search = self._search_cache if was_searching: match_against = prev_search else: words = orig_text.strip().split() if words: match_against = words[0] self._search_cache = (True, match_against) else: self._search_cache = (False, None) else: self._search_cache = (False, None) if match_against: while sel > 0: if self.listbox.item(sel - 1).text().startswith(match_against): break sel -= 1 if sel == 0: return self.listbox.clearSelection() self.listbox.setCurrentRow(sel - 1) new_text = self.listbox.item(sel - 1).text() self.controller.cmd_replace(new_text) if orig_text == new_text: self.up(shifted) def update_list(self): c = self.controller last8 = list(reversed(self.history()[-8:])) # without blocking signals, if the command list is empty then # "Command History" (the first entry) will execute... c.text.blockSignals(True) c.text.clear() c.text.addItems(last8 + [c.show_history_label, c.compact_label]) if not last8: c.text.lineEdit().setText("") c.text.blockSignals(False) def history(self): if self.typed_only: return [h[0] for h in self._history if h[1]] return [h[0] for h in self._history] def set_typed_only(self, typed_only): self.typed_only = typed_only self.populate() def _num_remembered_changed(self, new_hist_len): if len(self._history) > new_hist_len: self._history.replace(self._history[-new_hist_len:]) self.populate() self.controller.settings.num_remembered = new_hist_len
class ListEdit(QWidget): """A widget to edit a list of items (e.g. a list of directories).""" # emitted when anything changed in the listbox. changed = pyqtSignal() def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) layout = QGridLayout(self) self.setLayout(layout) self.addButton = QPushButton(icons.get('list-add'), '') self.editButton = QPushButton(icons.get('document-edit'), '') self.removeButton = QPushButton(icons.get('list-remove'), '') self.listBox = QListWidget() layout.setContentsMargins(1, 1, 1, 1) layout.setSpacing(0) layout.addWidget(self.listBox, 0, 0, 8, 1) layout.addWidget(self.addButton, 0, 1) layout.addWidget(self.editButton, 1, 1) layout.addWidget(self.removeButton, 2, 1) @self.addButton.clicked.connect def addClicked(): item = self.createItem() if self.openEditor(item): self.addItem(item) @self.editButton.clicked.connect def editClicked(): item = self.listBox.currentItem() item and self.editItem(item) @self.removeButton.clicked.connect def removeClicked(): item = self.listBox.currentItem() if item: self.removeItem(item) @self.listBox.itemDoubleClicked.connect def itemDoubleClicked(item): item and self.editItem(item) self.listBox.model().layoutChanged.connect(self.changed) def updateSelection(): selected = bool(self.listBox.currentItem()) self.editButton.setEnabled(selected) self.removeButton.setEnabled(selected) self.changed.connect(updateSelection) self.listBox.itemSelectionChanged.connect(updateSelection) updateSelection() app.translateUI(self) def translateUI(self): self.addButton.setText(_("&Add...")) self.editButton.setText(_("&Edit...")) self.removeButton.setText(_("&Remove")) def createItem(self): return QListWidgetItem() def addItem(self, item): self.listBox.addItem(item) self.itemChanged(item) self.changed.emit() def removeItem(self, item): self.listBox.takeItem(self.listBox.row(item)) self.changed.emit() def editItem(self, item): if self.openEditor(item): self.itemChanged(item) self.changed.emit() def setCurrentItem(self, item): self.listBox.setCurrentItem(item) def setCurrentRow(self, row): self.listBox.setCurrentRow(row) def openEditor(self, item): """Opens an editor (dialog) for the item. Returns True if the dialog was accepted and the item edited. Returns False if the dialog was cancelled (the item must be left unedited). """ pass def itemChanged(self, item): """Called after an item has been added or edited. Re-implement to do something at this moment if needed, e.g. alter the text or display of other items. """ pass def setValue(self, strings): """Sets the listbox to a list of strings.""" self.listBox.clear() self.listBox.addItems(strings) self.changed.emit() def value(self): """Returns the list of paths in the listbox.""" return [self.listBox.item(i).text() for i in range(self.listBox.count())] def setItems(self, items): """Sets the listbox to a list of items.""" self.listBox.clear() for item in items: self.listBox.addItem(item) self.itemChanged(item) self.changed.emit() def items(self): """Returns the list of items in the listbox.""" return [self.listBox.item(i) for i in range(self.listBox.count())] def clear(self): """Clears the listbox.""" self.listBox.clear() self.changed.emit()
class Checkers(QMainWindow): def __init__(self, share_dir): QMainWindow.__init__(self) self.share_dir = share_dir self.setWindowTitle(_("HCheckers client")) self.settings = QSettings("hcheckers", "hcheckers") self._board_setup_mode = False self._game_active = False self._connection_failed = False self._poll_try_number = 0 self.splashscreen = None self._show_splashcreen(_("Starting HCheckers...")) self.server_url = self.settings.value("server_url", DEFAULT_SERVER_URL) self._start_server() self._prepare() self._gui_setup() self._setup_actions() self._default_new_game() def get_board_setup_mode(self): return self._board_setup_mode def set_board_setup_mode(self, mode): self._board_setup_mode = mode self.run_action.setEnabled(mode) self.put_first_action.setEnabled(mode) self.put_second_action.setEnabled(mode) self.erase_action.setEnabled(mode) board_setup_mode = property(get_board_setup_mode, set_board_setup_mode) def get_my_turn(self): return self.board.my_turn def set_my_turn(self, value): self.board.my_turn = value if value: self.statusBar().showMessage(_("Your turn.")) self.board.text_message = None #self.board.repaint() else: self.statusBar().showMessage( _("Awaiting a turn from another side.")) #self.board.text_message = _("Waiting for another side turn") my_turn = property(get_my_turn, set_my_turn) def get_game_active(self): return self._game_active def set_game_active(self, value): self._game_active = value self.board.locked = not value game_active = property(get_game_active, set_game_active) def _start_server(self): server_running = Game.check_server(self.server_url) use_local_server = self.settings.value("use_local_server", type=bool) self.local_server_used = False if server_running: if use_local_server: logging.info( _("Server appears to be already running, do not start a local server" )) else: if use_local_server: self.splashscreen.showMessage(_("Starting local server...")) QApplication.processEvents() server_path = self.settings.value("local_server_path") logging.info(_("Running local server: {}").format(server_path)) server = subprocess.Popen(server_path, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) time.sleep(1) server.poll() if server.returncode is not None and server.returncode != 0: output = server.stdout.read() message = _( "Could not start local server; exit code: {}; message:\n{}" ).format(server.returncode, output.decode('utf-8')) logging.error(message) QMessageBox.critical(self, _("Exception"), message) else: self.local_server_used = True def _prepare(self): if self.share_dir is None: raise Exception("Cant locate share directory") theme_name = self.settings.value("theme", "default") self.theme = Theme(join(self.share_dir, "themes", theme_name), None) self.theme.enable_sound = self.settings.value("enable_sound", type=bool) self.game = Game(self.server_url) self.poll_timer = self.startTimer(500) self.setup_fields_on_poll = False def _icon(self, name): return QIcon(join(self.share_dir, "icons", name)) @handling_error def _gui_setup(self): widget = QWidget(self) layout = QVBoxLayout() self.board = Board(self.theme, self.settings, self.game, self) self.board.message.connect(self._on_board_message) self.board.field_clicked.connect(self._on_field_clicked) #self.board.show() self.toolbar = QToolBar(self) self.message = QLabel(self) layout.addWidget(self.toolbar) layout.addWidget(self.message) layout.addWidget(self.board, stretch=1) widget.setLayout(layout) self.setCentralWidget(widget) self.status_info = QLabel(self) self.statusBar().addPermanentWidget(self.status_info) self.rules_info = QLabel(self) self.rules_info.setFrameStyle(QFrame.Sunken | QFrame.Panel) #self.rules_info.setLineWidth(3) self.statusBar().addPermanentWidget(self.rules_info) self.opponent_info = QLabel(self) self.opponent_info.setFrameStyle(QFrame.Sunken | QFrame.Panel) #self.opponent_info.setLineWidth(3) self.statusBar().addPermanentWidget(self.opponent_info) self.history = HistoryWidget(self.game, self.board, self) self.history_dock = QDockWidget(_("History"), self) self.history_dock.setAllowedAreas(Qt.AllDockWidgetAreas) self.history_dock.setWidget(self.history) self.history_dock.setObjectName("history") self.addDockWidget(Qt.RightDockWidgetArea, self.history_dock) self.log = QListWidget(self) self.log.setContextMenuPolicy(Qt.CustomContextMenu) self.log.customContextMenuRequested.connect(self._on_log_context_menu) self.log_dock = QDockWidget(_("Log"), self) self.log_dock.setAllowedAreas(Qt.AllDockWidgetAreas) self.log_dock.setWidget(self.log) self.log_dock.setObjectName("log") self.addDockWidget(Qt.BottomDockWidgetArea, self.log_dock) console_handler = logging.getLogger().handlers[0] logging.getLogger().removeHandler(console_handler) log_handler = UiLogHandler(self.log) level = self.settings.value("log_level", logging.INFO, type=int) logging.getLogger().setLevel(level) logging.getLogger().addHandler(log_handler) for logger in lowered_loggers: logging.getLogger(logger).addFilter( LogFilter(lowered_regexps=lowered_regexps)) self.board.server_log.connect(self._on_server_log) geometry = self.settings.value("UI/geometry") if geometry is not None: self.restoreGeometry(geometry) state = self.settings.value("UI/windowState") if state is not None: self.restoreState(state) def _create_action(self, icon, title, menu, handler=None, group=None, toggle=False, toolbar=True, key=None): if group is None: parent = self else: parent = group action = QAction(title, parent) if icon is not None: action.setIcon(icon) if key is not None: action.setShortcut(key) if toggle: action.setCheckable(True) if toolbar: self.toolbar.addAction(action) menu.addAction(action) if handler: action.triggered.connect(handler) return action def _setup_actions(self): menu = self.menuBar().addMenu(_("&Game")) self._create_action(QIcon.fromTheme("document-new"), _("&New Game"), menu, self._on_new_game, key="Ctrl+N") self._create_action(QIcon.fromTheme("document-open"), _("&Open Game..."), menu, self._on_open_game, key="Ctrl+O") self._create_action(QIcon.fromTheme("document-save"), _("&Save Position"), menu, self._on_save_game, key="Ctrl+S") self._create_action(QIcon.fromTheme("edit-undo"), _("&Undo"), menu, self._on_undo, key="Ctrl+Z") self.request_draw_action = self._create_action( self._icon("draw_offer.svg"), _("Offer a &draw"), menu, self._on_draw_rq) self.capitulate_action = self._create_action(self._icon("handsup.svg"), _("Capitulate"), menu, self._on_capitulate) menu.addSeparator() self.clear_log_action = self._create_action( QIcon.fromTheme("edit-clear"), _("&Clear log"), menu, self._on_clear_log, toolbar=False) self.copy_log_action = self._create_action( QIcon.fromTheme("edit-copy"), _("Copy selected log record"), menu, self._on_copy_log, toolbar=False) self.save_log_action = self._create_action( QIcon.fromTheme("document-save"), _("Save &log..."), menu, self._on_save_log, toolbar=False) menu.addSeparator() self.toolbar.addSeparator() self.run_action = self._create_action( QIcon.fromTheme("media-playback-start"), _("Start &Game"), menu, self._on_run_game, key="Ctrl+R") menu.addSeparator() self._create_action(QIcon.fromTheme("preferences-system"), _("Se&ttings"), menu, self._on_settings, toolbar=False) menu.addSeparator() self._create_action(QIcon.fromTheme("application-exit"), _("E&xit"), menu, self._on_exit, toolbar=False, key="Ctrl+Q") menu = self.menuBar().addMenu(_("&Position")) setup = QActionGroup(self) setup.setExclusive(True) self.put_first_action = self._create_action(self._icon("manwhite.svg"), _("Put &white piece"), menu, group=setup, toggle=True) self.put_second_action = self._create_action( self._icon("manblack.svg"), _("Put &black piece"), menu, group=setup, toggle=True) self.erase_action = self._create_action(QIcon.fromTheme("list-remove"), _("&Remove piece"), menu, group=setup, toggle=True) self.board_setup_mode = False menu.addSeparator() self.toolbar.addSeparator() menu = self.menuBar().addMenu(_("&View")) self.flip_action = self._create_action( QIcon.fromTheme("object-flip-vertical"), _("&Flip board"), menu, self._on_flip_board, toggle=True, key="Ctrl+T") flip = self.settings.value("flip_board", False, type=bool) self.flip_action.setChecked(flip) self._set_flip_board(flip) menu.addAction(self.history_dock.toggleViewAction()) menu.addAction(self.log_dock.toggleViewAction()) @handling_error def _on_run_game(self, checked=None): self.board_setup_mode = False board = self.board.json() self.game.start_new_game( self.game_settings.user_name, rules=self.game_settings.rules, user_turn_first=self.game_settings.user_turn_first, ai=self.game_settings.ai, board=board) self.board.text_message = None self.board.fields_setup() @handling_error def _on_field_clicked(self, row, col): if not self.board_setup_mode: return field = self.board.fields[(row, col)] if not field.usable: logging.debug("You cant put piece at this field") return first = self.put_first_action.isChecked() second = self.put_second_action.isChecked() erase = self.erase_action.isChecked() if not first and not second and not erase: return if first: side = FIRST elif second: side = SECOND piece = field.piece if not erase: if piece and piece.side == side: if piece.kind == MAN: piece.kind = KING else: piece.kind = MAN else: piece = Piece(MAN, side) else: piece = None self.board.fields[(row, col)].piece = piece @handling_error def _default_new_game(self): self.splashscreen.finish(self) if len(sys.argv) == 2: path = sys.argv[1] if path.endswith(".pdn"): mask = PDN_MASK elif path.endswith(".fen"): mask = FEN_MASK else: mask = None else: path, mask = None, None self._on_new_game(show_exit=True, open_file=(path, mask)) @handling_error def _on_exit(self, *args): self.close() def _screen_size(self): rect = QApplication.desktop().availableGeometry(self) return min(rect.width(), rect.height()) def _splashscreen_size(self): screen_size = self._screen_size() return screen_size / 2 def _show_splashcreen(self, message=None): splash_size = self._splashscreen_size() splash_pix = self._icon("splashscreen.svg").pixmap( QSize(splash_size, splash_size)) self.splashscreen = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) self.splashscreen.show() QApplication.processEvents() if message is not None: self.splashscreen.showMessage(message) QApplication.processEvents() def _new_game(self, dialog): # Show splashcreen after user pressed Ok in the "new game" dialog self._show_splashcreen(_("Starting new game...")) if self.game.is_active(): self.game.capitulate() self.board.text_message = None self.game_active = True self.game.game_id = None self.request_draw_action.setEnabled(True) self.capitulate_action.setEnabled(True) self.game_settings = game = dialog.get_settings() if game.action == START_AI_GAME: if game.board_setup: self.board.empty() self.board_setup_mode = True self.game.rules = game.rules else: self.game.start_new_game( game.user_name, rules=game.rules, user_turn_first=game.user_turn_first, ai=game.ai, fen_path=game.fen_path, pdn_path=game.pdn_path, previous_board_game=game.previous_board_game) state = self.game.get_state() my_side = 'First' if self.game.user_side == FIRST else 'Second' self.my_turn = state["side"] == my_side self.rules_info.setText( _("Rules: {}").format(rules_dict[game.rules])) self.opponent_info.setText(_("AI: {}").format(game.ai.title)) self.status_info.setText("") elif game.action == START_HUMAN_GAME: game_id = self.game.new_game(game.rules) logging.info(_("New game ID: {}").format(game_id)) if game.user_turn_first: self.game.register_user(game.user_name, FIRST) else: self.game.register_user(game.user_name, SECOND) self.setup_fields_on_poll = True self.rules_info.setText(_("Rules: {}").format(game.rules)) self.opponent_info.setText("") self.status_info.setText("") self.game_active = False message = _("Waiting for another side to join the game.") self.statusBar().showMessage(message) self.board.text_message = message elif game.action == JOIN_HUMAN_GAME: self.game.game_id = dialog.lobby.get_game_id() self.game.user_side = side = dialog.lobby.get_free_side() self.game.rules = dialog.lobby.get_rules() #used_name = dialog.lobby.get_used_name() self.game.register_user(game.user_name, side) self.game.run_game() self.setup_fields_on_poll = True self.my_turn = side == FIRST self.rules_info.setText(_("Rules: {}").format(game.rules)) self.opponent_info.setText("") self.status_info.setText("") size, invert, notation = self.game.get_notation(game.rules) self.board.invert_colors = invert self.board.topology = self.game.get_topology(game.rules) self.board.set_notation(size, notation) self.board.theme = self.board.theme #self.board.repaint() self.history.fill() @handling_error def _on_new_game(self, checked=None, show_exit=False, open_file=(None, None)): dialog = NewGameDialog(self.settings, self.game, self.share_dir, show_exit, open_file=open_file, parent=self) result = dialog.exec_() if result == QDialog.Accepted: self._new_game(dialog) if self.splashscreen: self.splashscreen.finish(self) if result == EXIT: print("Exit!") #QApplication.quit() QTimer.singleShot(0, lambda: self.close()) @handling_error def _on_open_game(self, checked=None): path, mask = select_game_file(self) if path: dialog = NewGameDialog(self.settings, self.game, self.share_dir, show_exit=False, open_file=(path, mask), parent=self) result = dialog.exec_() if result == QDialog.Accepted: self._new_game(dialog) if self.splashscreen: self.splashscreen.finish(self) @handling_error def _on_save_game(self, checked=None): (path, mask) = QFileDialog.getSaveFileName(self.board, _("Save file"), ".", FEN_MASK + ";;" + PDN_MASK) if path: if mask == FEN_MASK: fen = self.game.get_fen() with open(path, 'w') as f: f.write(fen) elif mask == PDN_MASK: pdn = self.game.get_pdn() with open(path, 'w') as f: f.write(pdn) @handling_error def _on_undo(self, checked=None): try: prev_board = self.game.undo() self.board.fields_setup(prev_board) self.board.repaint() self.history.fill() except RequestError as e: error = e.rs.json().get("error", None) if error == "NothingToUndo": logging.warning(_("Nothing to undo.")) else: raise e @handling_error def _on_draw_rq(self, checked=None): messages = self.game.request_draw() for message in messages: self.board.process_message(message) self.request_draw_action.setEnabled(False) self.capitulate_action.setEnabled(False) @handling_error def _on_accept_draw(self, checked=None): messages = self.game.accept_draw(True) for message in messages: self.board.process_message(message) @handling_error def _on_decline_draw(self, checked=None): messages = self.game.accept_draw(False) for message in messages: self.board.process_message(message) @handling_error def _on_capitulate(self, checked=None): messages = self.game.capitulate() for message in messages: self.board.process_message(message) #self.my_turn = False @handling_error def get_result_str(self, result): first, second = self.game.get_colors(self.game.rules) if result == 'FirstWin': return _("{} win").format(first) elif result == 'SecondWin': return _("{} win").format(second) else: return _("Draw") def _on_board_message(self, message): if isinstance(message, GameResultMessage): self.game_active = False result = self.get_result_str(message.result) result_text = _("Game result: {}").format(result) self.status_info.setText(result_text) self.board.setCursor(Qt.ArrowCursor) game_over = _("Game over.") self.statusBar().showMessage(game_over) self.board.text_message = game_over + " " + result_text self.history.fill() self.request_draw_action.setEnabled(False) self.capitulate_action.setEnabled(False) elif isinstance(message, OtherSideMove): self.message.setText(str(message)) self.history.fill() self.my_turn = True self.board.text_message = None self.board.repaint() elif isinstance(message, WaitingMove): text = str(message) self.statusBar().showMessage(text) self.board.text_message = text elif isinstance(message, DrawRequestedMessage): ok = QMessageBox.question( self, _("Accept the draw?"), _("Another side have offered you a draw. Do you wish to accept it?" )) if ok == QMessageBox.Yes: self._on_accept_draw() else: self._on_decline_draw() elif isinstance(message, DrawResponseMessage): text = str(message) self.message.setText(text) self.board.text_message = text self.board.repaint() if not message.result: self.request_draw_action.setEnabled(True) self.capitulate_action.setEnabled(True) def _on_server_log(self, level, message): if level == "DEBUG": logging.debug(message) elif level == "INFO": logging.info(message) elif level == "WARNING": logging.warning(message) elif level == "ERROR": logging.error(message) # item = QListWidgetItem(self.log) # item.setText(message) # icon = None # if level == "DEBUG": # icon = QIcon.fromTheme("document-properties") # elif level == "INFO": # icon = QIcon.fromTheme("dialog-information") # elif level == "ERROR": # icon = QIcon.fromTheme("dialog-error") # elif level == "WARNING": # icon = QIcon.fromTheme("dialog-warning") # if icon is not None: # item.setIcon(icon) # self.log.update() # self.log.scrollToBottom() def _log_context_menu(self): menu = QMenu(self) menu.addAction(self.clear_log_action) menu.addAction(self.copy_log_action) menu.addAction(self.save_log_action) return menu def _on_log_context_menu(self, pos): menu = self._log_context_menu() menu.exec_(self.log.mapToGlobal(pos)) def _on_clear_log(self, checked): self.log.clear() logging.info(_("Log has been cleared.")) def _on_save_log(self, checked): text = "" for row in range(self.log.count()): text = text + self.log.item(row).text() + "\n" (path, mask) = QFileDialog.getSaveFileName(self, _("Save file"), ".", LOG_MASK) if path: with open(path, 'w') as f: f.write(text) #.encode("utf-8")) def _on_copy_log(self, checked): items = self.log.selectedItems() if not items: return text = items[0].text() QApplication.clipboard().setText(text) def _set_flip_board(self, value): self.board.flip = value self.settings.setValue("flip_board", self.board.flip) def _on_flip_board(self): self._set_flip_board(self.flip_action.isChecked()) def _on_settings(self): dialog = SettingsDialog(self.settings, self.share_dir, self) result = dialog.exec_() if result == QDialog.Accepted: self.board.show_possible_moves = dialog.get_show_possible_moves() self.board.show_notation = dialog.get_show_notation() self.board.theme = dialog.get_theme() self.board.theme.enable_sound = dialog.get_enable_sound() level = self.settings.value("log_level", logging.INFO, type=int) logging.getLogger().setLevel(level) self.settings.sync() logging.info(_("Settings have been updated.")) def _handle_game_error(self, rs): try: json = rs.json() except: json = None message_format = _( "Unexpected response received from the server.\nRequest URL: {}\nResponse code: {}\nResponse message: {}" ) if json is None: message = message_format.format(rs.url, rs.status_code, rs.text) else: err_msg = json.get("error", "Unspecified") if err_msg == "no such move": move = json.get("move", "?") #board = Game.parse_board(json["board"]) #board = self.board #possible = json.get("possible", []) #possible = [board.show_move(Move.fromJson(m)) for m in possible] message = _("No such move: {}").format(move) elif err_msg == "invalid game status": expected = json.get("expected", "?") actual = json.get("actual", "?") message = _( "Status of current game is unsuitable for this operation. Game status is {}; required status is {}" ).format(expected, actual) else: message = message_format.format(rs.url, rs.status_code, err_msg) QMessageBox.critical(self, _("Exception"), message) logging.exception(message) def _handle_connection_error(self, url, e): message = _( "An exception occured while connecting to the server.\nRequest URL: {}\nException text: {}" ).format(url, e) QMessageBox.critical(self, _("Exception"), message) logging.exception(message) self._connection_failed = True def closeEvent(self, ev): if self.game.is_active(): try: self.game.capitulate() except Exception as e: logging.exception(e) print(e) use_local_server = self.settings.value("use_local_server", type=bool) if use_local_server and self.local_server_used: try: self.game.shutdown() except RequestError as e: self._handle_game_error(e.rs) except Exception as e: logging.exception(e) print(e) self.settings.setValue("UI/geometry", self.saveGeometry()) self.settings.setValue("UI/windowState", self.saveState()) QMainWindow.closeEvent(self, ev) @handling_error def timerEvent(self, e): if e.timerId() != self.poll_timer: return if self._connection_failed: self._poll_try_number = self._poll_try_number + 1 if self._poll_try_number < 10: return if self.setup_fields_on_poll and not self.game_active: state = self.game.get_state() if state["status"] == 'Running': self.game_active = True my_side = 'First' if self.game.user_side == FIRST else 'Second' self.my_turn = state['side'] == my_side #self.statusBar().clearMessage() if not self.game.is_active(): return self._poll_try_number = 0 board, messages = self.game.poll() for message in messages: self.board.process_message(message) if "move" in message: self.my_turn = True if self.setup_fields_on_poll: self.board.fields_setup(board)