class fx_selector(QWidget): def __init__(self): QWidget.__init__(self) self.dump_dir = os.path.join(get_sim_path(), "light_dump") self.layout = QVBoxLayout() label = QLabel(_("Wavelengths") + ":") self.layout.addWidget(label) self.cb = QComboBox() self.layout.addWidget(self.cb) self.setLayout(self.layout) self.show() self.start = "" self.end = "" self.show_all = False def get_text(self): out = self.cb.currentText() out = out.split(" ")[0] return out def file_name_set_start(self, start): self.start = start def file_name_set_end(self, end): self.end = end def find_modes(self, path): result = [] lines = [] dump_dir = os.path.join(get_sim_path(), "light_dump") path = os.path.join(dump_dir, "wavelengths.dat") if os.path.isfile(path) == True: f = open(path, "r") lines = f.readlines() f.close() for l in range(0, len(lines)): txt = lines[l].rstrip() if txt != "": result.append(txt) if os.path.isdir(dump_dir) == True: files = os.listdir(dump_dir) for f in files: if f.startswith("light_ray_"): f = f[:-4] f = f[10:] #print(f) result.append(f) return result def update(self): self.cb.blockSignals(True) thefiles = self.find_modes(self.dump_dir) thefiles.sort() if len(thefiles) == 0: self.setEnabled(False) else: self.setEnabled(True) self.cb.clear() if self.show_all == True: self.cb.addItem("all") for i in range(0, len(thefiles)): path = os.path.join(self.dump_dir, self.start + str(thefiles[i]) + self.end) #print(path) if os.path.isfile(path): self.cb.addItem(str(thefiles[i]) + " nm") self.cb.setCurrentIndex(0) self.cb.blockSignals(False)
class ColorSet: """Stores color settings and provides dialogs for user changes. """ def __init__(self, option): self.option = option self.sysPalette = QApplication.palette() self.colors = [Color(roleKey) for roleKey in roles.keys()] self.theme = ThemeSetting[self.option.strData('ColorTheme')] for color in self.colors: color.colorChanged.connect(self.setCustomTheme) color.setFromPalette(self.sysPalette) if self.theme == ThemeSetting.dark: color.setFromTheme(darkColors) elif self.theme == ThemeSetting.custom: color.setFromOption(self.option) def setAppColors(self): """Set application to current colors. """ newPalette = QApplication.palette() for color in self.colors: color.updatePalette(newPalette) qApp.setPalette(newPalette) def showDialog(self, parent): """Show a dialog for user color changes. Return True if changes were made. """ dialog = QDialog(parent) dialog.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowSystemMenuHint) dialog.setWindowTitle(_('Color Settings')) topLayout = QVBoxLayout(dialog) dialog.setLayout(topLayout) themeBox = QGroupBox(_('Color Theme'), dialog) topLayout.addWidget(themeBox) themeLayout = QVBoxLayout(themeBox) self.themeControl = QComboBox(dialog) self.themeControl.addItem(_('Default system theme'), ThemeSetting.system) self.themeControl.addItem(_('Dark theme'), ThemeSetting.dark) self.themeControl.addItem(_('Custom theme'), ThemeSetting.custom) self.themeControl.setCurrentIndex( self.themeControl.findData(self.theme)) self.themeControl.currentIndexChanged.connect(self.updateThemeSetting) themeLayout.addWidget(self.themeControl) self.groupBox = QGroupBox(dialog) self.setBoxTitle() topLayout.addWidget(self.groupBox) gridLayout = QGridLayout(self.groupBox) row = 0 for color in self.colors: gridLayout.addWidget(color.getLabel(), row, 0) gridLayout.addWidget(color.getSwatch(), row, 1) row += 1 ctrlLayout = QHBoxLayout() topLayout.addLayout(ctrlLayout) ctrlLayout.addStretch(0) okButton = QPushButton(_('&OK'), dialog) ctrlLayout.addWidget(okButton) okButton.clicked.connect(dialog.accept) cancelButton = QPushButton(_('&Cancel'), dialog) ctrlLayout.addWidget(cancelButton) cancelButton.clicked.connect(dialog.reject) if dialog.exec_() == QDialog.Accepted: self.theme = ThemeSetting(self.themeControl.currentData()) self.option.changeData('ColorTheme', self.theme.name, True) if self.theme == ThemeSetting.system: qApp.setPalette(self.sysPalette) else: # dark theme or custom if self.theme == ThemeSetting.custom: for color in self.colors: color.updateOption(self.option) self.setAppColors() else: for color in self.colors: color.setFromPalette(self.sysPalette) if self.theme == ThemeSetting.dark: color.setFromTheme(darkColors) elif self.theme == ThemeSetting.custom: color.setFromOption(self.option) def setBoxTitle(self): """Set title of group box to standard or custom. """ if self.themeControl.currentData() == ThemeSetting.custom: title = _('Custom Colors') else: title = _('Theme Colors') self.groupBox.setTitle(title) def updateThemeSetting(self): """Update the colors based on a theme control change. """ if self.themeControl.currentData() == ThemeSetting.system: for color in self.colors: color.setFromPalette(self.sysPalette) color.changeSwatchColor() elif self.themeControl.currentData() == ThemeSetting.dark: for color in self.colors: color.setFromTheme(darkColors) color.changeSwatchColor() else: for color in self.colors: color.setFromOption(self.option) color.changeSwatchColor() self.setBoxTitle() def setCustomTheme(self): """Set to custom theme setting after user color change. """ if self.themeControl.currentData != ThemeSetting.custom: self.themeControl.blockSignals(True) self.themeControl.setCurrentIndex(2) self.themeControl.blockSignals(False) self.setBoxTitle()
class class_optical(QWidget): edit_list=[] line_number=[] file_name="" name="" visible=1 def __init__(self): QWidget.__init__(self) self.dump_dir=os.path.join(os.getcwd(),"light_dump") find_models() self.setWindowIcon(QIcon(os.path.join(get_image_file_path(),"image.png"))) self.setMinimumSize(1000, 600) self.edit_list=[] self.line_number=[] self.articles=[] input_files=[] input_files.append(os.path.join(os.getcwd(),"light_dump","light_2d_photons.dat")) input_files.append(os.path.join(os.getcwd(),"light_dump","light_2d_photons_asb.dat")) input_files.append(os.path.join(os.getcwd(),"light_dump","reflect.dat")) plot_labels=[] plot_labels.append("Photon distribution") plot_labels.append("Photon distribution absorbed") plot_labels.append("Reflection") self.setGeometry(300, 300, 600, 600) self.setWindowTitle(_("Optical simulation editor (www.gpvdm.com)")) self.setWindowIcon(QIcon(os.path.join(get_image_file_path(),"optics.png"))) self.main_vbox=QVBoxLayout() menubar = QMenuBar() file_menu = menubar.addMenu('&File') self.menu_refresh=file_menu.addAction(_("&Refresh")) self.menu_refresh.triggered.connect(self.update) self.menu_close=file_menu.addAction(_("&Close")) self.menu_close.triggered.connect(self.callback_close) self.main_vbox.addWidget(menubar) toolbar=QToolBar() toolbar.setIconSize(QSize(48, 48)) self.run = QAction(QIcon(os.path.join(get_image_file_path(),"play.png")), _("Run"), self) self.run.triggered.connect(self.callback_run) toolbar.addAction(self.run) label=QLabel(_("Wavelengths:")) toolbar.addWidget(label) self.cb = QComboBox() self.update_cb() toolbar.addWidget(self.cb) self.cb.currentIndexChanged.connect(self.mode_changed) label=QLabel(_("Optical model:")) toolbar.addWidget(label) self.cb_model = QComboBox() toolbar.addWidget(self.cb_model) self.update_cb_model() self.cb_model.activated.connect(self.on_cb_model_changed) label=QLabel(_("Solar spectrum:")) toolbar.addWidget(label) self.light_source_model = QComboBox() self.update_light_source_model() toolbar.addWidget(self.light_source_model) self.light_source_model.activated.connect(self.on_light_source_model_changed) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(spacer) self.help = QAction(QIcon(os.path.join(get_image_file_path(),"help.png")), 'Help', self) self.help.triggered.connect(self.callback_help) toolbar.addAction(self.help) self.main_vbox.addWidget(toolbar) self.progress_window=progress_class() self.notebook = QTabWidget() self.notebook.setMovable(True) self.fig_photon_density = band_graph() self.fig_photon_density.set_data_file("light_1d_photons_tot_norm.dat") self.fig_photon_density.init() self.notebook.addTab(self.fig_photon_density,"Photon density") self.fig_photon_abs = band_graph() self.fig_photon_abs.set_data_file("light_1d_photons_tot_abs_norm.dat") self.fig_photon_abs.init() self.notebook.addTab(self.fig_photon_abs,"Photon absorbed") widget=tab_class() widget.init("light.inp","Optical setup") self.notebook.addTab(widget,"Optical setup") self.plot_widgets=[] self.progress_window.start() for i in range(0,len(input_files)): self.plot_widgets.append(plot_widget()) self.plot_widgets[i].init() self.plot_widgets[i].set_labels([plot_labels[0]]) self.plot_widgets[i].load_data([input_files[i]],os.path.splitext(input_files[i])[0]+".oplot") self.plot_widgets[i].do_plot() #self.plot_widgets[i].show() self.notebook.addTab(self.plot_widgets[i],plot_labels[i]) self.fig_photon_density.draw_graph() self.fig_photon_abs.draw_graph() self.progress_window.stop() self.main_vbox.addWidget(self.notebook) self.setLayout(self.main_vbox) return def onclick(self, event): for i in range(0,len(self.layer_end)): if (self.layer_end[i]>event.xdata): break pwd=os.getcwd() plot_gen([os.path.join(pwd,"materials",self.layer_name[i],"alpha.omat")],[],None,"") def update_cb(self): self.cb.blockSignals(True) thefiles=find_modes(self.dump_dir) thefiles.sort() files_short=thefiles[::2] self.cb.clear() self.cb.addItem("all") for i in range(0, len(files_short)): self.cb.addItem(str(files_short[i])+" nm") self.cb.setCurrentIndex(0) self.cb.blockSignals(False) def update_cb_model(self): self.cb_model.blockSignals(True) self.cb_model.clear() models=find_models() if len(models)==0: error_dlg(self,_("I can't find any optical plugins, I think the model is not installed properly.")) return for i in range(0, len(models)): self.cb_model.addItem(models[i]) used_model=inp_get_token_value("light.inp", "#light_model") print(models,used_model) if models.count(used_model)==0: used_model="exp" inp_update_token_value("light.inp", "#light_model","exp",1) self.cb_model.setCurrentIndex(self.cb_model.findText(used_model)) else: self.cb_model.setCurrentIndex(self.cb_model.findText(used_model)) scan_item_add("light.inp","#light_model","Optical model",1) self.cb_model.blockSignals(False) def update_light_source_model(self): self.light_source_model.blockSignals(True) models=find_light_source() for i in range(0, len(models)): self.light_source_model.addItem(models[i]) used_model=inp_get_token_value("light.inp", "#sun") print("models================",models,used_model) if models.count(used_model)==0: used_model="sun" inp_update_token_value("light.inp", "#sun","sun",1) self.light_source_model.setCurrentIndex(self.light_source_model.findText(used_model)) scan_item_add("light.inp","#sun","Light source",1) self.light_source_model.blockSignals(False) def callback_close(self): self.hide() return True def update(self): self.fig_photon_density.my_figure.clf() self.fig_photon_density.draw_graph() self.fig_photon_density.canvas.draw() self.fig_photon_abs.my_figure.clf() self.fig_photon_abs.draw_graph() self.fig_photon_abs.canvas.draw() for i in range(0,len(self.plot_widgets)): self.plot_widgets[i].update() def callback_run(self): dump_optics=inp_get_token_value("dump.inp", "#dump_optics") dump_optics_verbose=inp_get_token_value("dump.inp", "#dump_optics_verbose") inp_update_token_value("dump.inp", "#dump_optics","true",1) inp_update_token_value("dump.inp", "#dump_optics_verbose","true",1) cmd = get_exe_command()+' --simmode opticalmodel@optics' print(cmd) ret= os.system(cmd) inp_update_token_value("dump.inp", "#dump_optics",dump_optics,1) inp_update_token_value("dump.inp", "#dump_optics_verbose",dump_optics_verbose,1) self.update() self.update_cb() inp_update_token_value("dump.inp", "#dump_optics","true",1) inp_update_token_value("dump.inp", "#dump_optics_verbose","true",1) def mode_changed(self): cb_text=self.cb.currentText() if cb_text=="all": self.fig_photon_density.set_data_file("light_1d_photons_tot_norm.dat") self.fig_photon_abs.set_data_file("light_1d_photons_tot_abs_norm.dat") else: self.fig_photon_density.set_data_file("light_1d_"+cb_text[:-3]+"_photons_norm.dat") self.fig_photon_abs.set_data_file("light_1d_"+cb_text[:-3]+"_photons_abs.dat") self.update() def on_cb_model_changed(self): cb_text=self.cb_model.currentText() inp_update_token_value("light.inp", "#light_model", cb_text,1) def on_light_source_model_changed(self): cb_text=self.light_source_model.currentText() cb_text=cb_text inp_update_token_value("light.inp", "#sun", cb_text,1) def callback_help(self, widget, data=None): webbrowser.open('http://www.gpvdm.com/man/index.html')
class FileChooser(QWidget): fileOpened = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.folderBox = QComboBox(self) self.explorerTree = FileTreeView(self) self.explorerTree.doubleClickCallback = self._fileOpened self.explorerModel = QFileSystemModel(self) self.explorerModel.setFilter( QDir.AllDirs | QDir.Files | QDir.NoDotAndDotDot) self.explorerModel.setNameFilters(["*.py"]) self.explorerModel.setNameFilterDisables(False) self.explorerTree.setModel(self.explorerModel) for index in range(1, self.explorerModel.columnCount()): self.explorerTree.hideColumn(index) self.setCurrentFolder() self.folderBox.currentIndexChanged[int].connect( self.updateCurrentFolder) layout = QVBoxLayout(self) layout.addWidget(self.folderBox) layout.addWidget(self.explorerTree) layout.setContentsMargins(5, 5, 0, 0) def _fileOpened(self, modelIndex): path = self.explorerModel.filePath(modelIndex) if os.path.isfile(path): self.fileOpened.emit(path) def currentFolder(self): return self.explorerModel.rootPath() def setCurrentFolder(self, path=None): if path is None: app = QApplication.instance() path = app.getScriptsDirectory() else: assert os.path.isdir(path) self.explorerModel.setRootPath(path) self.explorerTree.setRootIndex(self.explorerModel.index(path)) self.folderBox.blockSignals(True) self.folderBox.clear() style = self.style() dirIcon = style.standardIcon(style.SP_DirIcon) self.folderBox.addItem(dirIcon, os.path.basename(path)) self.folderBox.insertSeparator(1) self.folderBox.addItem(self.tr("Browse…")) self.folderBox.setCurrentIndex(0) self.folderBox.blockSignals(False) def updateCurrentFolder(self, index): if index < self.folderBox.count() - 1: return path = QFileDialog.getExistingDirectory( self, self.tr("Choose Directory"), self.currentFolder(), QFileDialog.ShowDirsOnly) if path: QSettings().setValue("scripting/path", path) self.setCurrentFolder(path)
class tb_spectrum(QWidget): def __init__(self): QWidget.__init__(self) self.layout = QVBoxLayout() label = QLabel(_("Solar spectrum") + ":") self.layout.addWidget(label) self.cb = QComboBox() self.cb.activated.connect(self.on_cb_model_changed) self.layout.addWidget(self.cb) self.update() self.setLayout(self.layout) self.show() def get_text(self): out = self.cb.currentText() out = out.split(" ")[0] return out def file_name_set_start(self, start): self.start = start def file_name_set_end(self, end): self.end = end def find_models(self): ret = [] path = get_plugins_path() for file in glob.glob(os.path.join(path, "*")): file_name = os.path.basename(file) if file_name.startswith("light_"): if file_name.endswith(".dll") or file_name.endswith(".so"): ret.append( os.path.splitext(os.path.basename(file_name[6:]))[0]) return ret def on_cb_model_changed(self): cb_text = self.cb.currentText() inp_update_token_value("light.inp", "#sun", cb_text) def update(self): self.cb.blockSignals(True) models = find_light_source() for i in range(0, len(models)): self.cb.addItem(models[i]) used_model = inp_get_token_value("light.inp", "#sun") if models.count(used_model) == 0: used_model = "sun" inp_update_token_value("light.inp", "#sun", "sun") self.cb.setCurrentIndex(self.cb.findText(used_model)) #scan_item_add("light.inp","#sun","Light source",1) self.cb.blockSignals(False)
class WosMoveActionWidget(QWidget): combo_updated = pyqtSignal() def __init__(self, index, parent=None): QWidget.__init__(self, parent) self.index = index self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.layout.addWidget(QLabel("Move %s:" % index, self), 0, 0) self.move_ship_combo = QComboBox(self) self.move_ship_combo.currentTextChanged.connect(self.combo_updated) self.layout.addWidget(self.move_ship_combo, 0, 1) self.move_action_combo = QComboBox(self) self.move_action_combo.currentTextChanged.connect(self.combo_updated) self.move_action_combo.addItem('Forward', cCommonGame.Action.FWD) self.move_action_combo.addItem('Turn clockwise', cCommonGame.Action.CW) self.move_action_combo.addItem('Turn anti-clockwise', cCommonGame.Action.CCW) self.move_action_combo.addItem('Skip', cCommonGame.Action.NOP) self.layout.addWidget(self.move_action_combo, 0, 2) def get_index(self): return self.index def get_move_info(self): return self.move_ship_combo.currentData(), self.move_action_combo.currentData() def set_ship_actions(self, ship_id, action): self.move_ship_combo.setCurrentText(str(ship_id)) if action == cCommonGame.Action.FWD: self.move_action_combo.setCurrentText('Forward') elif action == cCommonGame.Action.CW: self.move_action_combo.setCurrentText('Turn clockwise') elif action == cCommonGame.Action.CCW: self.move_action_combo.setCurrentText('Turn anti-clockwise') else: self.move_action_combo.setCurrentText('Skip') def update_move_ship_combo(self, ships): self.move_ship_combo.blockSignals(True) # Preserve last command old_text = self.move_ship_combo.currentText() self.move_ship_combo.clear() for ship_id, ship in ships.items(): if ship and not ship.ship_info.is_sunken and ship.type is ItemType.SHIP: self.move_ship_combo.addItem(str(ship_id), ship_id) if not old_text: # Default index self.move_ship_combo.setCurrentIndex(self.index - 1) else: self.move_ship_combo.setCurrentText(old_text) self.move_ship_combo.blockSignals(False)
def setEditorData(self, editor: QtWidgets.QComboBox, index: QtCore.QModelIndex): editor.blockSignals(True) dflist = index.internalPointer() # type: DFItemList item = dflist[index.row()] # type: DFItem column = index.column() if column == 1: editor.setCurrentIndex(editor.findText(str(item.x_accessor))) elif column == 2: editor.setCurrentIndex(editor.findText(str(item.y_accessor))) elif column == 3: editor.setCurrentIndex(editor.findText(str(item.z_accessor))) editor.blockSignals(False)
class SidePanel(QDockWidget): def __init__(self, title, scene=None, parent=None): super().__init__(title, parent) self.setWidget(QWidget()) self.scene = None self.stepbox = QComboBox() self.steptext = QTextEdit() self.stepbox.currentTextChanged.connect(self.report) layout = QVBoxLayout(self.widget()) layout.addSpacing(20) layout.addWidget(self.stepbox) layout.addSpacing(10) layout.addWidget(self.steptext) layout.addSpacing(20) self.connect_scene(scene) def connect_scene(self, scene): self.disconnect() self.scene = scene if scene == None: self.atoms = {0: ""} self._init_stepbox() self.set_to_step(0) else: self.atoms = scene._model.get_atoms() self._init_stepbox() self.set_to_step(scene._current_step) self.scene.currentStepChanged.connect(self.set_to_step) self.stepbox.currentTextChanged.connect(self.scene.go_to_step) def disconnect(self): if self.scene != None: self.scene.currentStepChanged.disconnect(self.set_to_step) self.stepbox.currentTextChanged.disconnect(self.scene.go_to_step) def report(self, step): print(f"Sending request to go to step {step}!") def set_to_step(self, step): if int(step) in self.atoms: self.stepbox.blockSignals(True) self.stepbox.setCurrentText(str(step)) self.stepbox.blockSignals(False) self.steptext.setText(".\n".join(self.atoms[step])) def _init_stepbox(self): self.stepbox.blockSignals(True) self.stepbox.clear() if "0" not in self.atoms: self.stepbox.addItem("0") for step in sorted(self.atoms): self.stepbox.addItem(str(step)) self.stepbox.blockSignals(False)
class Window(QDialog): def __init__(self): super().__init__() self.title = "PyQt5 Combo Box" self.top = 200 self.left = 500 self.width = 300 self.height = 100 self.InitWindow() def InitWindow(self): self.setWindowIcon(QtGui.QIcon("icon.png")) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) vbox = QVBoxLayout() self.combo = QComboBox() self.combo.currentTextChanged.connect(self.comboChanged) self.combo.blockSignals(True) self.combo.clear() self.combo.addItem("Python") self.combo.addItem("Java") self.combo.addItem("C++") self.combo.addItem("C#") self.combo.addItem("Ruby") self.combo.blockSignals(False) self.combo.currentTextChanged.connect(self.comboChanged) self.label = QLabel() self.label.setFont(QtGui.QFont("Sanserif", 15)) self.label.setStyleSheet('color:red') vbox.addWidget(self.combo) vbox.addWidget(self.label) self.setLayout(vbox) self.show() def comboChanged(self): text = self.combo.currentText() self.label.setText("You Have Selected : " + text)
def fill_combobox(combobox: QtWidgets.QComboBox, values: Iterable[Any], text_func: Callable = str, block_signals=False): """Fills the combobox with given values. Stores the values as user data and displays the string representations as item labels. Previous items are removed from the combobox. Args: combobox: combobox to fill values: collection of values to be added to the combobox text_func: function that detenmines how values are presented in text form block_signals: whether signals from combobox are blocked during filling """ if block_signals: combobox.blockSignals(True) combobox.clear() for value in values: combobox.addItem(text_func(value), userData=value) if block_signals: combobox.blockSignals(False)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() centralWidget = QWidget() fontLabel = QLabel("Font:") self.fontCombo = QFontComboBox() sizeLabel = QLabel("Size:") self.sizeCombo = QComboBox() styleLabel = QLabel("Style:") self.styleCombo = QComboBox() fontMergingLabel = QLabel("Automatic Font Merging:") self.fontMerging = QCheckBox() self.fontMerging.setChecked(True) self.scrollArea = QScrollArea() self.characterWidget = CharacterWidget() self.scrollArea.setWidget(self.characterWidget) self.findStyles(self.fontCombo.currentFont()) self.findSizes(self.fontCombo.currentFont()) self.lineEdit = QLineEdit() clipboardButton = QPushButton("&To clipboard") self.clipboard = QApplication.clipboard() self.fontCombo.currentFontChanged.connect(self.findStyles) self.fontCombo.activated[str].connect(self.characterWidget.updateFont) self.styleCombo.activated[str].connect( self.characterWidget.updateStyle) self.sizeCombo.currentIndexChanged[str].connect( self.characterWidget.updateSize) self.characterWidget.characterSelected.connect(self.insertCharacter) clipboardButton.clicked.connect(self.updateClipboard) controlsLayout = QHBoxLayout() controlsLayout.addWidget(fontLabel) controlsLayout.addWidget(self.fontCombo, 1) controlsLayout.addWidget(sizeLabel) controlsLayout.addWidget(self.sizeCombo, 1) controlsLayout.addWidget(styleLabel) controlsLayout.addWidget(self.styleCombo, 1) controlsLayout.addWidget(fontMergingLabel) controlsLayout.addWidget(self.fontMerging, 1) controlsLayout.addStretch(1) lineLayout = QHBoxLayout() lineLayout.addWidget(self.lineEdit, 1) lineLayout.addSpacing(12) lineLayout.addWidget(clipboardButton) centralLayout = QVBoxLayout() centralLayout.addLayout(controlsLayout) centralLayout.addWidget(self.scrollArea, 1) centralLayout.addSpacing(4) centralLayout.addLayout(lineLayout) centralWidget.setLayout(centralLayout) self.setCentralWidget(centralWidget) self.setWindowTitle("Character Map") def findStyles(self, font): fontDatabase = QFontDatabase() currentItem = self.styleCombo.currentText() self.styleCombo.clear() for style in fontDatabase.styles(font.family()): self.styleCombo.addItem(style) styleIndex = self.styleCombo.findText(currentItem) if styleIndex == -1: self.styleCombo.setCurrentIndex(0) else: self.styleCombo.setCurrentIndex(styleIndex) def findSizes(self, font): fontDatabase = QFontDatabase() currentSize = self.sizeCombo.currentText() self.sizeCombo.blockSignals(True) self.sizeCombo.clear() if fontDatabase.isSmoothlyScalable(font.family(), fontDatabase.styleString(font)): for size in QFontDatabase.standardSizes(): self.sizeCombo.addItem(str(size)) self.sizeCombo.setEditable(True) else: for size in fontDatabase.smoothSizes( font.family(), fontDatabase.styleString(font)): self.sizeCombo.addItem(str(size)) self.sizeCombo.setEditable(False) self.sizeCombo.blockSignals(False) sizeIndex = self.sizeCombo.findText(currentSize) if sizeIndex == -1: self.sizeCombo.setCurrentIndex(max(0, self.sizeCombo.count() / 3)) else: self.sizeCombo.setCurrentIndex(sizeIndex) def insertCharacter(self, character): self.lineEdit.insert(character) def updateClipboard(self): self.clipboard.setText(self.lineEdit.text(), QClipboard.Clipboard) self.clipboard.setText(self.lineEdit.text(), QClipboard.Selection)
class MotorizedLinearPoti(COMCUPluginBase): def __init__(self, *args): super().__init__(BrickletMotorizedLinearPoti, *args) self.mp = self.device self.cbe_position = CallbackEmulator(self.mp.get_position, None, self.cb_position, self.increase_error_count) self.current_position = CurveValueWrapper() self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setMinimumWidth(200) self.slider.setEnabled(False) plots = [('Potentiometer Position', Qt.red, self.current_position, str)] self.plot_widget = PlotWidget('Position', plots, extra_key_widgets=[self.slider], update_interval=0.025, y_resolution=1.0) self.motor_slider = QSlider(Qt.Horizontal) self.motor_slider.setRange(0, 100) self.motor_slider.valueChanged.connect(self.motor_slider_value_changed) self.motor_hold_position = QCheckBox("Hold Position") self.motor_drive_mode = QComboBox() self.motor_drive_mode.addItem('Fast') self.motor_drive_mode.addItem('Smooth') def get_motor_slider_value(): return self.motor_slider.value() self.motor_hold_position.stateChanged.connect(lambda x: self.motor_slider_value_changed(get_motor_slider_value())) self.motor_drive_mode.currentIndexChanged.connect(lambda x: self.motor_slider_value_changed(get_motor_slider_value())) self.motor_position_label = MotorPositionLabel('Motor Target Position:') hlayout = QHBoxLayout() hlayout.addWidget(self.motor_position_label) hlayout.addWidget(self.motor_slider) hlayout.addWidget(self.motor_drive_mode) hlayout.addWidget(self.motor_hold_position) line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def start(self): async_call(self.mp.get_motor_position, None, self.get_motor_position_async, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletMotorizedLinearPoti.DEVICE_IDENTIFIER def cb_position(self, position): self.current_position.value = position self.slider.setValue(position) def get_motor_position_async(self, motor): self.motor_slider.blockSignals(True) self.motor_hold_position.blockSignals(True) self.motor_drive_mode.blockSignals(True) self.motor_hold_position.setChecked(motor.hold_position) self.motor_drive_mode.setCurrentIndex(motor.drive_mode) self.motor_position_label.setText(str(motor.position)) self.motor_slider.setValue(motor.position) self.motor_slider.blockSignals(False) self.motor_hold_position.blockSignals(False) self.motor_drive_mode.blockSignals(False) def motor_slider_value_changed(self, position): self.motor_position_label.setText(str(position)) self.mp.set_motor_position(self.motor_slider.value(), self.motor_drive_mode.currentIndex(), self.motor_hold_position.isChecked())
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() centralWidget = QWidget() fontLabel = QLabel("Font:") self.fontCombo = QFontComboBox() sizeLabel = QLabel("Size:") self.sizeCombo = QComboBox() styleLabel = QLabel("Style:") self.styleCombo = QComboBox() fontMergingLabel = QLabel("Automatic Font Merging:") self.fontMerging = QCheckBox() self.fontMerging.setChecked(True) self.scrollArea = QScrollArea() self.characterWidget = CharacterWidget() self.scrollArea.setWidget(self.characterWidget) self.findStyles(self.fontCombo.currentFont()) self.findSizes(self.fontCombo.currentFont()) self.lineEdit = QLineEdit() clipboardButton = QPushButton("&To clipboard") self.clipboard = QApplication.clipboard() self.fontCombo.currentFontChanged.connect(self.findStyles) self.fontCombo.activated[str].connect(self.characterWidget.updateFont) self.styleCombo.activated[str].connect(self.characterWidget.updateStyle) self.sizeCombo.currentIndexChanged[str].connect(self.characterWidget.updateSize) self.characterWidget.characterSelected.connect(self.insertCharacter) clipboardButton.clicked.connect(self.updateClipboard) controlsLayout = QHBoxLayout() controlsLayout.addWidget(fontLabel) controlsLayout.addWidget(self.fontCombo, 1) controlsLayout.addWidget(sizeLabel) controlsLayout.addWidget(self.sizeCombo, 1) controlsLayout.addWidget(styleLabel) controlsLayout.addWidget(self.styleCombo, 1) controlsLayout.addWidget(fontMergingLabel) controlsLayout.addWidget(self.fontMerging, 1) controlsLayout.addStretch(1) lineLayout = QHBoxLayout() lineLayout.addWidget(self.lineEdit, 1) lineLayout.addSpacing(12) lineLayout.addWidget(clipboardButton) centralLayout = QVBoxLayout() centralLayout.addLayout(controlsLayout) centralLayout.addWidget(self.scrollArea, 1) centralLayout.addSpacing(4) centralLayout.addLayout(lineLayout) centralWidget.setLayout(centralLayout) self.setCentralWidget(centralWidget) self.setWindowTitle("Character Map") def findStyles(self, font): fontDatabase = QFontDatabase() currentItem = self.styleCombo.currentText() self.styleCombo.clear() for style in fontDatabase.styles(font.family()): self.styleCombo.addItem(style) styleIndex = self.styleCombo.findText(currentItem) if styleIndex == -1: self.styleCombo.setCurrentIndex(0) else: self.styleCombo.setCurrentIndex(styleIndex) def findSizes(self, font): fontDatabase = QFontDatabase() currentSize = self.sizeCombo.currentText() self.sizeCombo.blockSignals(True) self.sizeCombo.clear() if fontDatabase.isSmoothlyScalable(font.family(), fontDatabase.styleString(font)): for size in QFontDatabase.standardSizes(): self.sizeCombo.addItem(str(size)) self.sizeCombo.setEditable(True) else: for size in fontDatabase.smoothSizes(font.family(), fontDatabase.styleString(font)): self.sizeCombo.addItem(str(size)) self.sizeCombo.setEditable(False) self.sizeCombo.blockSignals(False) sizeIndex = self.sizeCombo.findText(currentSize) if sizeIndex == -1: self.sizeCombo.setCurrentIndex(max(0, self.sizeCombo.count() / 3)) else: self.sizeCombo.setCurrentIndex(sizeIndex) def insertCharacter(self, character): self.lineEdit.insert(character) def updateClipboard(self): self.clipboard.setText(self.lineEdit.text(), QClipboard.Clipboard) self.clipboard.setText(self.lineEdit.text(), QClipboard.Selection)
class snapshot_slider(QWidget): changed = pyqtSignal() def cal_min_max(self): self.z_max=-1e40 self.z_min=1e40 for i in range(0,len(self.dirs)): fname=os.path.join(self.dirs[i],self.files_combo.currentText()) x=[] y=[] z=[] my_data=dat_file() if dat_file_read(my_data,fname) == True: #print(z) temp_max,temp_min=dat_file_max_min(my_data) if temp_max>self.z_max: self.z_max=temp_max if temp_min<self.z_min: self.z_min=temp_min def update(self): self.dirs=[] if os.path.isdir(self.path)==True: for name in os.listdir(self.path): if name!="." and name!= "..": full_path=os.path.join(self.path, name) if os.path.isdir(full_path): self.dirs.append(full_path) self.slider_max=len(self.dirs)-1 self.slider0.setMaximum(self.slider_max) self.update_file_combo() def slider0_change(self): value = self.slider0.value() self.label0.setText(str(value)) self.changed.emit() def get_file_name(self): file_path=os.path.join(self.path,str(self.slider0.value()),self.files_combo.currentText()) if os.path.isfile(file_path)==False: file_path=None return file_path def set_path(self,path): self.path=path self.update() self.cal_min_max() def __init__(self): QWidget.__init__(self) self.path="" self.setWindowTitle(_("Snapshot slider")) self.main_vbox = QVBoxLayout() self.slider_hbox0= QHBoxLayout() self.slider_max=30 self.slider0 = QSlider(Qt.Horizontal) self.slider0.setMinimum(10) self.slider0.setMaximum(self.slider_max) self.slider0.setTickPosition(QSlider.TicksBelow) self.slider0.setTickInterval(5) self.slider0.valueChanged.connect(self.slider0_change) self.slider0.setMinimumSize(300, 80) self.slider_hbox0.addWidget(self.slider0) self.label0 = QLabel() self.label0.setText("") self.slider0.setValue(20) self.slider_hbox0.addWidget(self.label0) self.widget0=QWidget() self.widget0.setLayout(self.slider_hbox0) self.main_vbox.addWidget(self.widget0) ################ self.slider_hbox1= QHBoxLayout() self.label1 = QLabel() self.label1.setText("File") self.slider_hbox1.addWidget(self.label1) self.files_combo=QComboBox() self.slider_hbox1.addWidget(self.files_combo) self.files_combo.currentIndexChanged.connect(self.files_combo_changed) self.widget1=QWidget() self.widget1.setLayout(self.slider_hbox1) self.main_vbox.addWidget(self.widget1) ############### self.setLayout(self.main_vbox) def update_file_combo(self): self.files_combo.blockSignals(True) self.files_combo.clear() path=os.path.join(self.path,str(self.slider0.value())) if os.path.isdir(path)==True: for name in os.listdir(path): full_path=os.path.join(path, name) if os.path.isfile(full_path): if name!="snapshot_info.dat": self.files_combo.addItem(name) all_items = [self.files_combo.itemText(i) for i in range(self.files_combo.count())] for i in range(0,len(all_items)): if all_items[i] == "Jn.dat": self.files_combo.setCurrentIndex(i) self.files_combo.blockSignals(False) def files_combo_changed(self): self.cal_min_max() self.changed.emit()
class IndustrialDualAnalogInV2(COMCUPluginBase): def __init__(self, *args): super().__init__(BrickletIndustrialDualAnalogInV2, *args) self.analog_in = self.device self.cbe_voltage0 = CallbackEmulator( self.analog_in.get_voltage, 0, self.cb_voltage, self.increase_error_count, pass_arguments_to_result_callback=True) self.cbe_voltage1 = CallbackEmulator( self.analog_in.get_voltage, 1, self.cb_voltage, self.increase_error_count, pass_arguments_to_result_callback=True) self.calibration = None self.sample_rate_label = QLabel('Sample Rate:') self.sample_rate_combo = QComboBox() self.sample_rate_combo.addItem('976 Hz') self.sample_rate_combo.addItem('488 Hz') self.sample_rate_combo.addItem('244 Hz') self.sample_rate_combo.addItem('122 Hz') self.sample_rate_combo.addItem('61 Hz') self.sample_rate_combo.addItem('4 Hz') self.sample_rate_combo.addItem('2 Hz') self.sample_rate_combo.addItem('1 Hz') self.current_voltage = [CurveValueWrapper(), CurveValueWrapper()] # float, V self.calibration_button = QPushButton('Calibration...') self.sample_rate_combo.currentIndexChanged.connect( self.sample_rate_combo_index_changed) self.calibration_button.clicked.connect( self.calibration_button_clicked) plots = [ ('Channel 0', Qt.red, self.current_voltage[0], format_voltage), ('Channel 1', Qt.blue, self.current_voltage[1], format_voltage) ] self.plot_widget = PlotWidget('Voltage [V]', plots, y_resolution=0.001) # Define lines line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) line1 = QFrame() line1.setObjectName("line1") line1.setFrameShape(QFrame.HLine) line1.setFrameShadow(QFrame.Sunken) line2 = QFrame() line2.setObjectName("line2") line2.setFrameShape(QFrame.HLine) line2.setFrameShadow(QFrame.Sunken) # Define channel LED status config widgets self.led_config_ch0_label = QLabel('Channel 0') self.led_config_ch1_label = QLabel('Channel 1') self.led_config_label = QLabel('LED Config:') self.led_status_config_label = QLabel('LED Status Config:') self.led_status_config_ch0_min_label = QLabel('Min:') self.led_status_config_ch0_max_label = QLabel('Max:') self.led_status_config_ch1_min_label = QLabel('Min:') self.led_status_config_ch1_max_label = QLabel('Max:') self.led_config_ch0_combo = QComboBox() self.led_config_ch0_combo.addItem('Off') self.led_config_ch0_combo.addItem('On') self.led_config_ch0_combo.addItem('Show Heartbeat') self.led_config_ch0_combo.addItem('Show Channel Status') self.led_config_ch0_combo.currentIndexChanged.connect( self.led_config_ch0_combo_changed) self.led_config_ch1_combo = QComboBox() self.led_config_ch1_combo.addItem('Off') self.led_config_ch1_combo.addItem('On') self.led_config_ch1_combo.addItem('Show Heartbeat') self.led_config_ch1_combo.addItem('Show Channel Status') self.led_config_ch1_combo.currentIndexChanged.connect( self.led_config_ch1_combo_changed) self.led_status_config_ch0_combo = QComboBox() self.led_status_config_ch0_combo.addItem('Threshold') self.led_status_config_ch0_combo.addItem('Intensity') self.led_status_config_ch0_combo.currentIndexChanged.connect( self.led_status_config_ch0_combo_changed) self.led_status_config_ch1_combo = QComboBox() self.led_status_config_ch1_combo.addItem('Threshold') self.led_status_config_ch1_combo.addItem('Intensity') self.led_status_config_ch1_combo.currentIndexChanged.connect( self.led_status_config_ch1_combo_changed) self.led_status_config_ch0_min_sbox = QSpinBox() self.led_status_config_ch0_min_sbox.setMinimum(-35000) self.led_status_config_ch0_min_sbox.setMaximum(35000) self.led_status_config_ch0_min_sbox.setValue(0) self.led_status_config_ch0_min_sbox.setSingleStep(1) self.led_status_config_ch0_min_sbox.setSuffix(' mV') self.led_status_config_ch0_min_sbox.valueChanged.connect( self.led_status_config_ch0_min_sbox_changed) self.led_status_config_ch0_max_sbox = QSpinBox() self.led_status_config_ch0_max_sbox.setMinimum(-35000) self.led_status_config_ch0_max_sbox.setMaximum(35000) self.led_status_config_ch0_max_sbox.setValue(0) self.led_status_config_ch0_max_sbox.setSingleStep(1) self.led_status_config_ch0_max_sbox.setSuffix(' mV') self.led_status_config_ch0_max_sbox.valueChanged.connect( self.led_status_config_ch0_max_sbox_changed) self.led_status_config_ch1_min_sbox = QSpinBox() self.led_status_config_ch1_min_sbox.setMinimum(-35000) self.led_status_config_ch1_min_sbox.setMaximum(35000) self.led_status_config_ch1_min_sbox.setValue(0) self.led_status_config_ch1_min_sbox.setSingleStep(1) self.led_status_config_ch1_min_sbox.setSuffix(' mV') self.led_status_config_ch1_min_sbox.valueChanged.connect( self.led_status_config_ch1_min_sbox_changed) self.led_status_config_ch1_max_sbox = QSpinBox() self.led_status_config_ch1_max_sbox.setMinimum(-35000) self.led_status_config_ch1_max_sbox.setMaximum(35000) self.led_status_config_ch1_max_sbox.setValue(0) self.led_status_config_ch1_max_sbox.setSingleStep(1) self.led_status_config_ch1_max_sbox.setSuffix(' mV') self.led_status_config_ch1_max_sbox.valueChanged.connect( self.led_status_config_ch1_max_sbox_changed) # Define size policies h_sp = QSizePolicy() h_sp.setHorizontalPolicy(QSizePolicy.Expanding) # Set size policies self.sample_rate_combo.setSizePolicy(h_sp) self.led_config_ch0_combo.setSizePolicy(h_sp) self.led_config_ch1_combo.setSizePolicy(h_sp) self.led_status_config_ch0_combo.setSizePolicy(h_sp) self.led_status_config_ch1_combo.setSizePolicy(h_sp) self.led_status_config_ch0_min_sbox.setSizePolicy(h_sp) self.led_status_config_ch0_max_sbox.setSizePolicy(h_sp) self.led_status_config_ch1_min_sbox.setSizePolicy(h_sp) self.led_status_config_ch1_max_sbox.setSizePolicy(h_sp) # Define layouts hlayout = QHBoxLayout() vlayout = QVBoxLayout() glayout = QGridLayout() layout = QVBoxLayout(self) hlayout_ch0_min_max = QHBoxLayout() hlayout_ch1_min_max = QHBoxLayout() # Populate layouts vlayout.addWidget(self.calibration_button) hlayout.addWidget(self.sample_rate_label) hlayout.addWidget(self.sample_rate_combo) vlayout.addLayout(hlayout) vlayout.addWidget(line1) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_min_label) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_min_sbox) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_max_label) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_max_sbox) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_min_label) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_min_sbox) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_max_label) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_max_sbox) glayout.addWidget(self.led_config_ch0_label, 0, 1, 1, 1) # R, C, RS, CS glayout.addWidget(self.led_config_ch1_label, 0, 2, 1, 1) glayout.addWidget(line2, 1, 0, 1, 3) glayout.addWidget(self.led_config_label, 2, 0, 1, 1) glayout.addWidget(self.led_config_ch0_combo, 2, 1, 1, 1) glayout.addWidget(self.led_config_ch1_combo, 2, 2, 1, 1) glayout.addWidget(self.led_status_config_label, 3, 0, 1, 1) glayout.addWidget(self.led_status_config_ch0_combo, 3, 1, 1, 1) glayout.addWidget(self.led_status_config_ch1_combo, 3, 2, 1, 1) glayout.addLayout(hlayout_ch0_min_max, 4, 1, 1, 1) glayout.addLayout(hlayout_ch1_min_max, 4, 2, 1, 1) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(vlayout) layout.addLayout(glayout) self.ui_group_ch_status_ch0 = [ self.led_status_config_ch0_combo, self.led_status_config_ch0_min_sbox, self.led_status_config_ch0_max_sbox ] self.ui_group_ch_status_ch1 = [ self.led_status_config_ch1_combo, self.led_status_config_ch1_min_sbox, self.led_status_config_ch1_max_sbox ] def start(self): async_call(self.analog_in.get_sample_rate, None, self.get_sample_rate_async, self.increase_error_count) for channel in range(2): async_call(self.analog_in.get_channel_led_config, channel, self.get_channel_led_config_async, self.increase_error_count, pass_arguments_to_result_callback=True) async_call(self.analog_in.get_channel_led_status_config, channel, self.get_channel_led_status_config_async, self.increase_error_count, pass_arguments_to_result_callback=True) self.cbe_voltage0.set_period(100) self.cbe_voltage1.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_voltage0.set_period(0) self.cbe_voltage1.set_period(0) self.plot_widget.stop = True def destroy(self): if self.calibration != None: self.calibration.close() @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletIndustrialDualAnalogInV2.DEVICE_IDENTIFIER def calibration_button_clicked(self): if self.calibration == None: self.calibration = Calibration(self) self.calibration_button.setEnabled(False) self.calibration.show() def sample_rate_combo_index_changed(self, index): async_call(self.analog_in.set_sample_rate, index, None, self.increase_error_count) def led_config_ch0_combo_changed(self, index): if index != self.analog_in.CHANNEL_LED_CONFIG_SHOW_CHANNEL_STATUS: for e in self.ui_group_ch_status_ch0: e.setEnabled(False) else: for e in self.ui_group_ch_status_ch0: e.setEnabled(True) self.analog_in.set_channel_led_config(0, index) def led_config_ch1_combo_changed(self, index): if index != self.analog_in.CHANNEL_LED_CONFIG_SHOW_CHANNEL_STATUS: for e in self.ui_group_ch_status_ch1: e.setEnabled(False) else: for e in self.ui_group_ch_status_ch1: e.setEnabled(True) self.analog_in.set_channel_led_config(1, index) def led_status_config_ch0_combo_changed(self, index): self.analog_in.set_channel_led_status_config( 0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), index) def led_status_config_ch1_combo_changed(self, index): self.analog_in.set_channel_led_status_config( 1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), index) def led_status_config_ch0_min_sbox_changed(self, value): self.analog_in.set_channel_led_status_config( 0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), self.led_status_config_ch0_combo.currentIndex()) def led_status_config_ch0_max_sbox_changed(self, value): self.analog_in.set_channel_led_status_config( 0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), self.led_status_config_ch0_combo.currentIndex()) def led_status_config_ch1_min_sbox_changed(self, value): self.analog_in.set_channel_led_status_config( 1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), self.led_status_config_ch1_combo.currentIndex()) def led_status_config_ch1_max_sbox_changed(self, value): self.analog_in.set_channel_led_status_config( 1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), self.led_status_config_ch1_combo.currentIndex()) def get_voltage_value0(self): return self.voltage_value[0] def get_voltage_value1(self): return self.voltage_value[1] def get_sample_rate_async(self, rate): self.sample_rate_combo.blockSignals(True) self.sample_rate_combo.setCurrentIndex(rate) self.sample_rate_combo.blockSignals(False) def get_channel_led_config_async(self, channel, config): self.led_config_ch0_combo.blockSignals(True) self.led_config_ch1_combo.blockSignals(True) if channel == 0: self.led_config_ch0_combo.setCurrentIndex(config) elif channel == 1: self.led_config_ch1_combo.setCurrentIndex(config) self.led_config_ch0_combo.blockSignals(False) self.led_config_ch1_combo.blockSignals(False) def get_channel_led_status_config_async(self, channel, config): self.led_status_config_ch0_combo.blockSignals(True) self.led_status_config_ch1_combo.blockSignals(True) if channel == 0: self.led_status_config_ch0_combo.setCurrentIndex(config.config) self.led_status_config_ch0_max_sbox.setValue(config.max) self.led_status_config_ch0_min_sbox.setValue(config.min) elif channel == 1: self.led_status_config_ch1_combo.setCurrentIndex(config.config) self.led_status_config_ch1_max_sbox.setValue(config.max) self.led_status_config_ch1_min_sbox.setValue(config.min) self.led_status_config_ch0_combo.blockSignals(False) self.led_status_config_ch1_combo.blockSignals(False) def cb_voltage(self, sensor, voltage): self.current_voltage[sensor].value = voltage / 1000.0
class LedgerAuthDialog(QDialog): def __init__(self, handler, keystore: Ledger_KeyStore, data: Dict[str, Any]): '''Ask user for 2nd factor authentication. Support text and security card methods. Use last method from settings, but support downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(600) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(keystore.cfg) self.dongle = keystore.get_client().dongle self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): self.cfg['mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 0: keystore.cfg = self.cfg self.update_dlg() def return_pin(): self.pin = (self.pintxt.text() if self.txdata['confirmationType'] == 1 else self.cardtxt.text()) self.pin.setText('') self.cardtxt.setText('') if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i),16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet("QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = PasswordLineEdit() self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; " "padding:15px 10px; border:none; font-size:20pt; }") self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(120) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] addr = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:] self.addrtext.setHtml(str(addr)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = PasswordLineEdit() self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.update_dlg() def populate_modes(self): self.modes.blockSignals(True) self.modes.clear() self.modes.addItem(_("Summary Text PIN (requires dongle replugging)") if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled")) if self.txdata['confirmationType'] > 1: self.modes.addItem(_("Security Card Challenge")) self.modes.blockSignals(False) def update_dlg(self): self.modes.setCurrentIndex(self.cfg['mode']) self.modebox.setVisible(True) self.helpmsg.setText(helpTxt[self.cfg['mode']]) self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100) self.helpmsg.setVisible(True) self.pinbox.setVisible(self.cfg['mode'] == 0) self.cardbox.setVisible(self.cfg['mode'] == 1) self.pintxt.setFocus(True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True) self.setMaximumHeight(200) def getDevice2FAMode(self): apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode try: mode = self.dongle.exchange( bytearray(apdu) ) return mode except BTChipException as e: logger.debug('Device getMode Failed') return 0x11
class ParamsByType(QWidget, MooseWidget): """ Has a QComboBox for the different allowed types. On switching type a new ParamsByGroup is shown. """ needBlockList = pyqtSignal(list) blockRenamed = pyqtSignal(object, str) changed = pyqtSignal() def __init__(self, block, **kwds): """ Constructor. Input: block[BlockInfo]: The block to show. """ super(ParamsByType, self).__init__(**kwds) self.block = block self.combo = QComboBox() self.types = [] self.type_params_map = {} self.table_stack = QStackedWidget() self.type_table_map = {} for t in sorted(self.block.types.keys()): self.types.append(t) params_list = [] for p in self.block.parameters_list: params_list.append(self.block.parameters[p]) t_block = self.block.types[t] for p in t_block.parameters_list: params_list.append(t_block.parameters[p]) self.type_params_map[t] = params_list self.combo.addItems(sorted(self.block.types.keys())) self.combo.currentTextChanged.connect(self.setBlockType) self.top_layout = WidgetUtils.addLayout(vertical=True) self.top_layout.addWidget(self.combo) self.top_layout.addWidget(self.table_stack) self.setLayout(self.top_layout) self.user_params = [] self.setDefaultBlockType() self.setup() def _syncUserParams(self, current, to): """ Sync user added parameters that are on the main block into each type ParamsByGroup. Input: current[ParamsByGroup]: The current group parameter table to[ParamsByGroup]: The new group parameter table """ ct = current.findTable("Main") tot = to.findTable("Main") if not ct or not tot or ct == tot: return # first remove user params in tot tot.removeUserParams() params = ct.getUserParams() tot.addUserParams(params) idx = ct.findRow("Name") if idx >= 0: name = ct.item(idx, 1).text() idx = tot.findRow("Name") if idx >= 0: tot.item(idx, 1).setText(name) def currentType(self): return self.combo.currentText() def save(self): """ Look at the user params in self.block.parameters. update the type tables Save type on block """ t = self.getTable() if t: t.save() self.block.setBlockType(self.combo.currentText()) def reset(self): t = self.getTable() t.reset() def getOrCreateTypeTable(self, type_name): """ Gets the table for the type name or create it if it doesn't exist. Input: type_name[str]: Name of the type Return: ParamsByGroup: The parameters corresponding to the type """ t = self.type_table_map.get(type_name) if t: return t t = ParamsByGroup(self.block, self.type_params_map.get(type_name, self.block.orderedParameters())) t.needBlockList.connect(self.needBlockList) t.blockRenamed.connect(self.blockRenamed) t.changed.connect(self.changed) self.type_table_map[type_name] = t self.table_stack.addWidget(t) return t def setDefaultBlockType(self): param = self.block.getParamInfo("type") if param and param.value: self.setBlockType(param.value) elif self.block.types: self.setBlockType(sorted(self.block.types.keys())[0]) def setBlockType(self, type_name): if type_name not in self.block.types: return t = self.getOrCreateTypeTable(type_name) t.updateWatchers() self.combo.blockSignals(True) self.combo.setCurrentText(type_name) self.combo.blockSignals(False) t.updateType(type_name) current = self.table_stack.currentWidget() self._syncUserParams(current, t) self.table_stack.setCurrentWidget(t) self.changed.emit() def addUserParam(self, param): t = self.table_stack.currentWidget() t.addUserParam(param) def setWatchedBlockList(self, path, children): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.setWatchedBlockList(path, children) def updateWatchers(self): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.updateWatchers() def getTable(self): return self.table_stack.currentWidget() def paramValue(self, name): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) if t.paramValue(name): return t.paramValue(name)
class LedgerAuthDialog(QDialog): def __init__(self, handler, data): '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods. Use last method from settings, but support new pairing and downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(650) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg) self.dongle = self.handler.win.wallet.get_keystore().get_client().dongle self.ws = None self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): if idx < 2 and self.ws: self.ws.stop() self.ws = None self.cfg['mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws: self.req_validation() if self.cfg['mode'] > 0: self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.update_dlg() def add_pairing(): self.do_pairing() def return_pin(): self.pin = self.pintxt.text() if self.txdata['confirmationType'] == 1 else self.cardtxt.text() if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i),16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) self.addPair = QPushButton(_("Pair")) self.addPair.setMaximumWidth(60) modelayout.addWidget(self.addPair) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.addPair.clicked.connect(add_pairing) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet("QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = QLineEdit() self.pintxt.setEchoMode(2) self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; font-family:monospace; }") self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(130) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] if not constants.net.TESTNET: text = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:] else: # pin needs to be created from mainnet address addr_mainnet = bitcoin.script_to_address(bitcoin.address_to_script(addr), net=constants.BitcoinMainnet) addr_mainnet = addr_mainnet[:i] + '<u><b>' + addr_mainnet[i:i+1] + '</u></b>' + addr_mainnet[i+1:] text = str(addr) + '\n' + str(addr_mainnet) self.addrtext.setHtml(str(text)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = QLineEdit() self.cardtxt.setEchoMode(2) self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.pairbox = QWidget() pairlayout = QVBoxLayout() self.pairbox.setLayout(pairlayout) pairhelp = QTextEdit(helpTxt[5]) pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }") pairhelp.setReadOnly(True) pairlayout.addWidget(pairhelp, 1) self.pairqr = QRCodeWidget() pairlayout.addWidget(self.pairqr, 4) self.pairbox.setVisible(False) vbox.addWidget(self.pairbox) self.update_dlg() if self.cfg['mode'] > 1 and not self.ws: self.req_validation() def populate_modes(self): self.modes.blockSignals(True) self.modes.clear() self.modes.addItem(_("Summary Text PIN (requires dongle replugging)") if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled")) if self.txdata['confirmationType'] > 1: self.modes.addItem(_("Security Card Challenge")) if not self.cfg['pair']: self.modes.addItem(_("Mobile - Not paired")) else: self.modes.addItem(_("Mobile - {}").format(self.cfg['pair'][1])) self.modes.blockSignals(False) def update_dlg(self): self.modes.setCurrentIndex(self.cfg['mode']) self.modebox.setVisible(True) self.addPair.setText(_("Pair") if not self.cfg['pair'] else _("Re-Pair")) self.addPair.setVisible(self.txdata['confirmationType'] > 2) self.helpmsg.setText(helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self.cfg['pair'] else 4]) self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100) self.pairbox.setVisible(False) self.helpmsg.setVisible(True) self.pinbox.setVisible(self.cfg['mode'] == 0) self.cardbox.setVisible(self.cfg['mode'] == 1) self.pintxt.setFocus(True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True) self.setMaximumHeight(400) def do_pairing(self): rng = os.urandom(16) pairID = (hexlify(rng) + hexlify(hashlib.sha256(rng).digest()[0:1])).decode('utf-8') self.pairqr.setData(pairID) self.modebox.setVisible(False) self.helpmsg.setVisible(False) self.pinbox.setVisible(False) self.cardbox.setVisible(False) self.pairbox.setVisible(True) self.pairqr.setMinimumSize(300,300) if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, pairID) self.ws.pairing_done.connect(self.pairing_done) self.ws.start() def pairing_done(self, data): if data is not None: self.cfg['pair'] = [ data['pairid'], data['name'], data['platform'] ] self.cfg['mode'] = 2 self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.pin = 'paired' self.accept() def req_validation(self): if self.cfg['pair'] and 'secureScreenData' in self.txdata: if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata) self.ws.req_updated.connect(self.req_updated) self.ws.start() def req_updated(self, pin): if pin == 'accepted': self.helpmsg.setText(helpTxt[3]) else: self.pin = str(pin) self.accept() def getDevice2FAMode(self): apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode try: mode = self.dongle.exchange( bytearray(apdu) ) return mode except BTChipException as e: debug_msg('Device getMode Failed') return 0x11 def closeEvent(self, evnt): debug_msg("CLOSE - Stop WS") if self.ws: self.ws.stop() if self.pairbox.isVisible(): evnt.ignore() self.update_dlg()
class StandardLibraryPage(QSplitter): """ The GUI for the standard library page of a project. """ # The page's label. label = "Standard Library" @property def project(self): """ The project property getter. """ return self._project @project.setter def project(self, value): """ The project property setter. """ if self._project != value: self._project = value self._update_page() def __init__(self): """ Initialise the page. """ super().__init__() self._project = None # Create the page's GUI. stdlib_pane = QWidget() stdlib_layout = QVBoxLayout() self._stdlib_edit = QTreeWidget( whatsThis="This shows the packages and modules in the target " "Python version's standard library. Check those " "packages and modules that are explicitly imported by " "the application. A module will be partially checked " "(and automatically included) if another module " "requires it.") self._stdlib_edit.setHeaderLabels(["Package"]) self._stdlib_edit.itemChanged.connect(self._module_changed) stdlib_layout.addWidget(self._stdlib_edit) stdlib_pane.setLayout(stdlib_layout) self.addWidget(stdlib_pane) extlib_pane = QWidget() extlib_layout = QVBoxLayout() extlib_sublayout = QFormLayout() self._version_edit = QComboBox( whatsThis="Select the target Python version. This will cause " "the standard library package hierarchy to be " "updated.") self._version_edit.addItems(get_supported_python_versions()) self._version_edit.currentIndexChanged.connect(self._version_changed) extlib_sublayout.addRow("Target Python version", self._version_edit) self._ssl_edit = QCheckBox( whatsThis="Enable SSL for the standard library modules " "that have optional support for it.", stateChanged=self._ssl_changed) extlib_sublayout.addRow("Enable optional SSL support", self._ssl_edit) extlib_layout.addLayout(extlib_sublayout) plat_gb = QGroupBox("Use standard Python shared library") plat_gb_layout = QVBoxLayout() self._platform_buttons = [] for scope, plat, subscopes in PLATFORM_SCOPES: plat_cb = QCheckBox(plat, whatsThis="Enable the use of the standard Python shared " "library on {0} rather than a statically compiled " "library.".format(plat), stateChanged=self._platforms_changed) plat_cb._scope = scope plat_gb_layout.addWidget(plat_cb) self._platform_buttons.append(plat_cb) plat_gb.setLayout(plat_gb_layout) extlib_layout.addWidget(plat_gb) self._extlib_edit = QTreeView( whatsThis="This is the list of external libraries that must " "be linked with the application. A library will only " "be enabled if a module in the standard library uses " "it. Double-click in the <b>DEFINES</b>, " "<b>INCLUDEPATH</b> and <b>LIBS</b> columns to modify " "the corresponding <tt>qmake</tt> variable as " "required. Values may be prefixed by a platform " "specific <tt>qmake</tt> scope.") self._extlib_edit.setRootIsDecorated(False) self._extlib_edit.setEditTriggers( QTreeView.DoubleClicked|QTreeView.SelectedClicked| QTreeView.EditKeyPressed) model = QStandardItemModel(self._extlib_edit) model.setHorizontalHeaderLabels( ("External Library", 'DEFINES', 'INCLUDEPATH', 'LIBS')) model.itemChanged.connect(self._extlib_changed) for extlib in external_libraries_metadata: name_itm = QStandardItem(extlib.user_name) extlib._items = (name_itm, QStandardItem(), QStandardItem(), QStandardItem()) model.appendRow(extlib._items) self._extlib_edit.setModel(model) for col in range(3): self._extlib_edit.resizeColumnToContents(col) extlib_layout.addWidget(self._extlib_edit) self._ignore_extlib_changes = False extlib_pane.setLayout(extlib_layout) self.addWidget(extlib_pane) def _update_page(self): """ Update the page using the current project. """ project = self.project blocked = self._version_edit.blockSignals(True) self._version_edit.setCurrentIndex( get_supported_python_version_index( project.python_target_version)) self._version_edit.blockSignals(blocked) blocked = self._ssl_edit.blockSignals(True) self._ssl_edit.setCheckState( Qt.Checked if project.python_ssl else Qt.Unchecked) self._ssl_edit.blockSignals(blocked) for plat in self._platform_buttons: blocked = plat.blockSignals(True) plat.setCheckState( Qt.Checked if plat._scope in project.python_use_platform else Qt.Unchecked) plat.blockSignals(blocked) self._update_extlib_editor() self._update_stdlib_editor() def _update_stdlib_editor(self): """ Update the standard library module editor. """ project = self.project editor = self._stdlib_edit metadata = get_python_metadata(project.python_target_version) blocked = editor.blockSignals(True) editor.clear() def add_module(name, module, parent): itm = QTreeWidgetItem(parent, name.split('.')[-1:]) itm.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) itm._name = name # Handle any sub-modules. if module.modules is not None: for submodule_name in module.modules: # We assume that a missing sub-module is because it is not # in the current version rather than bad meta-data. submodule = metadata.get(submodule_name) if submodule is not None: add_module(submodule_name, submodule, itm) for name, module in metadata.items(): if not module.internal and '.' not in name: add_module(name, module, editor) editor.sortItems(0, Qt.AscendingOrder) editor.blockSignals(blocked) self._set_dependencies() def _set_dependencies(self): """ Set the dependency information. """ project = self.project editor = self._stdlib_edit required_modules, required_libraries = project.get_stdlib_requirements() blocked = editor.blockSignals(True) it = QTreeWidgetItemIterator(editor) itm = it.value() while itm is not None: external = required_modules.get(itm._name) expanded = False if external is None: state = Qt.Unchecked elif external: state = Qt.Checked expanded = True else: state = Qt.PartiallyChecked itm.setCheckState(0, state) # Make sure every explicitly checked item is visible. if expanded: parent = itm.parent() while parent is not None: parent.setExpanded(True) parent = parent.parent() it += 1 itm = it.value() editor.blockSignals(blocked) model = self._extlib_edit.model() # Note that we can't simply block the model's signals as this would # interfere with the model/view interactions. self._ignore_extlib_changes = True for extlib in external_libraries_metadata: if extlib.name in required_libraries: for idx, itm in enumerate(extlib._items): itm.setFlags( Qt.ItemIsEnabled|Qt.ItemIsEditable if idx != 0 else Qt.ItemIsEnabled) else: for itm in extlib._items: itm.setFlags(Qt.NoItemFlags) self._ignore_extlib_changes = False def _update_extlib_editor(self): """ Update the external library editor. """ project = self.project model = self._extlib_edit.model() blocked = model.blockSignals(True) for extlib in external_libraries_metadata: _, defs, incp, libs = extlib._items for prj_extlib in project.external_libraries: if prj_extlib.name == extlib.name: defs.setText(prj_extlib.defines) incp.setText(prj_extlib.includepath) libs.setText(prj_extlib.libs) break else: defs.setText('') incp.setText('') libs.setText(extlib.libs) model.blockSignals(blocked) def _version_changed(self, idx): """ Invoked when the target Python version changes. """ project = self.project project.python_target_version = get_supported_python_version(idx) self._update_page() project.modified = True def _ssl_changed(self, state): """ Invoked when the SSL support changes. """ project = self.project project.python_ssl = (state == Qt.Checked) self._set_dependencies() project.modified = True def _platforms_changed(self, state): """ Invoked when the platforms change. """ project = self._project project.python_use_platform = [] for plat in self._platform_buttons: if plat.checkState() == Qt.Checked: project.python_use_platform.append(plat._scope) project.modified = True def _module_changed(self, itm, col): """ Invoked when a standard library module has changed. """ project = self._project name = itm._name if name in project.standard_library: project.standard_library.remove(name) else: project.standard_library.append(name) self._set_dependencies() project.modified = True def _extlib_changed(self, itm): """ Invoked when an external library has changed. """ if self._ignore_extlib_changes: return self._ignore_extlib_changes = True project = self.project idx = self._extlib_edit.model().indexFromItem(itm) extlib = external_libraries_metadata[idx.row()] col = idx.column() # Get the project entry, creating it if necessary. for prj_extlib in project.external_libraries: if prj_extlib.name == extlib.name: break else: prj_extlib = ExternalLibrary(extlib.name, '', '', extlib.libs) project.external_libraries.append(prj_extlib) # Update the project. text = itm.text().strip() if col == 1: prj_extlib.defines = text elif col == 2: prj_extlib.includepath = text elif col == 3: if text == '': text = extlib.libs itm.setText(text) prj_extlib.libs = text # If the project entry corresponds to the default then remove it. if prj_extlib.defines == '' and prj_extlib.includepath == '' and prj_extlib.libs == extlib.libs: project.external_libraries.remove(prj_extlib) project.modified = True self._ignore_extlib_changes = False
class DebugViewer(QWidget): """ Class implementing a widget conatining various debug related views. The individual tabs contain the interpreter shell (optional), the filesystem browser (optional), the two variables viewers (global and local), a breakpoint viewer, a watch expression viewer and the exception logger. Additionally a list of all threads is shown. @signal sourceFile(string, int) emitted to open a source file at a line """ sourceFile = pyqtSignal(str, int) def __init__(self, debugServer, docked, vm, parent=None, embeddedShell=True, embeddedBrowser=True): """ Constructor @param debugServer reference to the debug server object @param docked flag indicating a dock window @param vm reference to the viewmanager object @param parent parent widget (QWidget) @param embeddedShell flag indicating whether the shell should be included. This flag is set to False by those layouts, that have the interpreter shell in a separate window. @param embeddedBrowser flag indicating whether the file browser should be included. This flag is set to False by those layouts, that have the file browser in a separate window or embedded in the project browser instead. """ super(DebugViewer, self).__init__(parent) self.debugServer = debugServer self.debugUI = None self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) self.__mainLayout = QVBoxLayout() self.__mainLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.__mainLayout) self.__tabWidget = E5TabWidget() self.__mainLayout.addWidget(self.__tabWidget) self.embeddedShell = embeddedShell if embeddedShell: from QScintilla.Shell import ShellAssembly # add the interpreter shell self.shellAssembly = ShellAssembly(debugServer, vm, False) self.shell = self.shellAssembly.shell() index = self.__tabWidget.addTab( self.shellAssembly, UI.PixmapCache.getIcon("shell.png"), '') self.__tabWidget.setTabToolTip(index, self.shell.windowTitle()) self.embeddedBrowser = embeddedBrowser if embeddedBrowser: from UI.Browser import Browser # add the browser self.browser = Browser() index = self.__tabWidget.addTab( self.browser, UI.PixmapCache.getIcon("browser.png"), '') self.__tabWidget.setTabToolTip(index, self.browser.windowTitle()) from .VariablesViewer import VariablesViewer # add the global variables viewer self.glvWidget = QWidget() self.glvWidgetVLayout = QVBoxLayout(self.glvWidget) self.glvWidgetVLayout.setContentsMargins(0, 0, 0, 0) self.glvWidgetVLayout.setSpacing(3) self.glvWidget.setLayout(self.glvWidgetVLayout) self.globalsViewer = VariablesViewer(self.glvWidget, True) self.glvWidgetVLayout.addWidget(self.globalsViewer) self.glvWidgetHLayout = QHBoxLayout() self.glvWidgetHLayout.setContentsMargins(3, 3, 3, 3) self.globalsFilterEdit = QLineEdit(self.glvWidget) self.globalsFilterEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed) self.glvWidgetHLayout.addWidget(self.globalsFilterEdit) self.globalsFilterEdit.setToolTip( self.tr("Enter regular expression patterns separated by ';'" " to define variable filters. ")) self.globalsFilterEdit.setWhatsThis( self.tr("Enter regular expression patterns separated by ';'" " to define variable filters. All variables and" " class attributes matched by one of the expressions" " are not shown in the list above.")) self.setGlobalsFilterButton = QPushButton( self.tr('Set'), self.glvWidget) self.glvWidgetHLayout.addWidget(self.setGlobalsFilterButton) self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout) index = self.__tabWidget.addTab( self.glvWidget, UI.PixmapCache.getIcon("globalVariables.png"), '') self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle()) self.setGlobalsFilterButton.clicked.connect( self.__setGlobalsFilter) self.globalsFilterEdit.returnPressed.connect(self.__setGlobalsFilter) # add the local variables viewer self.lvWidget = QWidget() self.lvWidgetVLayout = QVBoxLayout(self.lvWidget) self.lvWidgetVLayout.setContentsMargins(0, 0, 0, 0) self.lvWidgetVLayout.setSpacing(3) self.lvWidget.setLayout(self.lvWidgetVLayout) self.lvWidgetHLayout1 = QHBoxLayout() self.lvWidgetHLayout1.setContentsMargins(3, 3, 3, 3) self.stackComboBox = QComboBox(self.lvWidget) self.stackComboBox.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed) self.lvWidgetHLayout1.addWidget(self.stackComboBox) self.sourceButton = QPushButton(self.tr('Source'), self.lvWidget) self.lvWidgetHLayout1.addWidget(self.sourceButton) self.sourceButton.setEnabled(False) self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout1) self.localsViewer = VariablesViewer(self.lvWidget, False) self.lvWidgetVLayout.addWidget(self.localsViewer) self.lvWidgetHLayout2 = QHBoxLayout() self.lvWidgetHLayout2.setContentsMargins(3, 3, 3, 3) self.localsFilterEdit = QLineEdit(self.lvWidget) self.localsFilterEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed) self.lvWidgetHLayout2.addWidget(self.localsFilterEdit) self.localsFilterEdit.setToolTip( self.tr( "Enter regular expression patterns separated by ';' to define " "variable filters. ")) self.localsFilterEdit.setWhatsThis( self.tr( "Enter regular expression patterns separated by ';' to define " "variable filters. All variables and class attributes matched" " by one of the expressions are not shown in the list above.")) self.setLocalsFilterButton = QPushButton( self.tr('Set'), self.lvWidget) self.lvWidgetHLayout2.addWidget(self.setLocalsFilterButton) self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2) index = self.__tabWidget.addTab( self.lvWidget, UI.PixmapCache.getIcon("localVariables.png"), '') self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle()) self.sourceButton.clicked.connect(self.__showSource) self.stackComboBox.currentIndexChanged[int].connect( self.__frameSelected) self.setLocalsFilterButton.clicked.connect(self.__setLocalsFilter) self.localsFilterEdit.returnPressed.connect(self.__setLocalsFilter) from .CallStackViewer import CallStackViewer # add the call stack viewer self.callStackViewer = CallStackViewer(self.debugServer) index = self.__tabWidget.addTab( self.callStackViewer, UI.PixmapCache.getIcon("step.png"), "") self.__tabWidget.setTabToolTip( index, self.callStackViewer.windowTitle()) self.callStackViewer.sourceFile.connect(self.sourceFile) self.callStackViewer.frameSelected.connect( self.__callStackFrameSelected) from .CallTraceViewer import CallTraceViewer # add the call trace viewer self.callTraceViewer = CallTraceViewer(self.debugServer) index = self.__tabWidget.addTab( self.callTraceViewer, UI.PixmapCache.getIcon("callTrace.png"), "") self.__tabWidget.setTabToolTip( index, self.callTraceViewer.windowTitle()) self.callTraceViewer.sourceFile.connect(self.sourceFile) from .BreakPointViewer import BreakPointViewer # add the breakpoint viewer self.breakpointViewer = BreakPointViewer() self.breakpointViewer.setModel(self.debugServer.getBreakPointModel()) index = self.__tabWidget.addTab( self.breakpointViewer, UI.PixmapCache.getIcon("breakpoints.png"), '') self.__tabWidget.setTabToolTip( index, self.breakpointViewer.windowTitle()) self.breakpointViewer.sourceFile.connect(self.sourceFile) from .WatchPointViewer import WatchPointViewer # add the watch expression viewer self.watchpointViewer = WatchPointViewer() self.watchpointViewer.setModel(self.debugServer.getWatchPointModel()) index = self.__tabWidget.addTab( self.watchpointViewer, UI.PixmapCache.getIcon("watchpoints.png"), '') self.__tabWidget.setTabToolTip( index, self.watchpointViewer.windowTitle()) from .ExceptionLogger import ExceptionLogger # add the exception logger self.exceptionLogger = ExceptionLogger() index = self.__tabWidget.addTab( self.exceptionLogger, UI.PixmapCache.getIcon("exceptions.png"), '') self.__tabWidget.setTabToolTip( index, self.exceptionLogger.windowTitle()) if self.embeddedShell: self.__tabWidget.setCurrentWidget(self.shellAssembly) else: if self.embeddedBrowser: self.__tabWidget.setCurrentWidget(self.browser) else: self.__tabWidget.setCurrentWidget(self.glvWidget) # add the threads viewer self.__mainLayout.addWidget(QLabel(self.tr("Threads:"))) self.__threadList = QTreeWidget() self.__threadList.setHeaderLabels( [self.tr("ID"), self.tr("Name"), self.tr("State"), ""]) self.__threadList.setSortingEnabled(True) self.__mainLayout.addWidget(self.__threadList) self.__doThreadListUpdate = True self.__threadList.currentItemChanged.connect(self.__threadSelected) self.__mainLayout.setStretchFactor(self.__tabWidget, 5) self.__mainLayout.setStretchFactor(self.__threadList, 1) self.currPage = None self.currentStack = None self.framenr = 0 self.debugServer.clientStack.connect(self.handleClientStack) self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") self.sourceButton.setVisible(not self.__autoViewSource) def preferencesChanged(self): """ Public slot to handle the preferencesChanged signal. """ self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") self.sourceButton.setVisible(not self.__autoViewSource) def setDebugger(self, debugUI): """ Public method to set a reference to the Debug UI. @param debugUI reference to the DebugUI object (DebugUI) """ self.debugUI = debugUI self.debugUI.clientStack.connect(self.handleClientStack) self.callStackViewer.setDebugger(debugUI) def handleResetUI(self): """ Public method to reset the SBVviewer. """ self.globalsViewer.handleResetUI() self.localsViewer.handleResetUI() self.__setGlobalsFilter() self.__setLocalsFilter() self.sourceButton.setEnabled(False) self.currentStack = None self.stackComboBox.clear() self.__threadList.clear() if self.embeddedShell: self.__tabWidget.setCurrentWidget(self.shellAssembly) else: if self.embeddedBrowser: self.__tabWidget.setCurrentWidget(self.browser) else: self.__tabWidget.setCurrentWidget(self.glvWidget) self.breakpointViewer.handleResetUI() def handleRawInput(self): """ Public slot to handle the switch to the shell in raw input mode. """ if self.embeddedShell: self.saveCurrentPage() self.__tabWidget.setCurrentWidget(self.shellAssembly) def initCallStackViewer(self, projectMode): """ Public method to initialize the call stack viewer. @param projectMode flag indicating to enable the project mode (boolean) """ self.callStackViewer.clear() self.callStackViewer.setProjectMode(projectMode) def isCallTraceEnabled(self): """ Public method to get the state of the call trace function. @return flag indicating the state of the call trace function (boolean) """ return self.callTraceViewer.isCallTraceEnabled() def clearCallTrace(self): """ Public method to clear the recorded call trace. """ self.callTraceViewer.clear() def setCallTraceToProjectMode(self, enabled): """ Public slot to set the call trace viewer to project mode. In project mode the call trace info is shown with project relative path names. @param enabled flag indicating to enable the project mode (boolean) """ self.callTraceViewer.setProjectMode(enabled) def showVariables(self, vlist, globals): """ Public method to show the variables in the respective window. @param vlist list of variables to display @param globals flag indicating global/local state """ if globals: self.globalsViewer.showVariables(vlist, self.framenr) else: self.localsViewer.showVariables(vlist, self.framenr) def showVariable(self, vlist, globals): """ Public method to show the variables in the respective window. @param vlist list of variables to display @param globals flag indicating global/local state """ if globals: self.globalsViewer.showVariable(vlist) else: self.localsViewer.showVariable(vlist) def showVariablesTab(self, globals): """ Public method to make a variables tab visible. @param globals flag indicating global/local state """ if globals: self.__tabWidget.setCurrentWidget(self.glvWidget) else: self.__tabWidget.setCurrentWidget(self.lvWidget) def saveCurrentPage(self): """ Public slot to save the current page. """ self.currPage = self.__tabWidget.currentWidget() def restoreCurrentPage(self): """ Public slot to restore the previously saved page. """ if self.currPage is not None: self.__tabWidget.setCurrentWidget(self.currPage) def handleClientStack(self, stack): """ Public slot to show the call stack of the program being debugged. @param stack list of tuples with call stack data (file name, line number, function name, formatted argument/values list) """ block = self.stackComboBox.blockSignals(True) self.framenr = 0 self.stackComboBox.clear() self.currentStack = stack self.sourceButton.setEnabled(len(stack) > 0) for s in stack: # just show base filename to make it readable s = (os.path.basename(s[0]), s[1], s[2]) self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s)) self.stackComboBox.blockSignals(block) def setVariablesFilter(self, globalsFilter, localsFilter): """ Public slot to set the local variables filter. @param globalsFilter filter list for global variable types (list of int) @param localsFilter filter list for local variable types (list of int) """ self.globalsFilter = globalsFilter self.localsFilter = localsFilter def __showSource(self): """ Private slot to handle the source button press to show the selected file. """ index = self.stackComboBox.currentIndex() if index > -1 and self.currentStack: s = self.currentStack[index] self.sourceFile.emit(s[0], int(s[1])) def __frameSelected(self, frmnr): """ Private slot to handle the selection of a new stack frame number. @param frmnr frame number (0 is the current frame) (int) """ self.framenr = frmnr self.debugServer.remoteClientVariables(0, self.localsFilter, frmnr) if self.__autoViewSource: self.__showSource() def __setGlobalsFilter(self): """ Private slot to set the global variable filter. """ filter = self.globalsFilterEdit.text() self.debugServer.remoteClientSetFilter(1, filter) self.debugServer.remoteClientVariables(2, self.globalsFilter) def __setLocalsFilter(self): """ Private slot to set the local variable filter. """ filter = self.localsFilterEdit.text() self.debugServer.remoteClientSetFilter(0, filter) if self.currentStack: self.debugServer.remoteClientVariables( 0, self.localsFilter, self.framenr) def handleDebuggingStarted(self): """ Public slot to handle the start of a debugging session. This slot sets the variables filter expressions. """ self.__setGlobalsFilter() self.__setLocalsFilter() self.showVariablesTab(False) def currentWidget(self): """ Public method to get a reference to the current widget. @return reference to the current widget (QWidget) """ return self.__tabWidget.currentWidget() def setCurrentWidget(self, widget): """ Public slot to set the current page based on the given widget. @param widget reference to the widget (QWidget) """ self.__tabWidget.setCurrentWidget(widget) def showThreadList(self, currentID, threadList): """ Public method to show the thread list. @param currentID id of the current thread (integer) @param threadList list of dictionaries containing the thread data """ citm = None self.__threadList.clear() for thread in threadList: if thread['broken']: state = self.tr("waiting at breakpoint") else: state = self.tr("running") itm = QTreeWidgetItem(self.__threadList, ["{0:d}".format(thread['id']), thread['name'], state]) if thread['id'] == currentID: citm = itm self.__threadList.header().resizeSections(QHeaderView.ResizeToContents) self.__threadList.header().setStretchLastSection(True) if citm: self.__doThreadListUpdate = False self.__threadList.setCurrentItem(citm) self.__doThreadListUpdate = True def __threadSelected(self, current, previous): """ Private slot to handle the selection of a thread in the thread list. @param current reference to the new current item (QTreeWidgetItem) @param previous reference to the previous current item (QTreeWidgetItem) """ if current is not None and self.__doThreadListUpdate: tid = int(current.text(0)) self.debugServer.remoteSetThread(tid) def __callStackFrameSelected(self, frameNo): """ Private slot to handle the selection of a call stack entry of the call stack viewer. @param frameNo frame number (index) of the selected entry (integer) """ if frameNo >= 0: self.stackComboBox.setCurrentIndex(frameNo)
class FontToolBar(QToolBar): def __init__(self, pointSize, parent=None): super(FontToolBar, self).__init__(parent) auxiliaryWidth = self.fontMetrics().width('0') * 8 self.leftTextField = QLineEdit(self) self.leftTextField.setMaximumWidth(auxiliaryWidth) self.textField = QComboBox(self) self.textField.setEditable(True) completer = self.textField.completer() completer.setCaseSensitivity(Qt.CaseSensitive) self.textField.setCompleter(completer) # XXX: had to use Maximum because Preferred did entend the widget(?) self.textField.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) items = QSettings().value("metricsWindow/comboBoxItems", comboBoxItems, str) self.textField.addItems(items) self.rightTextField = QLineEdit(self) self.rightTextField.setMaximumWidth(auxiliaryWidth) self.leftTextField.textEdited.connect(self.textField.editTextChanged) self.rightTextField.textEdited.connect(self.textField.editTextChanged) self.comboBox = QComboBox(self) self.comboBox.setEditable(True) self.comboBox.setValidator(QIntValidator(self)) for p in pointSizes: self.comboBox.addItem(str(p)) self.comboBox.setEditText(str(pointSize)) self.configBar = QPushButton(self) self.configBar.setFlat(True) self.configBar.setIcon(QIcon(":/resources/settings.svg")) self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") self.toolsMenu = QMenu(self) showKerning = self.toolsMenu.addAction( "Show Kerning", self.showKerning) showKerning.setCheckable(True) showMetrics = self.toolsMenu.addAction( "Show Metrics", self.showMetrics) showMetrics.setCheckable(True) self.toolsMenu.addSeparator() wrapLines = self.toolsMenu.addAction("Wrap lines", self.wrapLines) wrapLines.setCheckable(True) noWrapLines = self.toolsMenu.addAction("No wrap", self.noWrapLines) noWrapLines.setCheckable(True) self.toolsMenu.addSeparator() verticalFlip = self.toolsMenu.addAction( "Vertical flip", self.verticalFlip) verticalFlip.setCheckable(True) """ lineHeight = QWidgetAction(self.toolsMenu) lineHeight.setText("Line height:") lineHeightSlider = QSlider(Qt.Horizontal, self) # QSlider works with integers so we'll just divide by 100 what comes # out of it lineHeightSlider.setMinimum(80) lineHeightSlider.setMaximum(160) lineHeightSlider.setValue(100) #lineHeightSlider.setContentsMargins(30, 0, 30, 0) lineHeightSlider.valueChanged.connect(self.lineHeight) lineHeight.setDefaultWidget(lineHeightSlider) self.toolsMenu.addAction(lineHeight) """ wrapLinesGroup = QActionGroup(self) wrapLinesGroup.addAction(wrapLines) wrapLinesGroup.addAction(noWrapLines) wrapLines.setChecked(True) # self.toolsMenu.setActiveAction(wrapLines) self.configBar.setMenu(self.toolsMenu) self.addWidget(self.leftTextField) self.addWidget(self.textField) self.addWidget(self.rightTextField) self.addWidget(self.comboBox) self.addWidget(self.configBar) def showEvent(self, event): super(FontToolBar, self).showEvent(event) self.textField.setFocus(True) def setPointSize(self, pointSize): self.comboBox.blockSignals(True) self.comboBox.setEditText(str(pointSize)) self.comboBox.blockSignals(False) def showKerning(self): action = self.sender() self.parent().canvas.setShowKerning(action.isChecked()) def showMetrics(self): action = self.sender() self.parent().canvas.setShowMetrics(action.isChecked()) def verticalFlip(self): action = self.sender() self.parent().canvas.setVerticalFlip(action.isChecked()) def lineHeight(self, value): self.parent().canvas.setLineHeight(value / 100) def wrapLines(self): self.parent().canvas.setWrapLines(True) def noWrapLines(self): self.parent().canvas.setWrapLines(False)
class DistanceIRV2(COMCUPluginBase): NUM_VALUES = 512 DIVIDER = 2**12//NUM_VALUES def __init__(self, *args): super().__init__(BrickletDistanceIRV2, *args) self.dist = self.device self.cbe_distance = CallbackEmulator(self.dist.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_analog_value = CallbackEmulator(self.dist.get_analog_value, None, self.cb_analog_value, self.increase_error_count) self.analog_label = AnalogLabel('Analog Value:') hlayout = QHBoxLayout() self.average_label = QLabel('Moving Average Length:') self.average_spin = QSpinBox() self.average_spin.setMinimum(1) self.average_spin.setMaximum(1000) self.average_spin.setSingleStep(1) self.average_spin.setValue(25) self.average_spin.editingFinished.connect(self.average_spin_finished) self.sensor_label = QLabel('Sensor Type:') self.sensor_combo = QComboBox() self.sensor_combo.addItem('2Y0A41 (4-30cm)') self.sensor_combo.addItem('2Y0A21 (10-80cm)') self.sensor_combo.addItem('2Y0A02 (20-150cm)') self.sensor_combo.currentIndexChanged.connect(self.sensor_combo_changed) hlayout.addWidget(self.average_label) hlayout.addWidget(self.average_spin) hlayout.addStretch() hlayout.addWidget(self.sensor_label) hlayout.addWidget(self.sensor_combo) self.current_distance = CurveValueWrapper() # float, cm plots = [('Distance', Qt.red, self.current_distance, '{} cm'.format)] self.plot_widget = PlotWidget('Distance [cm]', plots, extra_key_widgets=[self.analog_label], y_resolution=0.1) line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def sensor_combo_changed(self, index): self.dist.set_sensor_type(index) def average_spin_finished(self): self.dist.set_moving_average_configuration(self.average_spin.value()) def get_moving_average_configuration_async(self, average): self.average_spin.blockSignals(True) self.average_spin.setValue(average) self.average_spin.blockSignals(False) def get_sensor_type_async(self, sensor): self.sensor_combo.blockSignals(True) self.sensor_combo.setCurrentIndex(sensor) self.sensor_combo.blockSignals(False) def start(self): async_call(self.dist.get_moving_average_configuration, None, self.get_moving_average_configuration_async, self.increase_error_count) async_call(self.dist.get_sensor_type, None, self.get_sensor_type_async, self.increase_error_count) self.cbe_distance.set_period(10) self.cbe_analog_value.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_analog_value.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletDistanceIRV2.DEVICE_IDENTIFIER def cb_distance(self, distance): self.current_distance.value = distance / 10.0 def cb_analog_value(self, analog_value): self.analog_label.setText(analog_value)
class AccountDialog(QDialog): selected = QtCore.pyqtSignal(int, bool) aborted = QtCore.pyqtSignal() def __init__(self, parent, accountman): QDialog.__init__(self, parent) self.accountman = accountman layout = QVBoxLayout() self.setWindowTitle('Select Account') # Create list self.table = QTableWidget() self.table.horizontalHeader().setHighlightSections(False) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table.verticalHeader().hide() self.table.setGridStyle(QtCore.Qt.NoPen) self.table.doubleClicked.connect(self.select) bottom_layout = QHBoxLayout() self.remember_chk = QCheckBox('Remember') cancel_btn = QPushButton('Cancel') cancel_btn.clicked.connect(self.cancel) add_btn = QPushButton('Add') add_btn.clicked.connect(self.add) self.edit_btns = QComboBox() self.edit_btns.blockSignals(True) self.edit_btns.addItem('Edit...') self.edit_btns.addItem('Update') self.edit_btns.addItem('Delete') self.edit_btns.addItem('Purge') self.edit_btns.setItemData(1, 'Change the local password/PIN for this account', QtCore.Qt.ToolTipRole) self.edit_btns.setItemData(2, 'Remove this account from Trackma', QtCore.Qt.ToolTipRole) self.edit_btns.setItemData(3, 'Clear local DB for this account', QtCore.Qt.ToolTipRole) self.edit_btns.setCurrentIndex(0) self.edit_btns.blockSignals(False) self.edit_btns.activated.connect(self.s_edit) select_btn = QPushButton('Select') select_btn.clicked.connect(self.select) select_btn.setDefault(True) bottom_layout.addWidget(self.remember_chk) bottom_layout.addWidget(cancel_btn) bottom_layout.addWidget(add_btn) bottom_layout.addWidget(self.edit_btns) bottom_layout.addWidget(select_btn) # Get icons self.icons = dict() for libname, lib in utils.available_libs.items(): self.icons[libname] = QtGui.QIcon(lib[1]) # Populate list self.update() self.rebuild() # Finish layout layout.addWidget(self.table) layout.addLayout(bottom_layout) self.setLayout(layout) def update(self): self.remember_chk.setChecked(self.accountman.get_default() is not None) def add(self): result = AccountAddDialog.do(icons=self.icons) if result: (username, password, api) = result self.accountman.add_account(username, password, api) self.rebuild() def edit(self): self.edit_btns.blockSignals(True) self.edit_btns.setCurrentIndex(0) self.edit_btns.blockSignals(False) try: selected_account_num = self.table.selectedItems()[0].num acct = self.accountman.get_account(selected_account_num) result = AccountAddDialog.do(icons=self.icons, edit=True, username=acct['username'], password=acct['password'], api=acct['api']) if result: (username, password, api) = result self.accountman.edit_account(selected_account_num, username, password, api) self.rebuild() except IndexError: self._error("Please select an account.") def delete(self): try: selected_account_num = self.table.selectedItems()[0].num reply = QMessageBox.question(self, 'Confirmation', 'Do you want to delete the selected account?', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.accountman.delete_account(selected_account_num) self.rebuild() except IndexError: self._error("Please select an account.") def purge(self): try: selected_account_num = self.table.selectedItems()[0].num reply = QMessageBox.question(self, 'Confirmation', 'Do you want to purge the selected account\'s local data?', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.accountman.purge_account(selected_account_num) self.rebuild() except IndexError: self._error("Please select an account.") def s_edit(self, index): if index is 1: self.edit() elif index is 2: self.delete() elif index is 3: self.purge() def rebuild(self): self.table.clear() columns = ['Username', 'Site'] self.table.setColumnCount(len(columns)) self.table.setHorizontalHeaderLabels(columns) self.table.setRowCount(len(self.accountman.accounts['accounts'])) accounts = self.accountman.get_accounts() i = 0 for k, account in accounts: self.table.setRowHeight(i, QtGui.QFontMetrics(self.table.font()).height() + 2) self.table.setItem(i, 0, AccountItem(k, account['username'])) self.table.setItem(i, 1, AccountItem(k, account['api'], self.icons.get(account['api']))) i += 1 if pyqt_version is 5: self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) else: self.table.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) def select(self, checked): try: selected_account_num = self.table.selectedItems()[0].num self.selected.emit(selected_account_num, self.remember_chk.isChecked()) self.close() except IndexError: self._error("Please select an account.") def cancel(self, checked): self.aborted.emit() self.close() def _error(self, msg): QMessageBox.critical(self, 'Error', str(msg), QMessageBox.Ok)
class ConditionDialog(QDialog): """Dialog for defining field condition tests. Used for defining conditional types (modal), for finding by condition (nonmodal) and for filtering by condition (nonmodal). """ dialogShown = pyqtSignal(bool) def __init__(self, dialogType, caption, nodeFormat=None, parent=None): """Create the conditional dialog. Arguments: dialogType -- either typeDialog, findDialog or filterDialog caption -- the window title for this dialog nodeFormat -- the current node format for the typeDialog parent -- the parent overall dialog """ super().__init__(parent) self.setWindowTitle(caption) self.dialogType = dialogType self.ruleList = [] self.combiningBoxes = [] self.typeCombo = None self.resultLabel = None self.endFilterButton = None self.fieldNames = [] if nodeFormat: self.fieldNames = nodeFormat.fieldNames() topLayout = QVBoxLayout(self) if dialogType == FindDialogType.typeDialog: self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) else: self.setAttribute(Qt.WA_QuitOnClose, False) self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) typeBox = QGroupBox(_('Node Type')) topLayout.addWidget(typeBox) typeLayout = QVBoxLayout(typeBox) self.typeCombo = QComboBox() typeLayout.addWidget(self.typeCombo) self.typeCombo.currentIndexChanged.connect(self.updateDataType) self.mainLayout = QVBoxLayout() topLayout.addLayout(self.mainLayout) upCtrlLayout = QHBoxLayout() topLayout.addLayout(upCtrlLayout) addButton = QPushButton(_('&Add New Rule')) upCtrlLayout.addWidget(addButton) addButton.clicked.connect(self.addNewRule) self.removeButton = QPushButton(_('&Remove Rule')) upCtrlLayout.addWidget(self.removeButton) self.removeButton.clicked.connect(self.removeRule) upCtrlLayout.addStretch() if dialogType == FindDialogType.typeDialog: okButton = QPushButton(_('&OK')) upCtrlLayout.addWidget(okButton) okButton.clicked.connect(self.accept) cancelButton = QPushButton(_('&Cancel')) upCtrlLayout.addWidget(cancelButton) cancelButton.clicked.connect(self.reject) else: self.removeButton.setEnabled(False) saveBox = QGroupBox(_('Saved Rules')) topLayout.addWidget(saveBox) saveLayout = QVBoxLayout(saveBox) self.saveListBox = SmallListWidget() saveLayout.addWidget(self.saveListBox) self.saveListBox.itemDoubleClicked.connect(self.loadSavedRule) nameLayout = QHBoxLayout() saveLayout.addLayout(nameLayout) label = QLabel(_('Name:')) nameLayout.addWidget(label) self.saveNameEdit = QLineEdit() nameLayout.addWidget(self.saveNameEdit) self.saveNameEdit.textChanged.connect(self.updateSaveEnable) saveButtonLayout = QHBoxLayout() saveLayout.addLayout(saveButtonLayout) self.loadSavedButton = QPushButton(_('&Load')) saveButtonLayout.addWidget(self.loadSavedButton) self.loadSavedButton.clicked.connect(self.loadSavedRule) self.saveButton = QPushButton(_('&Save')) saveButtonLayout.addWidget(self.saveButton) self.saveButton.clicked.connect(self.saveRule) self.saveButton.setEnabled(False) self.delSavedButton = QPushButton(_('&Delete')) saveButtonLayout.addWidget(self.delSavedButton) self.delSavedButton.clicked.connect(self.deleteRule) saveButtonLayout.addStretch() if dialogType == FindDialogType.findDialog: self.resultLabel = QLabel() topLayout.addWidget(self.resultLabel) lowCtrlLayout = QHBoxLayout() topLayout.addLayout(lowCtrlLayout) if dialogType == FindDialogType.findDialog: previousButton = QPushButton(_('Find &Previous')) lowCtrlLayout.addWidget(previousButton) previousButton.clicked.connect(self.findPrevious) nextButton = QPushButton(_('Find &Next')) nextButton.setDefault(True) lowCtrlLayout.addWidget(nextButton) nextButton.clicked.connect(self.findNext) else: filterButton = QPushButton(_('&Filter')) lowCtrlLayout.addWidget(filterButton) filterButton.clicked.connect(self.startFilter) self.endFilterButton = QPushButton(_('&End Filter')) lowCtrlLayout.addWidget(self.endFilterButton) self.endFilterButton.setEnabled(False) self.endFilterButton.clicked.connect(self.endFilter) lowCtrlLayout.addStretch() closeButton = QPushButton(_('&Close')) lowCtrlLayout.addWidget(closeButton) closeButton.clicked.connect(self.close) origTypeName = nodeFormat.name if nodeFormat else '' self.loadTypeNames(origTypeName) self.loadSavedNames() self.ruleList.append(ConditionRule(1, self.fieldNames)) self.mainLayout.addWidget(self.ruleList[0]) def addNewRule(self, checked=False, combineBool='and'): """Add a new empty rule to the dialog. Arguments: checked -- unused placekeeper variable for signal combineBool -- the boolean op for combining with the previous rule """ if self.ruleList: boolBox = QComboBox() boolBox.setEditable(False) self.combiningBoxes.append(boolBox) boolBox.addItems([_(op) for op in _boolOper]) if combineBool != 'and': boolBox.setCurrentIndex(1) self.mainLayout.insertWidget( len(self.ruleList) * 2 - 1, boolBox, 0, Qt.AlignHCenter) rule = ConditionRule(len(self.ruleList) + 1, self.fieldNames) self.ruleList.append(rule) self.mainLayout.insertWidget(len(self.ruleList) * 2 - 2, rule) self.removeButton.setEnabled(True) def removeRule(self): """Remove the last rule from the dialog. """ if self.ruleList: if self.combiningBoxes: self.combiningBoxes[-1].hide() del self.combiningBoxes[-1] self.ruleList[-1].hide() del self.ruleList[-1] if self.dialogType == FindDialogType.typeDialog: self.removeButton.setEnabled(len(self.ruleList) > 0) else: self.removeButton.setEnabled(len(self.ruleList) > 1) def clearRules(self): """Remove all rules from the dialog and add default rule. """ for box in self.combiningBoxes: box.hide() for rule in self.ruleList: rule.hide() self.combiningBoxes = [] self.ruleList = [ConditionRule(1, self.fieldNames)] self.mainLayout.insertWidget(0, self.ruleList[0]) self.removeButton.setEnabled(True) def setCondition(self, conditional, typeName=''): """Set rule values to match the given conditional. Arguments: conditional -- the Conditional class to match typeName -- an optional type name used with some dialog types """ if self.typeCombo: if typeName: self.typeCombo.setCurrentIndex( self.typeCombo.findText(typeName)) else: self.typeCombo.setCurrentIndex(0) while len(self.ruleList) > 1: self.removeRule() if conditional: self.ruleList[0].setCondition(conditional.conditionLines[0]) for conditionLine in conditional.conditionLines[1:]: self.addNewRule(combineBool=conditionLine.boolOper) self.ruleList[-1].setCondition(conditionLine) def conditional(self): """Return a Conditional instance for the current settings. """ combineBools = [0] + [ boolBox.currentIndex() for boolBox in self.combiningBoxes ] typeName = self.typeCombo.currentText() if self.typeCombo else '' if typeName == _allTypeEntry: typeName = '' conditional = Conditional('', typeName) for boolIndex, rule in zip(combineBools, self.ruleList): condition = rule.conditionLine() if boolIndex != 0: condition.boolOper = 'or' conditional.conditionLines.append(condition) return conditional def loadTypeNames(self, origTypeName=''): """Load format type names into combo box. Arguments: origTypeName -- a starting type name if given """ if not origTypeName: origTypeName = self.typeCombo.currentText() nodeFormats = globalref.mainControl.activeControl.structure.treeFormats self.typeCombo.blockSignals(True) self.typeCombo.clear() self.typeCombo.addItem(_allTypeEntry) typeNames = nodeFormats.typeNames() self.typeCombo.addItems(typeNames) if origTypeName and origTypeName != _allTypeEntry: try: self.typeCombo.setCurrentIndex( typeNames.index(origTypeName) + 1) except ValueError: if self.endFilterButton and self.endFilterButton.isEnabled(): self.endFilter() self.clearRules() self.typeCombo.blockSignals(False) self.updateDataType() def updateDataType(self): """Update the node format based on a data type change. """ typeName = self.typeCombo.currentText() if not typeName: return nodeFormats = globalref.mainControl.activeControl.structure.treeFormats if typeName == _allTypeEntry: fieldNameSet = set() for typeFormat in nodeFormats.values(): fieldNameSet.update(typeFormat.fieldNames()) self.fieldNames = sorted(list(fieldNameSet)) else: self.fieldNames = nodeFormats[typeName].fieldNames() for rule in self.ruleList: currentField = rule.conditionLine().fieldName if currentField not in self.fieldNames: if self.endFilterButton and self.endFilterButton.isEnabled(): self.endFilter() self.clearRules() break rule.reloadFieldBox(self.fieldNames, currentField) def loadSavedNames(self, updateOtherDialog=False): """Refresh the list of saved rule names. """ selNum = 0 if self.saveListBox.count(): selNum = self.saveListBox.currentRow() self.saveListBox.clear() nodeFormats = globalref.mainControl.activeControl.structure.treeFormats savedRules = nodeFormats.savedConditions() ruleNames = sorted(list(savedRules.keys())) if ruleNames: self.saveListBox.addItems(ruleNames) if selNum >= len(ruleNames): selNum = len(ruleNames) - 1 self.saveListBox.setCurrentRow(selNum) self.loadSavedButton.setEnabled(len(ruleNames) > 0) self.delSavedButton.setEnabled(len(ruleNames) > 0) if updateOtherDialog: if (self != globalref.mainControl.findConditionDialog and globalref.mainControl.findConditionDialog and globalref.mainControl.findConditionDialog.isVisible()): globalref.mainControl.findConditionDialog.loadSavedNames() elif (self != globalref.mainControl.filterConditionDialog and globalref.mainControl.filterConditionDialog and globalref.mainControl.filterConditionDialog.isVisible()): globalref.mainControl.filterConditionDialog.loadSavedNames() def updateSaveEnable(self): """Set the save rule button enabled based on save name entry. """ self.saveButton.setEnabled(len(self.saveNameEdit.text())) def updateFilterControls(self): """Set filter button status based on active window changes. """ window = globalref.mainControl.activeControl.activeWindow if window.treeFilterView: filterView = window.treeFilterView conditional = filterView.conditionalFilter self.setCondition(conditional, conditional.origNodeFormatName) self.endFilterButton.setEnabled(True) else: self.endFilterButton.setEnabled(False) def loadSavedRule(self): """Load the current saved rule into the dialog. """ nodeFormats = globalref.mainControl.activeControl.structure.treeFormats savedRules = nodeFormats.savedConditions() ruleName = self.saveListBox.currentItem().text() conditional = savedRules[ruleName] self.setCondition(conditional, conditional.origNodeFormatName) def saveRule(self): """Save the current rule settings. """ name = self.saveNameEdit.text() self.saveNameEdit.setText('') treeStructure = globalref.mainControl.activeControl.structure undo.FormatUndo(treeStructure.undoList, treeStructure.treeFormats, treeformats.TreeFormats()) typeName = self.typeCombo.currentText() if typeName == _allTypeEntry: nodeFormat = treeStructure.treeFormats else: nodeFormat = treeStructure.treeFormats[typeName] nodeFormat.savedConditionText[name] = ( self.conditional().conditionStr()) self.loadSavedNames(True) self.saveListBox.setCurrentItem( self.saveListBox.findItems(name, Qt.MatchExactly)[0]) globalref.mainControl.activeControl.setModified() def deleteRule(self): """Remove the current saved rule. """ treeStructure = globalref.mainControl.activeControl.structure nodeFormats = treeStructure.treeFormats undo.FormatUndo(treeStructure.undoList, nodeFormats, treeformats.TreeFormats()) savedRules = nodeFormats.savedConditions() ruleName = self.saveListBox.currentItem().text() conditional = savedRules[ruleName] if conditional.origNodeFormatName: typeFormat = nodeFormats[conditional.origNodeFormatName] del typeFormat.savedConditionText[ruleName] else: del nodeFormats.savedConditionText[ruleName] self.loadSavedNames(True) globalref.mainControl.activeControl.setModified() def find(self, forward=True): """Find another match in the indicated direction. Arguments: forward -- next if True, previous if False """ self.resultLabel.setText('') conditional = self.conditional() control = globalref.mainControl.activeControl if not control.findNodesByCondition(conditional, forward): self.resultLabel.setText(_('No conditional matches were found')) def findPrevious(self): """Find the previous match. """ self.find(False) def findNext(self): """Find the next match. """ self.find(True) def startFilter(self): """Start filtering nodes. """ window = globalref.mainControl.activeControl.activeWindow filterView = window.filterView() filterView.conditionalFilter = self.conditional() filterView.updateContents() self.endFilterButton.setEnabled(True) def endFilter(self): """Stop filtering nodes. """ window = globalref.mainControl.activeControl.activeWindow window.removeFilterView() self.endFilterButton.setEnabled(False) def closeEvent(self, event): """Signal that the dialog is closing. Arguments: event -- the close event """ self.dialogShown.emit(False)
class IndustrialDualAnalogInV2(COMCUPluginBase): def __init__(self, *args): super().__init__(BrickletIndustrialDualAnalogInV2, *args) self.analog_in = self.device self.cbe_voltage0 = CallbackEmulator(self.analog_in.get_voltage, 0, self.cb_voltage, self.increase_error_count, pass_arguments_to_result_callback=True) self.cbe_voltage1 = CallbackEmulator(self.analog_in.get_voltage, 1, self.cb_voltage, self.increase_error_count, pass_arguments_to_result_callback=True) self.calibration = None self.sample_rate_label = QLabel('Sample Rate:') self.sample_rate_combo = QComboBox() self.sample_rate_combo.addItem('976 Hz') self.sample_rate_combo.addItem('488 Hz') self.sample_rate_combo.addItem('244 Hz') self.sample_rate_combo.addItem('122 Hz') self.sample_rate_combo.addItem('61 Hz') self.sample_rate_combo.addItem('4 Hz') self.sample_rate_combo.addItem('2 Hz') self.sample_rate_combo.addItem('1 Hz') self.current_voltage = [CurveValueWrapper(), CurveValueWrapper()] # float, V self.calibration_button = QPushButton('Calibration...') self.sample_rate_combo.currentIndexChanged.connect(self.sample_rate_combo_index_changed) self.calibration_button.clicked.connect(self.calibration_button_clicked) plots = [('Channel 0', Qt.red, self.current_voltage[0], format_voltage), ('Channel 1', Qt.blue, self.current_voltage[1], format_voltage)] self.plot_widget = PlotWidget('Voltage [V]', plots, y_resolution=0.001) # Define lines line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) line1 = QFrame() line1.setObjectName("line1") line1.setFrameShape(QFrame.HLine) line1.setFrameShadow(QFrame.Sunken) line2 = QFrame() line2.setObjectName("line2") line2.setFrameShape(QFrame.HLine) line2.setFrameShadow(QFrame.Sunken) # Define channel LED status config widgets self.led_config_ch0_label = QLabel('Channel 0') self.led_config_ch1_label = QLabel('Channel 1') self.led_config_label = QLabel('LED Config:') self.led_status_config_label = QLabel('LED Status Config:') self.led_status_config_ch0_min_label = QLabel('Min:') self.led_status_config_ch0_max_label = QLabel('Max:') self.led_status_config_ch1_min_label = QLabel('Min:') self.led_status_config_ch1_max_label = QLabel('Max:') self.led_config_ch0_combo = QComboBox() self.led_config_ch0_combo.addItem('Off') self.led_config_ch0_combo.addItem('On') self.led_config_ch0_combo.addItem('Show Heartbeat') self.led_config_ch0_combo.addItem('Show Channel Status') self.led_config_ch0_combo.currentIndexChanged.connect(self.led_config_ch0_combo_changed) self.led_config_ch1_combo = QComboBox() self.led_config_ch1_combo.addItem('Off') self.led_config_ch1_combo.addItem('On') self.led_config_ch1_combo.addItem('Show Heartbeat') self.led_config_ch1_combo.addItem('Show Channel Status') self.led_config_ch1_combo.currentIndexChanged.connect(self.led_config_ch1_combo_changed) self.led_status_config_ch0_combo = QComboBox() self.led_status_config_ch0_combo.addItem('Threshold') self.led_status_config_ch0_combo.addItem('Intensity') self.led_status_config_ch0_combo.currentIndexChanged.connect(self.led_status_config_ch0_combo_changed) self.led_status_config_ch1_combo = QComboBox() self.led_status_config_ch1_combo.addItem('Threshold') self.led_status_config_ch1_combo.addItem('Intensity') self.led_status_config_ch1_combo.currentIndexChanged.connect(self.led_status_config_ch1_combo_changed) self.led_status_config_ch0_min_sbox = QSpinBox() self.led_status_config_ch0_min_sbox.setMinimum(-35000) self.led_status_config_ch0_min_sbox.setMaximum(35000) self.led_status_config_ch0_min_sbox.setValue(0) self.led_status_config_ch0_min_sbox.setSingleStep(1) self.led_status_config_ch0_min_sbox.setSuffix(' mV') self.led_status_config_ch0_min_sbox.valueChanged.connect(self.led_status_config_ch0_min_sbox_changed) self.led_status_config_ch0_max_sbox = QSpinBox() self.led_status_config_ch0_max_sbox.setMinimum(-35000) self.led_status_config_ch0_max_sbox.setMaximum(35000) self.led_status_config_ch0_max_sbox.setValue(0) self.led_status_config_ch0_max_sbox.setSingleStep(1) self.led_status_config_ch0_max_sbox.setSuffix(' mV') self.led_status_config_ch0_max_sbox.valueChanged.connect(self.led_status_config_ch0_max_sbox_changed) self.led_status_config_ch1_min_sbox = QSpinBox() self.led_status_config_ch1_min_sbox.setMinimum(-35000) self.led_status_config_ch1_min_sbox.setMaximum(35000) self.led_status_config_ch1_min_sbox.setValue(0) self.led_status_config_ch1_min_sbox.setSingleStep(1) self.led_status_config_ch1_min_sbox.setSuffix(' mV') self.led_status_config_ch1_min_sbox.valueChanged.connect(self.led_status_config_ch1_min_sbox_changed) self.led_status_config_ch1_max_sbox = QSpinBox() self.led_status_config_ch1_max_sbox.setMinimum(-35000) self.led_status_config_ch1_max_sbox.setMaximum(35000) self.led_status_config_ch1_max_sbox.setValue(0) self.led_status_config_ch1_max_sbox.setSingleStep(1) self.led_status_config_ch1_max_sbox.setSuffix(' mV') self.led_status_config_ch1_max_sbox.valueChanged.connect(self.led_status_config_ch1_max_sbox_changed) # Define size policies h_sp = QSizePolicy() h_sp.setHorizontalPolicy(QSizePolicy.Expanding) # Set size policies self.sample_rate_combo.setSizePolicy(h_sp) self.led_config_ch0_combo.setSizePolicy(h_sp) self.led_config_ch1_combo.setSizePolicy(h_sp) self.led_status_config_ch0_combo.setSizePolicy(h_sp) self.led_status_config_ch1_combo.setSizePolicy(h_sp) self.led_status_config_ch0_min_sbox.setSizePolicy(h_sp) self.led_status_config_ch0_max_sbox.setSizePolicy(h_sp) self.led_status_config_ch1_min_sbox.setSizePolicy(h_sp) self.led_status_config_ch1_max_sbox.setSizePolicy(h_sp) # Define layouts hlayout = QHBoxLayout() vlayout = QVBoxLayout() glayout = QGridLayout() layout = QVBoxLayout(self) hlayout_ch0_min_max = QHBoxLayout() hlayout_ch1_min_max = QHBoxLayout() # Populate layouts vlayout.addWidget(self.calibration_button) hlayout.addWidget(self.sample_rate_label) hlayout.addWidget(self.sample_rate_combo) vlayout.addLayout(hlayout) vlayout.addWidget(line1) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_min_label) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_min_sbox) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_max_label) hlayout_ch0_min_max.addWidget(self.led_status_config_ch0_max_sbox) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_min_label) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_min_sbox) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_max_label) hlayout_ch1_min_max.addWidget(self.led_status_config_ch1_max_sbox) glayout.addWidget(self.led_config_ch0_label, 0, 1, 1, 1) # R, C, RS, CS glayout.addWidget(self.led_config_ch1_label, 0, 2, 1, 1) glayout.addWidget(line2, 1, 0, 1, 3) glayout.addWidget(self.led_config_label, 2, 0, 1, 1) glayout.addWidget(self.led_config_ch0_combo, 2, 1, 1, 1) glayout.addWidget(self.led_config_ch1_combo, 2, 2, 1, 1) glayout.addWidget(self.led_status_config_label, 3, 0, 1, 1) glayout.addWidget(self.led_status_config_ch0_combo, 3, 1, 1, 1) glayout.addWidget(self.led_status_config_ch1_combo, 3, 2, 1, 1) glayout.addLayout(hlayout_ch0_min_max, 4, 1, 1, 1) glayout.addLayout(hlayout_ch1_min_max, 4, 2, 1, 1) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(vlayout) layout.addLayout(glayout) self.ui_group_ch_status_ch0 = [self.led_status_config_ch0_combo, self.led_status_config_ch0_min_sbox, self.led_status_config_ch0_max_sbox] self.ui_group_ch_status_ch1 = [self.led_status_config_ch1_combo, self.led_status_config_ch1_min_sbox, self.led_status_config_ch1_max_sbox] def start(self): async_call(self.analog_in.get_sample_rate, None, self.get_sample_rate_async, self.increase_error_count) for channel in range(2): async_call(self.analog_in.get_channel_led_config, channel, self.get_channel_led_config_async, self.increase_error_count, pass_arguments_to_result_callback=True) async_call(self.analog_in.get_channel_led_status_config, channel, self.get_channel_led_status_config_async, self.increase_error_count, pass_arguments_to_result_callback=True) self.cbe_voltage0.set_period(100) self.cbe_voltage1.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_voltage0.set_period(0) self.cbe_voltage1.set_period(0) self.plot_widget.stop = True def destroy(self): if self.calibration != None: self.calibration.close() @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletIndustrialDualAnalogInV2.DEVICE_IDENTIFIER def calibration_button_clicked(self): if self.calibration == None: self.calibration = Calibration(self) self.calibration_button.setEnabled(False) self.calibration.show() def sample_rate_combo_index_changed(self, index): async_call(self.analog_in.set_sample_rate, index, None, self.increase_error_count) def led_config_ch0_combo_changed(self, index): if index != self.analog_in.CHANNEL_LED_CONFIG_SHOW_CHANNEL_STATUS: for e in self.ui_group_ch_status_ch0: e.setEnabled(False) else: for e in self.ui_group_ch_status_ch0: e.setEnabled(True) self.analog_in.set_channel_led_config(0, index) def led_config_ch1_combo_changed(self, index): if index != self.analog_in.CHANNEL_LED_CONFIG_SHOW_CHANNEL_STATUS: for e in self.ui_group_ch_status_ch1: e.setEnabled(False) else: for e in self.ui_group_ch_status_ch1: e.setEnabled(True) self.analog_in.set_channel_led_config(1, index) def led_status_config_ch0_combo_changed(self, index): self.analog_in.set_channel_led_status_config(0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), index) def led_status_config_ch1_combo_changed(self, index): self.analog_in.set_channel_led_status_config(1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), index) def led_status_config_ch0_min_sbox_changed(self, value): self.analog_in.set_channel_led_status_config(0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), self.led_status_config_ch0_combo.currentIndex()) def led_status_config_ch0_max_sbox_changed(self, value): self.analog_in.set_channel_led_status_config(0, self.led_status_config_ch0_min_sbox.value(), self.led_status_config_ch0_max_sbox.value(), self.led_status_config_ch0_combo.currentIndex()) def led_status_config_ch1_min_sbox_changed(self, value): self.analog_in.set_channel_led_status_config(1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), self.led_status_config_ch1_combo.currentIndex()) def led_status_config_ch1_max_sbox_changed(self, value): self.analog_in.set_channel_led_status_config(1, self.led_status_config_ch1_min_sbox.value(), self.led_status_config_ch1_max_sbox.value(), self.led_status_config_ch1_combo.currentIndex()) def get_voltage_value0(self): return self.voltage_value[0] def get_voltage_value1(self): return self.voltage_value[1] def get_sample_rate_async(self, rate): self.sample_rate_combo.blockSignals(True) self.sample_rate_combo.setCurrentIndex(rate) self.sample_rate_combo.blockSignals(False) def get_channel_led_config_async(self, channel, config): self.led_config_ch0_combo.blockSignals(True) self.led_config_ch1_combo.blockSignals(True) if channel == 0: self.led_config_ch0_combo.setCurrentIndex(config) elif channel == 1: self.led_config_ch1_combo.setCurrentIndex(config) self.led_config_ch0_combo.blockSignals(False) self.led_config_ch1_combo.blockSignals(False) def get_channel_led_status_config_async(self, channel, config): self.led_status_config_ch0_combo.blockSignals(True) self.led_status_config_ch1_combo.blockSignals(True) if channel == 0: self.led_status_config_ch0_combo.setCurrentIndex(config.config) self.led_status_config_ch0_max_sbox.setValue(config.max) self.led_status_config_ch0_min_sbox.setValue(config.min) elif channel == 1: self.led_status_config_ch1_combo.setCurrentIndex(config.config) self.led_status_config_ch1_max_sbox.setValue(config.max) self.led_status_config_ch1_min_sbox.setValue(config.min) self.led_status_config_ch0_combo.blockSignals(False) self.led_status_config_ch1_combo.blockSignals(False) def cb_voltage(self, sensor, voltage): self.current_voltage[sensor].value = voltage / 1000.0
class MainWindow(QMainWindow, Ui_MainWindow): """ classdocs """ def __init__(self, app): """ Init :param sakia.core.app.Application app: application :type: sakia.core.app.Application """ # Set up the user interface from Designer. super().__init__() self.app = app self.initialized = False self.password_asker = None self.import_dialog = None super().setupUi(self) QApplication.setWindowIcon(QIcon(":/icons/sakia_logo")) self.app.version_requested.connect(self.latest_version_requested) self.status_label = QLabel("", self) self.status_label.setTextFormat(Qt.RichText) self.statusbar.addPermanentWidget(self.status_label, 1) self.label_time = QLabel("", self) self.statusbar.addPermanentWidget(self.label_time) self.combo_referential = QComboBox(self) self.combo_referential.setEnabled(False) self.combo_referential.currentIndexChanged.connect(self.referential_changed) self.statusbar.addPermanentWidget(self.combo_referential) self.homescreen = HomeScreenWidget(self.app, self.status_label) self.homescreen.frame_communities.community_tile_clicked.connect(self.change_community) self.homescreen.toolbutton_new_account.clicked.connect(self.open_add_account_dialog) self.homescreen.toolbutton_new_account.addAction(self.action_add_account) self.homescreen.toolbutton_new_account.addAction(self.action_import) self.homescreen.button_add_community.clicked.connect(self.action_open_add_community) self.homescreen.button_disconnect.clicked.connect(lambda :self.action_change_account("")) self.centralWidget().layout().addWidget(self.homescreen) self.homescreen.toolbutton_connect.setMenu(self.menu_change_account) self.community_view = CommunityWidget(self.app, self.status_label) self.community_view.button_home.clicked.connect(lambda: self.change_community(None)) self.community_view.button_certification.clicked.connect(self.open_certification_dialog) self.community_view.button_send_money.clicked.connect(self.open_transfer_money_dialog) self.centralWidget().layout().addWidget(self.community_view) def startup(self): self.update_time() # FIXME : Need python 3.5 self.app.get_last_version() if self.app.preferences['maximized']: self.showMaximized() else: self.show() if self.app.current_account: self.password_asker = PasswordAskerDialog(self.app.current_account) self.community_view.change_account(self.app.current_account, self.password_asker) self.refresh() @asyncify @asyncio.coroutine def open_add_account_dialog(self, checked=False): dialog = ProcessConfigureAccount(self.app, None) result = yield from dialog.async_exec() if result == QDialog.Accepted: self.action_change_account(self.app.current_account.name) @asyncify @asyncio.coroutine def open_configure_account_dialog(self, checked=False): dialog = ProcessConfigureAccount(self.app, self.app.current_account) result = yield from dialog.async_exec() if result == QDialog.Accepted: if self.app.current_account: self.action_change_account(self.app.current_account.name) else: self.refresh() @pyqtSlot(str) def display_error(self, error): QMessageBox.critical(self, ":(", error, QMessageBox.Ok) @pyqtSlot(str) def referential_changed(self, index): if self.app.current_account: self.app.current_account.set_display_referential(index) if self.community_view: self.community_view.referential_changed() self.homescreen.referential_changed() @pyqtSlot() def update_time(self): dateTime = QDateTime.currentDateTime() self.label_time.setText("{0}".format(QLocale.toString( QLocale(), QDateTime.currentDateTime(), QLocale.dateTimeFormat(QLocale(), QLocale.NarrowFormat) ))) timer = QTimer() timer.timeout.connect(self.update_time) timer.start(1000) @pyqtSlot() def delete_contact(self): contact = self.sender().data() self.app.current_account.contacts.remove(contact) self.refresh_contacts() @pyqtSlot() def edit_contact(self): index = self.sender().data() dialog = ConfigureContactDialog(self.app.current_account, self, None, index) result = dialog.exec_() if result == QDialog.Accepted: self.window().refresh_contacts() def action_change_account(self, account_name): self.app.change_current_account(self.app.get_account(account_name)) self.password_asker = PasswordAskerDialog(self.app.current_account) self.community_view.change_account(self.app.current_account, self.password_asker) self.refresh() @pyqtSlot() def action_open_add_community(self): dialog = ProcessConfigureCommunity(self.app, self.app.current_account, None, self.password_asker) if dialog.exec_() == QDialog.Accepted: self.app.save(self.app.current_account) dialog.community.start_coroutines() self.homescreen.refresh() def open_transfer_money_dialog(self): dialog = TransferMoneyDialog(self.app, self.app.current_account, self.password_asker, self.community_view.community, None) if dialog.exec_() == QDialog.Accepted: self.community_view.tab_history.table_history.model().sourceModel().refresh_transfers() def open_certification_dialog(self): dialog = CertificationDialog(self.app, self.app.current_account, self.password_asker) dialog.exec_() def open_add_contact_dialog(self): dialog = ConfigureContactDialog(self.app.current_account, self) result = dialog.exec_() if result == QDialog.Accepted: self.window().refresh_contacts() def open_preferences_dialog(self): dialog = PreferencesDialog(self.app) result = dialog.exec_() def open_about_popup(self): """ Open about popup window """ aboutDialog = QDialog(self) aboutUi = Ui_AboutPopup() aboutUi.setupUi(aboutDialog) latest = self.app.available_version version_info = "" version_url = "" if not latest[0]: version_info = self.tr("Latest release : {version}") \ .format(version=latest[1]) version_url = latest[2] new_version_text = """ <p><b>{version_info}</b></p> <p><a href="{version_url}">{link_text}</a></p> """.format(version_info=version_info, version_url=version_url, link_text=self.tr("Download link")) else: new_version_text = "" text = self.tr(""" <h1>sakia</h1> <p>Python/Qt uCoin client</p> <p>Version : {:}</p> {new_version_text} <p>License : GPLv3</p> <p><b>Authors</b></p> <p>inso</p> <p>vit</p> <p>Moul</p> <p>canercandan</p> """).format(__version__, new_version_text=new_version_text) aboutUi.label.setText(text) aboutDialog.show() @pyqtSlot() def latest_version_requested(self): latest = self.app.available_version logging.debug("Latest version requested") if not latest[0]: version_info = self.tr("Please get the latest release {version}") \ .format(version=latest[1]) version_url = latest[2] if self.app.preferences['notifications']: toast.display("sakia", """{version_info}""".format( version_info=version_info, version_url=version_url)) @pyqtSlot(Community) def change_community(self, community): if community: self.homescreen.hide() self.community_view.show() else: self.community_view.hide() self.homescreen.show() self.community_view.change_community(community) def refresh_accounts(self): self.menu_change_account.clear() for account_name in sorted(self.app.accounts.keys()): action = QAction(account_name, self) action.triggered.connect(lambda checked, account_name=account_name: self.action_change_account(account_name)) self.menu_change_account.addAction(action) def refresh_contacts(self): self.menu_contacts_list.clear() if self.app.current_account: for index, contact in enumerate(self.app.current_account.contacts): contact_menu = self.menu_contacts_list.addMenu(contact['name']) edit_action = contact_menu.addAction(self.tr("Edit")) edit_action.triggered.connect(self.edit_contact) edit_action.setData(index) delete_action = contact_menu.addAction(self.tr("Delete")) delete_action.setData(contact) delete_action.triggered.connect(self.delete_contact) def refresh(self): """ Refresh main window When the selected account changes, all the widgets in the window have to be refreshed """ logging.debug("Refresh started") self.refresh_accounts() self.community_view.hide() self.homescreen.show() self.homescreen.refresh() if self.app.current_account is None: self.setWindowTitle(self.tr("sakia {0}").format(__version__)) self.action_add_a_contact.setEnabled(False) self.actionCertification.setEnabled(False) self.actionTransfer_money.setEnabled(False) self.action_configure_parameters.setEnabled(False) self.action_set_as_default.setEnabled(False) self.menu_contacts_list.setEnabled(False) self.combo_referential.setEnabled(False) self.status_label.setText(self.tr("")) self.password_asker = None else: self.password_asker = PasswordAskerDialog(self.app.current_account) self.combo_referential.blockSignals(True) self.combo_referential.clear() for ref in money.Referentials: self.combo_referential.addItem(ref.translated_name()) self.combo_referential.setEnabled(True) self.combo_referential.blockSignals(False) logging.debug(self.app.preferences) self.combo_referential.setCurrentIndex(self.app.preferences['ref']) self.action_add_a_contact.setEnabled(True) self.actionCertification.setEnabled(True) self.actionTransfer_money.setEnabled(True) self.menu_contacts_list.setEnabled(True) self.action_configure_parameters.setEnabled(True) self.setWindowTitle(self.tr("sakia {0} - Account : {1}").format(__version__, self.app.current_account.name)) self.refresh_contacts() def import_account(self): self.import_dialog = ImportAccountDialog(self.app, self) self.import_dialog.accepted.connect(self.import_account_accepted) self.import_dialog.exec_() def import_account_accepted(self): # open account after import self.action_change_account(self.import_dialog.edit_name.text()) def export_account(self): # Testable way of using a QFileDialog export_dialog = QFileDialog(self) export_dialog.setObjectName('ExportFileDialog') export_dialog.setWindowTitle(self.tr("Export an account")) export_dialog.setNameFilter(self.tr("All account files (*.acc)")) export_dialog.setLabelText(QFileDialog.Accept, self.tr('Export')) export_dialog.setOption(QFileDialog.DontUseNativeDialog, True) export_dialog.accepted.connect(self.export_account_accepted) export_dialog.show() def export_account_accepted(self): export_dialog = self.sender() selected_file = export_dialog.selectedFiles() if selected_file: if selected_file[0][-4:] == ".acc": path = selected_file[0] else: path = selected_file[0] + ".acc" self.app.export_account(path, self.app.current_account) def closeEvent(self, event): self.app.stop() super().closeEvent(event) def changeEvent(self, event): """ Intercepte LanguageChange event to translate UI :param QEvent QEvent: Event :return: """ if event.type() == QEvent.LanguageChange: self.retranslateUi(self) self.refresh() return super(MainWindow, self).changeEvent(event)
class Thermocouple(PluginBase): qtcb_error_state = pyqtSignal(bool, bool) def __init__(self, *args): super().__init__(BrickletThermocouple, *args) self.thermo = self.device self.qtcb_error_state.connect(self.cb_error_state) self.thermo.register_callback(self.thermo.CALLBACK_ERROR_STATE, self.qtcb_error_state.emit) self.cbe_temperature = CallbackEmulator(self.thermo.get_temperature, None, self.cb_temperature, self.increase_error_count) self.current_temperature = CurveValueWrapper() # float, °C self.error_label = QLabel('Current Errors: None') self.error_label.setAlignment(Qt.AlignVCenter | Qt.AlignRight) plots = [('Temperature', Qt.red, self.current_temperature, '{:.2f} °C'.format)] self.plot_widget = PlotWidget('Temperature [°C]', plots, extra_key_widgets=[self.error_label], y_resolution=0.01) self.averaging_label = QLabel('Averaging:') self.averaging_combo = QComboBox() self.averaging_combo.addItem('1', BrickletThermocouple.AVERAGING_1) self.averaging_combo.addItem('2', BrickletThermocouple.AVERAGING_2) self.averaging_combo.addItem('4', BrickletThermocouple.AVERAGING_4) self.averaging_combo.addItem('8', BrickletThermocouple.AVERAGING_8) self.averaging_combo.addItem('16', BrickletThermocouple.AVERAGING_16) self.type_label = QLabel('Thermocouple Type:') self.type_combo = QComboBox() self.type_combo.addItem('B', BrickletThermocouple.TYPE_B) self.type_combo.addItem('E', BrickletThermocouple.TYPE_E) self.type_combo.addItem('J', BrickletThermocouple.TYPE_J) self.type_combo.addItem('K', BrickletThermocouple.TYPE_K) self.type_combo.addItem('N', BrickletThermocouple.TYPE_N) self.type_combo.addItem('R', BrickletThermocouple.TYPE_R) self.type_combo.addItem('S', BrickletThermocouple.TYPE_S) self.type_combo.addItem('T', BrickletThermocouple.TYPE_T) self.type_combo.addItem('Gain 8', BrickletThermocouple.TYPE_G8) self.type_combo.addItem('Gain 32', BrickletThermocouple.TYPE_G32) self.filter_label = QLabel('Noise Rejection Filter:') self.filter_combo = QComboBox() self.filter_combo.addItem('50 Hz', BrickletThermocouple.FILTER_OPTION_50HZ) self.filter_combo.addItem('60 Hz', BrickletThermocouple.FILTER_OPTION_60HZ) hlayout = QHBoxLayout() hlayout.addWidget(self.averaging_label) hlayout.addWidget(self.averaging_combo) hlayout.addStretch() hlayout.addWidget(self.type_label) hlayout.addWidget(self.type_combo) hlayout.addStretch() hlayout.addWidget(self.filter_label) hlayout.addWidget(self.filter_combo) line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) self.averaging_combo.currentIndexChanged.connect(self.configuration_changed) self.type_combo.currentIndexChanged.connect(self.configuration_changed) self.filter_combo.currentIndexChanged.connect(self.configuration_changed) def start(self): async_call(self.thermo.get_configuration, None, self.get_configuration_async, self.increase_error_count) async_call(self.thermo.get_error_state, None, self.cb_error_state, self.increase_error_count, expand_result_tuple_for_callback=True) self.cbe_temperature.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_temperature.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletThermocouple.DEVICE_IDENTIFIER def configuration_changed(self, _): conf_averaging = self.averaging_combo.itemData(self.averaging_combo.currentIndex()) conf_type = self.type_combo.itemData(self.type_combo.currentIndex()) conf_filter = self.filter_combo.itemData(self.filter_combo.currentIndex()) self.thermo.set_configuration(conf_averaging, conf_type, conf_filter) def cb_temperature(self, temperature): self.current_temperature.value = temperature / 100.0 def get_configuration_async(self, conf): self.averaging_combo.blockSignals(True) self.averaging_combo.setCurrentIndex(self.averaging_combo.findData(conf.averaging)) self.averaging_combo.blockSignals(False) self.type_combo.blockSignals(True) self.type_combo.setCurrentIndex(self.type_combo.findData(conf.thermocouple_type)) self.type_combo.blockSignals(False) self.filter_combo.blockSignals(True) self.filter_combo.setCurrentIndex(self.filter_combo.findData(conf.filter)) self.filter_combo.blockSignals(False) def cb_error_state(self, over_under, open_circuit): if over_under or open_circuit: text = 'Current Errors: ' if over_under: text += 'Over/Under Voltage' if over_under and open_circuit: text += ' and ' if open_circuit: text += 'Open Circuit\n(defective thermocouple or nothing connected)' self.error_label.setStyleSheet('QLabel { color : red }') self.error_label.setText(text) else: self.error_label.setStyleSheet('') self.error_label.setText('Current Errors: None')
class MetricsToolBar(QToolBar): """ Emits *pointSizeChanged*. """ glyphsChanged = pyqtSignal(list) settingsChanged = pyqtSignal(dict) def __init__(self, font, parent=None): super().__init__(parent) auxiliaryWidth = self.fontMetrics().width('0') * 8 self.leftTextField = MetricsSequenceEdit(font, self) self.leftTextField.setMaximumWidth(auxiliaryWidth) self.textField = MetricsSequenceComboBox(font, self) # XXX: had to use Maximum because Preferred did extend the widget(?) self.textField.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) self.rightTextField = MetricsSequenceEdit(font, self) self.rightTextField.setMaximumWidth(auxiliaryWidth) self.leftTextField.textEdited.connect(self.textField.editTextChanged) self.rightTextField.textEdited.connect(self.textField.editTextChanged) self.textField.editTextChanged.connect(self._textChanged) self.comboBox = QComboBox(self) self.comboBox.setEditable(True) self.comboBox.setCompleter(None) self.comboBox.setValidator(QIntValidator(self)) for p in pointSizes: self.comboBox.addItem(str(p)) self.pointSizeChanged = self.comboBox.currentIndexChanged[str] self.configBar = QPushButton(self) self.configBar.setFlat(True) self.configBar.setIcon(QIcon(":settings.svg")) self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") self.toolsMenu = QMenu(self) self._showKerning = self.toolsMenu.addAction( self.tr("Show Kerning"), self._kerningVisibilityChanged) self._showKerning.setCheckable(True) self._showMetrics = self.toolsMenu.addAction(self.tr("Show Metrics"), self._controlsTriggered) self._showMetrics.setCheckable(True) self.toolsMenu.addSeparator() self._verticalFlip = self.toolsMenu.addAction(self.tr("Vertical Flip"), self._controlsTriggered) self._verticalFlip.setCheckable(True) self._wrapLines = self.toolsMenu.addAction(self.tr("Wrap Lines"), self._controlsTriggered) self._wrapLines.setCheckable(True) self.toolsMenu.addSeparator() action = self.toolsMenu.addAction(self.tr("Line Height:")) action.setEnabled(False) lineHeight = QWidgetAction(self.toolsMenu) self._lineHeightSlider = slider = QSlider(Qt.Horizontal, self) # QSlider works with integers so we'll just divide what comes out of it # by 100 slider.setMinimum(80) slider.setMaximum(160) slider.setValue(110) slider.valueChanged.connect(self._controlsTriggered) slider.valueChanged.connect(self._sliderLineHeightChanged) lineHeight.setDefaultWidget(slider) self.toolsMenu.addAction(lineHeight) self.configBar.setMenu(self.toolsMenu) self.addWidget(self.leftTextField) self.addWidget(self.textField) self.addWidget(self.rightTextField) self.addWidget(self.comboBox) self.addWidget(self.configBar) app = QApplication.instance() app.dispatcher.addObserver(self, "_currentGlyphChanged", "currentGlyphChanged") self.readSettings() def readSettings(self): items = settings.metricsWindowComboBoxItems() self.textField.clear() self.textField.addItems(items) # ------------- # Notifications # ------------- # app def _currentGlyphChanged(self, notification): self._textChanged() # widget def _controlsTriggered(self): params = dict( lineHeight=self._lineHeightSlider.value() / 100, showKerning=self._showKerning.isChecked(), showMetrics=self._showMetrics.isChecked(), verticalFlip=self._verticalFlip.isChecked(), wrapLines=self._wrapLines.isChecked(), ) self.settingsChanged.emit(params) def _kerningVisibilityChanged(self): self._controlsTriggered() # if showKerning was triggered, it won't apply until we pipe the glyphs # again. do so self._textChanged() def _sliderLineHeightChanged(self, value): QToolTip.showText(QCursor.pos(), str(value / 100), self) def _textChanged(self): leftGlyphs = self.leftTextField.glyphs() rightGlyphs = self.rightTextField.glyphs() glyphs = self.textField.glyphs() ret = [] for glyph in glyphs: ret.extend(leftGlyphs + [glyph] + rightGlyphs) self.glyphsChanged.emit(ret) # -------------- # Public methods # -------------- def setPointSize(self, pointSize): self.comboBox.blockSignals(True) self.comboBox.setEditText(str(pointSize)) self.comboBox.blockSignals(False) def setText(self, text, left=None, right=None): self.leftTextField.setText(left) self.rightTextField.setText(right) self.textField.setEditText(text) def setWrapLines(self, value): self._wrapLines.setChecked(value) self._controlsTriggered() # TODO: more methods # ---------- # Qt methods # ---------- def closeEvent(self, event): super().closeEvent(event) if event.isAccepted(): app = QApplication.instance() app.dispatcher.removeObserver(self, "currentGlyphChanged") def showEvent(self, event): super().showEvent(event) self.textField.setFocus(Qt.OtherFocusReason)
class ApplicationPage(QWidget): """ The GUI for the application page of a project. """ # The page's label. label = "Application Source" # Emitted when the user changes the PyQt version. pyqt_version_changed = pyqtSignal(bool) # Emitted when the user changes the Python target version. python_target_version_changed = pyqtSignal() @property def project(self): """ The project property getter. """ return self._project @project.setter def project(self, value): """ The project property setter. """ if self._project != value: self._project = value self._script_edit.set_project(value) self._package_edit.set_project(value) self._update_page() def __init__(self): """ Initialise the page. """ super().__init__() self._project = None # Create the page's GUI. layout = QGridLayout() form = BetterForm() self._name_edit = QLineEdit( placeholderText="Application name", whatsThis="The name of the application. It will default to " "the base name of the application script without any " "extension.", textEdited=self._name_changed) form.addRow("Name", self._name_edit) self._script_edit = FilenameEditor("Application Script", placeholderText="Application script", whatsThis="The name of the application's optional main script " "file.", textEdited=self._script_changed) form.addRow("Main script file", self._script_edit) self._entry_point_edit = QLineEdit( placeholderText="Entry point in application package", whatsThis="The name of the optional entry point in the " "application's package.", textEdited=self._entry_point_changed) form.addRow("Entry point", self._entry_point_edit) self._sys_path_edit = QLineEdit( placeholderText="Additional sys.path directories", whatsThis="A space separated list of additional directories, " "ZIP files and eggs to add to <tt>sys.path</tt>. Only " "set this if you want to allow external packages to " "be imported.", textEdited=self._sys_path_changed) form.addRow("sys.path", self._sys_path_edit) layout.addLayout(form, 0, 0) options_layout = BetterForm() self._py_version_edit = QComboBox( whatsThis="Select the target Python version.") self._py_version_edit.addItems(get_supported_python_versions()) self._py_version_edit.currentIndexChanged.connect( self._py_version_changed) options_layout.addRow("Target Python version", self._py_version_edit) self._pyqt_version_edit = QComboBox( whatsThis="Select the PyQt version.") self._pyqt_version_edit.addItems(["PyQt4", "PyQt5"]) self._pyqt_version_edit.currentIndexChanged.connect( self._pyqt_version_changed) options_layout.addRow("Target PyQt version", self._pyqt_version_edit) self._console_edit = QCheckBox("Use console (Windows)", whatsThis="Enable console output for Windows applications. " "Console output will be enabled automatically if no " "graphical PyQt modules are used.", stateChanged=self._console_changed) options_layout.addRow(self._console_edit) self._bundle_edit = QCheckBox("Application bundle (OS X)", whatsThis="Build an application bundle on OS X. If it is not " "checked then the application will be built as a " "simple executable.", stateChanged=self._bundle_changed) options_layout.addRow(self._bundle_edit) layout.addLayout(options_layout, 0, 1) self._package_edit = _ApplicationPackageEditor() self._package_edit.package_changed.connect(self._package_changed) package_edit_gb = QGroupBox(self._package_edit.title) package_edit_gb.setLayout(self._package_edit) layout.addWidget(package_edit_gb, 1, 0, 1, 2) layout.setRowStretch(1, 1) self.setLayout(layout) def _update_page(self): """ Update the page using the current project. """ project = self.project self._name_edit.setText(project.application_name) self._script_edit.setText(project.application_script) self._entry_point_edit.setText(project.application_entry_point) self._sys_path_edit.setText(project.sys_path) self._package_edit.configure(project.application_package, project) blocked = self._py_version_edit.blockSignals(True) self._py_version_edit.setCurrentIndex( get_supported_python_version_index( project.python_target_version)) self._py_version_edit.blockSignals(blocked) blocked = self._pyqt_version_edit.blockSignals(True) self._pyqt_version_edit.setCurrentIndex( 1 if project.application_is_pyqt5 else 0) self._pyqt_version_edit.blockSignals(blocked) blocked = self._console_edit.blockSignals(True) self._console_edit.setCheckState( Qt.Checked if project.application_is_console else Qt.Unchecked) self._console_edit.blockSignals(blocked) blocked = self._bundle_edit.blockSignals(True) self._bundle_edit.setCheckState( Qt.Checked if project.application_is_bundle else Qt.Unchecked) self._bundle_edit.blockSignals(blocked) def _py_version_changed(self, idx): """ Invoked when the user changes the Python version number. """ self.project.python_target_version = get_supported_python_version(idx) self.project.modified = True self.python_target_version_changed.emit() def _pyqt_version_changed(self, idx): """ Invoked when the user changes the PyQt version number. """ pyqt5 = (idx == 1) self.project.application_is_pyqt5 = pyqt5 self.project.modified = True self.pyqt_version_changed.emit(pyqt5) def _console_changed(self, state): """ Invoked when the user changes the console state. """ self.project.application_is_console = (state == Qt.Checked) self.project.modified = True def _bundle_changed(self, state): """ Invoked when the user changes the bundle state. """ self.project.application_is_bundle = (state == Qt.Checked) self.project.modified = True def _name_changed(self, value): """ Invoked when the user edits the application name. """ self.project.application_name = value self.project.modified = True def _script_changed(self, value): """ Invoked when the user edits the application script name. """ self.project.application_script = value self.project.modified = True def _entry_point_changed(self, value): """ Invoked when the user edits the entry point. """ self.project.application_entry_point = value self.project.modified = True def _sys_path_changed(self, value): """ Invoked when the user edits the sys.path directories. """ self.project.sys_path = value.strip() self.project.modified = True def _package_changed(self): """ Invoked when the user edits the application package. """ self.project.modified = True
class FileWranglerApp(QMainWindow): def __init__(self): super().__init__() self.targetDir = FileChooserTextBox("Destination: ", "Select destination directory", True) self.move_button = QPushButton(ActionKeys.move.value) self.move_button.pressed.connect(partial(self.execute_merge, ActionKeys.move.value)) self.copy_button = QPushButton(ActionKeys.copy.value) self.copy_button.pressed.connect(partial(self.execute_merge, ActionKeys.copy.value)) self.progress_bar = QProgressBar() self.date_checkbox = QCheckBox("Append Date (YYYY.MM.DD) to destination file") self.delete_source_checkbox = QCheckBox("After move, delete Source directories if empty") self.dir_include = QSpinBox() self.dir_include.setMinimum(0) self.dir_include.setValue(0) self.dir_include.setMaximum(10) self.sort_name = QRadioButton("Name") self.sort_name.setChecked(True) self.sort_date = QRadioButton("Date") self.sort_size = QRadioButton("Size") self.sort_none = QRadioButton("None") self.key_token_string = QComboBox() self.key_token_string.setEditable(True) self.key_token_string.setInsertPolicy(QComboBox.InsertAtTop) self.key_token_string.setCurrentText(_DEFAULT_REGEX) self.key_separator = QRadioButton("Separator") self.key_regex = QRadioButton("Regular Expression") self.key_regex.setChecked(True) self.key_replace = QRadioButton("Completely Replace") self.key_match_counter = QSpinBox() self.key_match_counter.setMinimum(1) self.key_match_counter.setValue(1) self.key_match_counter.setMaximum(10) self.dropZone = DropZone() self.key_match_counter.valueChanged.connect(self.create_merge) self.dir_include.valueChanged.connect(self.create_merge) self.dropZone.files_dropped_event.connect(self.create_merge) self.key_token_string.editTextChanged.connect(self.create_merge) self.key_regex.released.connect(partial(self.create_merge)) self.key_replace.released.connect(partial(self.create_merge)) self.key_separator.released.connect(partial(self.create_merge)) self.date_checkbox.stateChanged.connect(self.create_merge) self.targetDir.file_selection_changed.connect(self.create_merge) self.sort_date.released.connect(partial(self.create_merge)) self.sort_name.released.connect(partial(self.create_merge)) self.sort_size.released.connect(partial(self.create_merge)) self.sort_none.released.connect(partial(self.create_merge)) self.table = MainTable() self.init_ui() def init_ui(self): def create_dropzone_groupbox(): dropzone_sort_layout = QHBoxLayout() dropzone_sort_layout.addWidget(QLabel("Order By")) dropzone_sort_layout.addWidget(self.sort_name) dropzone_sort_layout.addWidget(self.sort_date) dropzone_sort_layout.addWidget(self.sort_size) dropzone_sort_layout.addWidget(self.sort_none) dropzone_layout = QVBoxLayout() dropzone_layout.addWidget(self.dropZone) dropzone_layout.addLayout(dropzone_sort_layout) dropzone_layout.setContentsMargins(0, 0, 0, 0) groupbox = QGroupBox() groupbox.setLayout(dropzone_layout) return groupbox def create_rename_controlbox(): self.move_button.setIcon(QIcon.fromTheme("edit-paste")) self.copy_button.setIcon(QIcon.fromTheme("edit-copy")) button_layout = QHBoxLayout() button_layout.addWidget(QLabel(""), stretch=1) button_layout.addWidget(self.move_button) button_layout.addWidget(self.copy_button) key_layout = QHBoxLayout() key_layout.addWidget(QLabel("Match Key: ")) key_layout.addWidget(self.key_match_counter) key_layout.addWidget(QLabel("Key Identifier")) key_layout.addWidget(self.key_token_string, stretch=1) key_layout.addWidget(self.key_regex) key_layout.addWidget(self.key_separator) key_layout.addWidget(self.key_replace) sub_control_layout = QHBoxLayout() sub_control_layout.addWidget(QLabel("Dir. names: ")) sub_control_layout.addWidget(self.dir_include) sub_control_layout.addWidget(self.date_checkbox) sub_control_layout.addWidget(self.delete_source_checkbox) sub_control_layout.addWidget(QLabel(""), stretch=1) control_layout = QVBoxLayout() control_layout.addWidget(self.targetDir) control_layout.addLayout(key_layout) control_layout.addLayout(sub_control_layout) # control_layout.addWidget(QLabel("")) control_layout.addLayout(button_layout) groupbox = QGroupBox() groupbox.setLayout(control_layout) return groupbox top_layout = QHBoxLayout() top_layout.addWidget(create_dropzone_groupbox()) top_layout.addWidget(QVLine()) top_layout.addWidget(create_rename_controlbox()) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.table) main_layout.setContentsMargins(2, 2, 2, 2) dummy_widget = QWidget() dummy_widget.setLayout(main_layout) self.setCentralWidget(dummy_widget) self.setWindowTitle('File Wrangler') self.setMinimumWidth(1724) self.setMinimumHeight(768) self.show() def create_merge(self): token_string = self.key_token_string.currentText() if token_string is None or token_string == "": return sort_key = SortBy.none if self.sort_size.isChecked(): sort_key = SortBy.size elif self.sort_name.isChecked(): sort_key = SortBy.name elif self.sort_date.isChecked(): sort_key = SortBy.date config = { ConfigKeys.append_date: self.date_checkbox.isChecked(), ConfigKeys.key_token_string: self.key_token_string.currentText(), ConfigKeys.key_token_count: self.key_match_counter.value(), ConfigKeys.sort_by: sort_key, ConfigKeys.dir_include: self.dir_include.value() } if self.key_regex.isChecked(): try: re.compile(self.key_token_string.currentText()) config[ConfigKeys.key_type] = KeyType.regular_expression except re.error: logger.error("Regular expression error") return elif self.key_replace.isChecked(): config[ConfigKeys.key_type] = KeyType.replacement else: config[ConfigKeys.key_type] = KeyType.separator try: model = create_merge_tree(self.dropZone.dropped_files, self.targetDir.getSelection(), config) if model: self.table.set_model(model) except FileNotFoundError as fne: logger.error(f"File not found {fne.filename}") def execute_merge(self, action): file_items = self.table.model transfer_dialog = QProgressDialog() transfer_dialog.setWindowModality(Qt.WindowModal) transfer_dialog.setWindowTitle(action + " Files") transfer_dialog.setCancelButtonText("Abort") transfer_dialog.setValue(0) transfer_dialog.setMaximum(len(file_items)) transfer_dialog.setMinimumWidth(550) transfer_dialog.setMaximumWidth(self.minimumWidth()) transfer_dialog.show() source_dirs = set() for item in file_items: source = item[DisplayKeys.source] source_dirs.add(os.path.dirname(source)) target = item[DisplayKeys.target] transfer_dialog.setValue(transfer_dialog.value() + 1) if self.table.is_selected(source): logger.debug(f"{action}: {source} -> {target}") transfer_dialog.setLabelText(f"{action}: \n{source} \n->\n {target}") # Allow repainting etc. QCoreApplication.processEvents() if action == ActionKeys.copy.value: shutil.copy(source, target) self.table.remove_file(source) elif action == ActionKeys.move.value: shutil.move(source, target) self.table.remove_file(source) else: logger.error("Unknown Action! " + action) if transfer_dialog.wasCanceled(): logger.info("User aborted operation") break self.key_token_string.blockSignals(True) self.key_token_string.addItem(self.key_token_string.currentText()) self.key_token_string.blockSignals(False) # Delete Source dirs if self.delete_source_checkbox.checkState() == Qt.Checked: for _dir in source_dirs: if not os.listdir(_dir): logger.info(f"Directory {_dir} is empty. Attempting to delete it") os.rmdir(_dir) logger.info(f"Directory {_dir} is empty. Attempting to delete it - Done") else: logger.info(f"Directory {_dir} is not empty, so will not delete it")
class SchemeSelector(QWidget): currentChanged = pyqtSignal() changed = pyqtSignal() def __init__(self, parent=None): super(SchemeSelector, self).__init__(parent) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.label = QLabel() self.scheme = QComboBox() self.menuButton = QPushButton(flat=True) menu = QMenu(self.menuButton) self.menuButton.setMenu(menu) layout.addWidget(self.label) layout.addWidget(self.scheme) layout.addWidget(self.menuButton) layout.addStretch(1) # action generator def act(slot, icon=None): a = QAction(self, triggered=slot) self.addAction(a) icon and a.setIcon(icons.get(icon)) return a # add action a = self.addAction_ = act(self.slotAdd, 'list-add') menu.addAction(a) # remove action a = self.removeAction = act(self.slotRemove, 'list-remove') menu.addAction(a) # rename action a = self.renameAction = act(self.slotRename, 'document-edit') menu.addAction(a) menu.addSeparator() # import action a = self.importAction = act(self.slotImport, 'document-open') menu.addAction(a) # export action a = self.exportAction = act(self.slotExport, 'document-save-as') menu.addAction(a) self.scheme.currentIndexChanged.connect(self.slotSchemeChanged) app.translateUI(self) def translateUI(self): self.label.setText(_("Scheme:")) self.menuButton.setText(_("&Menu")) self.addAction_.setText(_("&Add...")) self.removeAction.setText(_("&Remove")) self.renameAction.setText(_("Re&name...")) self.importAction.setText(_("&Import...")) self.exportAction.setText(_("&Export...")) def slotSchemeChanged(self, index): """Called when the Scheme combobox is changed by the user.""" self.disableDefault(self.scheme.itemData(index) == 'default') self.currentChanged.emit() self.changed.emit() def disableDefault(self, val): self.removeAction.setDisabled(val) self.renameAction.setDisabled(val) def schemes(self): """Returns the list with internal names of currently available schemes.""" return [self.scheme.itemData(i) for i in range(self.scheme.count())] def currentScheme(self): """Returns the internal name of the currently selected scheme""" return self.scheme.itemData(self.scheme.currentIndex()) def insertSchemeItem(self, name, scheme): for i in range(1, self.scheme.count()): n = self.scheme.itemText(i) if n.lower() > name.lower(): self.scheme.insertItem(i, name, scheme) break else: self.scheme.addItem(name, scheme) def addScheme(self, name): num, key = 1, 'user1' while key in self.schemes() or key in self._schemesToRemove: num += 1 key = 'user{0}'.format(num) self.insertSchemeItem(name, key) self.scheme.setCurrentIndex(self.scheme.findData(key)) return key def slotAdd(self): name, ok = QInputDialog.getText(self, app.caption(_("Add Scheme")), _("Please enter a name for the new scheme:")) if ok: self.addScheme(name) def slotRemove(self): index = self.scheme.currentIndex() scheme = self.scheme.itemData(index) if scheme == 'default': return # default can not be removed self._schemesToRemove.add(scheme) self.scheme.removeItem(index) def slotRename(self): index = self.scheme.currentIndex() name = self.scheme.itemText(index) scheme = self.scheme.itemData(index) newName, ok = QInputDialog.getText(self, _("Rename"), _("New name:"), text=name) if ok: self.scheme.blockSignals(True) self.scheme.removeItem(index) self.insertSchemeItem(newName, scheme) self.scheme.setCurrentIndex(self.scheme.findData(scheme)) self.scheme.blockSignals(False) self.changed.emit() def slotImport(self): filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption(_("dialog title", "Import color theme")) filename = QFileDialog.getOpenFileName(self, caption, QDir.homePath(), filetypes)[0] if filename: self.parent().import_(filename) def slotExport(self): name = self.scheme.currentText() filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption(_("dialog title", "Export {name}").format(name=name)) path = os.path.join(QDir.homePath(), name+'.xml') filename = QFileDialog.getSaveFileName(self, caption, path, filetypes)[0] if filename: if os.path.splitext(filename)[1] != '.xml': filename += '.xml' self.parent().export(name, filename) def loadSettings(self, currentKey, namesGroup): # don't mark schemes for removal anymore self._schemesToRemove = set() s = QSettings() cur = s.value(currentKey, "default", str) # load the names for the shortcut schemes s.beginGroup(namesGroup) block = self.scheme.blockSignals(True) self.scheme.clear() self.scheme.addItem(_("Default"), "default") lst = [(s.value(key, key, str), key) for key in s.childKeys()] for name, key in sorted(lst, key=lambda f: f[0].lower()): self.scheme.addItem(name, key) # find out index index = self.scheme.findData(cur) self.disableDefault(cur == 'default') self.scheme.setCurrentIndex(index) self.scheme.blockSignals(block) self.currentChanged.emit() def saveSettings(self, currentKey, namesGroup, removePrefix=None): # first save new scheme names s = QSettings() s.beginGroup(namesGroup) for i in range(self.scheme.count()): if self.scheme.itemData(i) != 'default': s.setValue(self.scheme.itemData(i), self.scheme.itemText(i)) for scheme in self._schemesToRemove: s.remove(scheme) s.endGroup() if removePrefix: for scheme in self._schemesToRemove: s.remove("{0}/{1}".format(removePrefix, scheme)) # then save current scheme = self.currentScheme() s.setValue(currentKey, scheme) # clean up self._schemesToRemove = set()
class GameControllerWindow(QWidget): def __init__(self, model: GameControllerModel, controller: GameControllerController): super(QWidget, self).__init__() self.toolbar_w = 80 self.toolbar_h = 80 self.model = model self.controller = controller self.main_layout = QGridLayout() self.toolbarOptions = QVBoxLayout() self.dungeonSelector = QDungeonSelector(self, controller, model) # self.widRun = QToolboxRun(self) self.currentLevelWidget = QLevelViewer(self.model, 0) self.widActions = QToolboxActions(self) self.size_info_lbl = QLabel("Screen size:\n1x1") self.lblDataFolder = QLabel() self.lblConnectionStatus = QLabel() self.lblCheckConnectionStatus = QLabel() self.controlWidget = QDungeonController(self, controller, model) self.content_wid = QDeskArea(self, controller, model) # QtWidgets.QWidget() self.infoLabel = QLabel() self.cBoxhealStrategy = QComboBox() self.lblInfoHealStrategy = QLabel() # self.setupUi() self.initConnectors() self.onChangeHealStrategy(self.model.engine.healingStrategy == self.model.engine.healingStrategy.AlwaysHeal) # self.model.onSourceChanged.connect(self.source_changed) def initConnectors(self): self.model.engine.gameWon.connect(self.onGameWon) self.model.engine.noEnergyLeft.connect(self.onNoEnergyLeft) self.model.engineStatechanged.connect(self.onEngineStateChanged) self.model.connectionStateChanged.connect(self.onConnectionStateChange) self.model.checkConnectionStateChanged.connect( self.onCheckConnectionStateChanged) self.model.engine.resolutionChanged.connect(self.onScreenDataChanged) self.model.engine.dataFolderChanged.connect(self.onScreenDataChanged) self.model.engine.levelChanged.connect(self.onLevelChanged) self.model.engine.healingStrategyChanged.connect( self.onHealingStrategyChange) self.cBoxhealStrategy.currentIndexChanged.connect( self.onChangeHealStrategy) def onHealingStrategyChange(self, always_heal: bool): index = 1 if always_heal else 0 if self.cBoxhealStrategy.currentIndex != index: self.cBoxhealStrategy.blockSignals(True) self.cBoxhealStrategy.setCurrentIndex(index) self.cBoxhealStrategy.blockSignals(False) def onChangeHealStrategy(self, new_index): self.controller.requestChangeHealingStrategy(new_index == 1) def onLevelChanged(self, newLevel): self.currentLevelWidget.changeLevel(newLevel) def onGameWon(self): self.infoLabel.setText("Finished 20 chapters. Win!") def onNoEnergyLeft(self): self.infoLabel.setText( "No energy left. Waiting until refill to play again.") def onEngineStateChanged(self, state: EngineState): if state == EngineState.Playing: self.infoLabel.setText("Engine started playing") elif state == EngineState.StopInvoked: self.infoLabel.setText("Engine stopping. Wait a second....") elif state == EngineState.Ready: self.infoLabel.setText("Engine is ready") def get_toolbar_size(self): return self.toolbar_w, self.toolbar_h def onConnectionStateChange(self, connected: bool): if connected: self.infoLabel.setText("Device found! Engine is ready") self.lblConnectionStatus.setText("Connected") self.lblConnectionStatus.setStyleSheet("color: white") else: self.infoLabel.setText("Waiting for a device to be connected") self.lblConnectionStatus.setText("NO device!") self.lblConnectionStatus.setStyleSheet( "background-color: red;color:white") def onCheckConnectionStateChanged(self, checking: bool): self.lblCheckConnectionStatus.setText( 'connecting..' if checking else '') def onScreenDataChanged(self): self.size_info_lbl.setText("Device size:\n{}x{}".format( self.model.engine.width, self.model.engine.heigth)) self.lblDataFolder.setText("{}".format( self.model.engine.currentDataFolder)) def setupUi(self, main_window: QMainWindow): main_window.setObjectName("game_controller_window") self.setObjectName("main_window") self.resize(450, 620) self.setMinimumWidth(620) self.setMinimumHeight(450) self.main_layout.setSpacing(0) self.main_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.setColumnStretch(0, 0) self.main_layout.setRowStretch(0, 0) self.main_layout.setColumnStretch(1, 200) self.main_layout.setRowStretch(1, 200) self.setLayout(self.main_layout) self.main_layout.addWidget(self.dungeonSelector, 0, 0) lay_content = QVBoxLayout() self.toolbarOptions.addWidget(self.size_info_lbl) self.toolbarOptions.addWidget(self.lblConnectionStatus) self.toolbarOptions.addWidget(self.lblCheckConnectionStatus) self.cBoxhealStrategy.addItems(['Always power', 'Always heal']) self.lblInfoHealStrategy.setText('Healing Strategy:') self.toolbarOptions.addWidget(self.lblInfoHealStrategy) self.toolbarOptions.addWidget(self.cBoxhealStrategy) lay_content.addWidget(self.controlWidget) lay_content.addWidget(self.infoLabel) self.lblInfoHealStrategy.setStyleSheet( "background-color: #6e6e6e; color: white") self.cBoxhealStrategy.setStyleSheet( "background-color: #6e6e6e; color: white") self.controlWidget.setStyleSheet("background-color: #6e6e6e") self.size_info_lbl.setStyleSheet( "background-color: #6e6e6e; color: white") self.lblConnectionStatus.setStyleSheet( "background-color: #6e6e6e; color: white") self.lblCheckConnectionStatus.setStyleSheet("color: white") self.lblCheckConnectionStatus.setText('Starting...') self.lblDataFolder.setStyleSheet( "background-color: #6e6e6e; color: white") self.infoLabel.setStyleSheet("background-color: #6e6e6e; color: white") self.size_info_lbl.setAlignment(Qt.AlignCenter) self.infoLabel.setAlignment(Qt.AlignCenter) self.lblConnectionStatus.setAlignment(Qt.AlignCenter) self.lblCheckConnectionStatus.setAlignment(Qt.AlignCenter) lay_header = QHBoxLayout() lay_header.addLayout(lay_content, 20) lay_header.addWidget(self.currentLevelWidget, 1) self.main_layout.addLayout(lay_header, 0, 1) self.widActions.setFixedWidth(self.toolbar_w) self.main_layout.addLayout(self.toolbarOptions, 1, 0) self.toolbarOptions.setAlignment(Qt.AlignTop) self.toolbarOptions.setContentsMargins(5, 5, 5, 0) self.toolbarOptions.setSpacing(10) self.content_wid.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)) # self.content_wid.setStyleSheet("background-color: rgb(43, 43, 43)") self.main_layout.addWidget(self.content_wid, 1, 1) self.setStyleSheet("background-color: #6e6e6e") main_window.setStyleSheet("background-color: #6e6e6e") self.setLayout(self.main_layout) main_window.setCentralWidget(self) self.onScreenDataChanged() # To initialize self.onConnectionStateChange(self.model.connected()) # To initialize
class MainWindow(QMainWindow, Ui_MainWindow): ''' classdocs ''' def __init__(self, app): """ Init :param cutecoin.core.app.Application app: application :type: cutecoin.core.app.Application """ # Set up the user interface from Designer. super().__init__() self.setupUi(self) QApplication.setWindowIcon(QIcon(":/icons/cutecoin_logo")) self.app = app logging.debug(app.thread()) logging.debug(self.thread()) self.password_asker = None self.initialized = False self.busybar = QProgressBar(self.statusbar) self.busybar.setMinimum(0) self.busybar.setMaximum(0) self.busybar.setValue(-1) self.statusbar.addWidget(self.busybar) self.busybar.hide() self.app.version_requested.connect(self.latest_version_requested) self.app.get_last_version() self.combo_referential = QComboBox(self) self.combo_referential.setEnabled(False) self.combo_referential.currentIndexChanged.connect(self.referential_changed) self.status_label = QLabel("", self) self.status_label.setTextFormat(Qt.RichText) self.label_time = QLabel("", self) self.statusbar.addPermanentWidget(self.status_label, 1) self.statusbar.addPermanentWidget(self.label_time) self.statusbar.addPermanentWidget(self.combo_referential) self.update_time() self.loader = Loader(self.app) self.loader.loaded.connect(self.loader_finished) self.loader.connection_error.connect(self.display_error) self.homescreen = HomeScreenWidget(self.app) self.centralWidget().layout().addWidget(self.homescreen) self.homescreen.button_new.clicked.connect(self.open_add_account_dialog) self.homescreen.button_import.clicked.connect(self.import_account) self.open_ucoin_info = lambda: QDesktopServices.openUrl(QUrl("http://ucoin.io/theoretical/")) self.homescreen.button_info.clicked.connect(self.open_ucoin_info) self.import_dialog = None self.export_dialog = None # TODO: There are too much refresh() calls on startup self.refresh() def open_add_account_dialog(self): dialog = ProcessConfigureAccount(self.app, None) result = dialog.exec_() if result == QDialog.Accepted: self.action_change_account(self.app.current_account.name) @pyqtSlot(str) def display_error(self, error): QMessageBox.critical(self, ":(", error, QMessageBox.Ok) @pyqtSlot(str) def referential_changed(self, index): if self.app.current_account: self.app.current_account.set_display_referential(index) if self.currencies_tabwidget.currentWidget(): self.currencies_tabwidget.currentWidget().referential_changed() @pyqtSlot() def update_time(self): date = QDate.currentDate() self.label_time.setText("{0}".format(date.toString("dd/MM/yyyy"))) next_day = date.addDays(1) current_time = QDateTime().currentDateTime().toMSecsSinceEpoch() next_time = QDateTime(next_day).toMSecsSinceEpoch() timer = QTimer() timer.timeout.connect(self.update_time) timer.start(next_time - current_time) @pyqtSlot() def delete_contact(self): contact = self.sender().data() self.app.current_account.contacts.remove(contact) self.refresh_contacts() @pyqtSlot() def edit_contact(self): index = self.sender().data() dialog = ConfigureContactDialog(self.app.current_account, self, None, index) result = dialog.exec_() if result == QDialog.Accepted: self.window().refresh_contacts() def action_change_account(self, account_name): def loading_progressed(value, maximum): logging.debug("Busybar : {:} : {:}".format(value, maximum)) self.busybar.setValue(value) self.busybar.setMaximum(maximum) QApplication.processEvents() if self.app.current_account: self.app.save_cache(self.app.current_account) self.app.current_account = None self.refresh() QApplication.setOverrideCursor(Qt.BusyCursor) self.app.loading_progressed.connect(loading_progressed) self.busybar.setMinimum(0) self.busybar.setMaximum(0) self.busybar.setValue(-1) self.busybar.show() self.status_label.setText(self.tr("Loading account {0}").format(account_name)) self.loader.set_account_name(account_name) QTimer.singleShot(10, self.loader.load) self.homescreen.button_new.hide() self.homescreen.button_import.hide() @pyqtSlot() def loader_finished(self): logging.debug("Finished loading") self.refresh() self.busybar.hide() QApplication.setOverrideCursor(Qt.ArrowCursor) try: self.app.disconnect() except: logging.debug("Disconnect of app failed") self.app.monitor.start_network_watchers() QApplication.processEvents() def open_transfer_money_dialog(self): dialog = TransferMoneyDialog(self.app.current_account, self.password_asker) dialog.accepted.connect(self.refresh_wallets) if dialog.exec_() == QDialog.Accepted: currency_tab = self.currencies_tabwidget.currentWidget() currency_tab.tab_history.table_history.model().sourceModel().refresh_transfers() def open_certification_dialog(self): dialog = CertificationDialog(self.app.current_account, self.password_asker) dialog.exec_() def open_add_contact_dialog(self): dialog = ConfigureContactDialog(self.app.current_account, self) result = dialog.exec_() if result == QDialog.Accepted: self.window().refresh_contacts() def open_configure_account_dialog(self): dialog = ProcessConfigureAccount(self.app, self.app.current_account) result = dialog.exec_() if result == QDialog.Accepted: if self.app.current_account: self.action_change_account(self.app.current_account.name) else: self.refresh() def open_preferences_dialog(self): dialog = PreferencesDialog(self.app) result = dialog.exec_() def open_about_popup(self): """ Open about popup window """ aboutDialog = QDialog(self) aboutUi = Ui_AboutPopup() aboutUi.setupUi(aboutDialog) latest = self.app.available_version version_info = "" version_url = "" if not latest[0]: version_info = self.tr("Latest release : {version}") \ .format(version=latest[1]) version_url = latest[2] new_version_text = """ <p><b>{version_info}</b></p> <p><a href="{version_url}">{link_text}</a></p> """.format(version_info=version_info, version_url=version_url, link_text=self.tr("Download link")) else: new_version_text = "" text = self.tr(""" <h1>Cutecoin</h1> <p>Python/Qt uCoin client</p> <p>Version : {:}</p> {new_version_text} <p>License : MIT</p> <p><b>Authors</b></p> <p>inso</p> <p>vit</p> <p>canercandan</p> """).format(__version__, new_version_text=new_version_text) aboutUi.label.setText(text) aboutDialog.show() @pyqtSlot() def latest_version_requested(self): latest = self.app.available_version logging.debug("Latest version requested") if not latest[0]: version_info = self.tr("Please get the latest release {version}") \ .format(version=latest[1]) version_url = latest[2] toast.display("Cutecoin", """<p>{version_info}</br> <a href={version_url}>Download link</a></p>""".format( version_info=version_info, version_url=version_url)) def refresh_wallets(self): currency_tab = self.currencies_tabwidget.currentWidget() if currency_tab: currency_tab.refresh_wallets() def refresh_communities(self): logging.debug("CLEAR") self.currencies_tabwidget.clear() if self.app.current_account: for community in self.app.current_account.communities: tab_currency = CurrencyTabWidget(self.app, community, self.password_asker, self.status_label) tab_currency.refresh() self.currencies_tabwidget.addTab(tab_currency, QIcon(":/icons/currency_icon"), community.name) def refresh_accounts(self): self.menu_change_account.clear() signal_mapper = QSignalMapper(self) for account_name in sorted(self.app.accounts.keys()): action = QAction(account_name, self) self.menu_change_account.addAction(action) signal_mapper.setMapping(action, account_name) action.triggered.connect(signal_mapper.map) signal_mapper.mapped[str].connect(self.action_change_account) def refresh_contacts(self): self.menu_contacts_list.clear() if self.app.current_account: for index, contact in enumerate(self.app.current_account.contacts): contact_menu = self.menu_contacts_list.addMenu(contact['name']) edit_action = contact_menu.addAction(self.tr("Edit")) edit_action.triggered.connect(self.edit_contact) edit_action.setData(index) delete_action = contact_menu.addAction(self.tr("Delete")) delete_action.setData(contact) delete_action.triggered.connect(self.delete_contact) def refresh(self): ''' Refresh main window When the selected account changes, all the widgets in the window have to be refreshed ''' logging.debug("Refresh started") self.refresh_accounts() if self.app.current_account is None: self.currencies_tabwidget.hide() self.homescreen.show() self.setWindowTitle(self.tr("CuteCoin {0}").format(__version__)) self.menu_account.setEnabled(False) self.action_configure_parameters.setEnabled(False) self.action_set_as_default.setEnabled(False) self.combo_referential.setEnabled(False) self.status_label.setText(self.tr("")) self.password_asker = None else: logging.debug("Show currencies loading") self.currencies_tabwidget.show() logging.debug("Hide homescreen") self.homescreen.hide() self.password_asker = PasswordAskerDialog(self.app.current_account) self.combo_referential.blockSignals(True) self.combo_referential.clear() for ref in self.app.current_account.referentials: self.combo_referential.addItem(QCoreApplication.translate('Account', ref[4])) self.combo_referential.setEnabled(True) self.combo_referential.blockSignals(False) logging.debug(self.app.preferences) self.combo_referential.setCurrentIndex(self.app.preferences['ref']) self.menu_account.setEnabled(True) self.action_configure_parameters.setEnabled(True) self.setWindowTitle(self.tr("CuteCoin {0} - Account : {1}").format(__version__, self.app.current_account.name)) self.refresh_communities() self.refresh_wallets() self.refresh_contacts() def import_account(self): self.import_dialog = ImportAccountDialog(self.app, self) self.import_dialog.accepted.connect(self.import_account_accepted) self.import_dialog.exec_() def import_account_accepted(self): # open account after import self.action_change_account(self.import_dialog.edit_name.text()) def export_account(self): # Testable way of using a QFileDialog self.export_dialog = QFileDialog(self) self.export_dialog.setObjectName('ExportFileDialog') self.export_dialog.setWindowTitle(self.tr("Export an account")) self.export_dialog.setNameFilter(self.tr("All account files (*.acc)")) self.export_dialog.setLabelText(QFileDialog.Accept, self.tr('Export')) self.export_dialog.accepted.connect(self.export_account_accepted) self.export_dialog.show() def export_account_accepted(self): selected_file = self.export_dialog.selectedFiles() if selected_file: if selected_file[0][-4:] == ".acc": path = selected_file[0] else: path = selected_file[0] + ".acc" self.app.export_account(path, self.app.current_account) def closeEvent(self, event): if self.app.current_account: self.app.save_cache(self.app.current_account) self.app.save_persons() super().closeEvent(event) def showEvent(self, event): super().showEvent(event) if not self.initialized: # if default account in preferences... if self.app.preferences['account'] != "": logging.debug("Loading default account") self.action_change_account(self.app.preferences['account']) # no default account... else: # if at least one account exists, set it as default... if len(self.app.accounts) > 0: # capture names sorted alphabetically names = list(self.app.accounts.keys()) names.sort() # set first name in list as default in preferences self.app.preferences['account'] = names[0] self.app.save_preferences(self.app.preferences) # open it logging.debug("No default account in preferences. Set %s as default account." % names[0]) self.action_change_account(self.app.preferences['account']) self.initialized = True
class PropertyWidget(QWidget): value_changed = pyqtSignal(object) """display widget for tcam property""" def __init__(self, data: TcamCaptureData, prop: Prop, app_property: bool = False): super().__init__() self.app_property = app_property self.tcam = data.tcam self.signals = data.signals self.prop = prop self.is_log = False self.setup_ui() def __repr__(self): return repr((self.prop.name, self.prop.valuetype, self.prop.category, self.prop.group)) def __setup_ui_boolean(self): """ Helper function that contains all setup code for bool UIs """ self.toggle = QCheckBox(self) self.toggle.setCheckable(True) if self.prop.value: self.toggle.toggle() self.toggle.toggled.connect(self.button_clicked) self.layout.addWidget(self.toggle) def __setup_ui_integer(self): """ """ self.value_box = TcamSpinBox.TcamSpinBox(self) try: self.value_box.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("Property {} reported a range " "that could not be handled".format(self.prop.name)) self.value_box.setSingleStep(self.prop.step) self.value_box.blockSignals(True) self.value_box.setValue(self.prop.value) self.value_box.blockSignals(False) self.value_box.valueChanged[int].connect(self.set_property_box) self.value_box.setKeyboardTracking(False) if self.is_log: log.debug("Adding log slider for {}".format(self.prop.name)) self.sld = TcamSlider.TcamLogSlider(self) self.sld.valueLogChanged.connect(self.set_property) else: self.sld = TcamSlider.TcamSlider(self) self.sld.valueChanged[int].connect(self.set_property) self.sld.setFocusPolicy(Qt.NoFocus) try: self.sld.blockSignals(True) self.sld.setRange(self.prop.minval, self.prop.maxval) self.sld.setValue(self.prop.value) self.sld.blockSignals(False) except OverflowError: log.error("Property {} reported a range " "that could not be handled".format(self.prop.name)) self.sld.setSingleStep(self.prop.step) self.sld.doubleClicked.connect(self.reset) self.layout.addWidget(self.sld) self.layout.addWidget(self.value_box) def __setup_ui_double(self): """ Helper function that contains all setup code for doube UIs """ self.value_box = TcamSpinBox.TcamDoubleSpinBox(self) try: self.value_box.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("Property {} reported a range " "that could not be handled".format(self.prop.name)) self.value_box.setSingleStep(self.prop.step) self.value_box.blockSignals(True) self.value_box.setValue(self.prop.value) self.value_box.blockSignals(False) self.value_box.valueChanged[float].connect(self.set_property_box) self.value_box.setKeyboardTracking(False) if self.is_log: self.sld = TcamSlider.TcamLogSlider(self) self.sld.valueLogChanged.connect(self.set_property) else: self.sld = TcamSlider.TcamSlider(self) self.sld.valueChanged[int].connect(self.set_property) self.sld.setFocusPolicy(Qt.NoFocus) try: self.sld.blockSignals(True) self.sld.setRange(int(self.prop.minval * 100), int(self.prop.maxval * 100)) self.sld.setValue(int(self.prop.value * 100)) self.sld.blockSignals(False) except OverflowError: log.warning("Property {} reported a range " "that could not be handled min: {} max: {}".format( self.prop.name, self.prop.minval, self.prop.maxval)) self.sld.setSingleStep(int(self.prop.step * 100)) self.sld.doubleClicked.connect(self.reset) self.sld.setGeometry(30, 40, 100, 30) self.layout.addWidget(self.sld) self.layout.addWidget(self.value_box) def setup_ui(self): self.layout = QHBoxLayout() def should_be_logarithmic(prop): """ Returns True when the range is > 5000 """ if prop.valuetype == "integer": if (prop.maxval - prop.minval) >= 5000: return True elif prop.valuetype == "double": if (prop.maxval - prop.minval) >= 5000: return True return False self.setLayout(self.layout) self.is_log = should_be_logarithmic(self.prop) if self.prop.valuetype == "integer": self.__setup_ui_integer() elif self.prop.valuetype == "double": self.__setup_ui_double() elif self.prop.valuetype == "button": self.checkbox = QPushButton(self) self.checkbox.clicked.connect(self.set_property) self.layout.addWidget(self.checkbox) elif self.prop.valuetype == "boolean": self.__setup_ui_boolean() elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo = QComboBox(self) entry_list = self.tcam.get_tcam_menu_entries(self.prop.name) for e in entry_list: self.combo.addItem(e) self.combo.setCurrentText(self.prop.value) self.combo.currentIndexChanged['QString'].connect( self.set_property) self.layout.addWidget(self.combo) def button_clicked(self): log.debug("button clicked") self.signals.change_property.emit(self.tcam, self.prop.name, self.toggle.isChecked(), self.prop.valuetype) self.value_changed.emit(self) def set_property(self, value, emit_value_changed=True): self.prop.value = value if self.prop.valuetype == "integer": self.update_box_value(self.value_box, value) if self.prop.valuetype == "double": self.update_box_value(self.value_box, value / 100) self.signals.change_property.emit(self.tcam, self.prop.name, float(value / 100), self.prop.valuetype) return self.signals.change_property.emit(self.tcam, self.prop.name, value, self.prop.valuetype) if emit_value_changed: self.value_changed.emit(self) def set_property_box(self, value): if self.prop.valuetype == "integer": self.update_slider_value(self.sld, value) if self.prop.valuetype == "double": self.update_slider_value(self.sld, value) self.signals.change_property.emit(self.tcam, self.prop.name, float(value), self.prop.valuetype) return self.signals.change_property.emit(self.tcam, self.prop.name, value, self.prop.valuetype) self.value_changed.emit(self) def update_box_value(self, box, value): box.blockSignals(True) box.setValue(value) box.blockSignals(False) def update_box_range(self, box, minval, maxval): """""" box.blockSignals(True) box.setRange(self.prop.minval, self.prop.maxval) box.blockSignals(False) def update_slider_value(self, slider, value): slider.blockSignals(True) try: if self.prop.valuetype == "double": slider.setValue(int(value * 100)) else: slider.setValue(value) except OverflowError: log.info("The slider for '{}' had a value outside of the integer " "range. That should no happen.".format(self.prop.name)) finally: slider.blockSignals(False) def update_slider_range(self, slider, minval, maxval): """""" self.sld.blockSignals(True) try: if self.prop.valuetype == "double": self.sld.setRange(self.prop.minval * 100, self.prop.maxval * 100) else: self.sld.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("The slider for '{}' had a value outside of the integer " "range. That should no happen. " "Min: '{}' Max: {}".format(self.prop.name, self.prop.minval, self.prop.maxval)) finally: self.sld.blockSignals(False) def update(self, prop: Prop): emit_value_changed = False if self.prop.value != prop.value: emit_value_changed = True self.prop = prop if self.prop.valuetype == "integer": if type(self.value_box) is TcamSpinBox.TcamSpinBox: if self.value_box.active(): # box.setValue(value) return self.update_slider_value(self.sld, self.prop.value) self.update_slider_range(self.sld, self.prop.minval, self.prop.maxval) self.update_box_range(self.value_box, self.prop.minval, self.prop.maxval) self.update_box_value(self.value_box, self.prop.value) elif self.prop.valuetype == "double": self.update_slider_range(self.sld, self.prop.minval, self.prop.maxval) self.update_slider_value(self.sld, self.prop.value) self.update_box_range(self.value_box, self.prop.minval, self.prop.maxval) self.update_box_value(self.value_box, self.prop.value) elif self.prop.valuetype == "button": pass elif self.prop.valuetype == "boolean": if self.prop.value and not self.toggle.isChecked(): self.toggle.blockSignals(True) self.toggle.toggle() self.toggle.blockSignals(False) elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo.blockSignals(True) self.combo.setCurrentText(prop.value) self.combo.blockSignals(False) if emit_value_changed: self.value_changed.emit(self) def reset(self): if self.prop.valuetype == "integer": self.update_box_value(self.value_box, self.prop.defval) self.update_slider_value(self.sld, self.prop.defval) elif self.prop.valuetype == "double": self.update_box_value(self.value_box, self.prop.defval) self.update_slider_value(self.sld, self.prop.defval) elif self.prop.valuetype == "button": pass elif self.prop.valuetype == "boolean": if self.prop.defval and not self.toggle.isChecked(): self.toggle.toggle() elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo.setCurrentText(self.prop.defval) self.value_changed.emit(self) self.signals.change_property.emit(self.tcam, self.prop.name, self.prop.defval, self.prop.valuetype)
class MetricsToolBar(QToolBar): """ Emits *pointSizeChanged*. """ glyphsChanged = pyqtSignal(list) settingsChanged = pyqtSignal(dict) def __init__(self, font, parent=None): super().__init__(parent) auxiliaryWidth = self.fontMetrics().width('0') * 8 self.leftTextField = MetricsSequenceEdit(font, self) self.leftTextField.setMaximumWidth(auxiliaryWidth) self.textField = MetricsSequenceComboBox(font, self) # XXX: had to use Maximum because Preferred did extend the widget(?) self.textField.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Maximum) self.rightTextField = MetricsSequenceEdit(font, self) self.rightTextField.setMaximumWidth(auxiliaryWidth) self.leftTextField.textEdited.connect(self.textField.editTextChanged) self.rightTextField.textEdited.connect(self.textField.editTextChanged) self.textField.editTextChanged.connect(self._textChanged) self.comboBox = QComboBox(self) self.comboBox.setEditable(True) self.comboBox.setCompleter(None) self.comboBox.setValidator(QIntValidator(self)) for p in pointSizes: self.comboBox.addItem(str(p)) self.pointSizeChanged = self.comboBox.currentIndexChanged[str] self.configBar = QPushButton(self) self.configBar.setFlat(True) self.configBar.setIcon(QIcon(":settings.svg")) self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") self.toolsMenu = QMenu(self) self._showKerning = self.toolsMenu.addAction( self.tr("Show Kerning"), self._kerningVisibilityChanged) self._showKerning.setCheckable(True) self._showMetrics = self.toolsMenu.addAction( self.tr("Show Metrics"), self._controlsTriggered) self._showMetrics.setCheckable(True) self.toolsMenu.addSeparator() self._verticalFlip = self.toolsMenu.addAction( self.tr("Vertical Flip"), self._controlsTriggered) self._verticalFlip.setCheckable(True) self._wrapLines = self.toolsMenu.addAction( self.tr("Wrap Lines"), self._controlsTriggered) self._wrapLines.setCheckable(True) self.toolsMenu.addSeparator() action = self.toolsMenu.addAction(self.tr("Line Height:")) action.setEnabled(False) lineHeight = QWidgetAction(self.toolsMenu) self._lineHeightSlider = slider = QSlider(Qt.Horizontal, self) # QSlider works with integers so we'll just divide what comes out of it # by 100 slider.setMinimum(80) slider.setMaximum(160) slider.setValue(110) slider.valueChanged.connect(self._controlsTriggered) slider.valueChanged.connect(self._sliderLineHeightChanged) lineHeight.setDefaultWidget(slider) self.toolsMenu.addAction(lineHeight) self.configBar.setMenu(self.toolsMenu) self.addWidget(self.leftTextField) self.addWidget(self.textField) self.addWidget(self.rightTextField) self.addWidget(self.comboBox) self.addWidget(self.configBar) app = QApplication.instance() app.dispatcher.addObserver( self, "_currentGlyphChanged", "currentGlyphChanged") self.readSettings() def readSettings(self): items = settings.metricsWindowComboBoxItems() self.textField.clear() self.textField.addItems(items) # ------------- # Notifications # ------------- # app def _currentGlyphChanged(self, notification): self._textChanged() # widget def _controlsTriggered(self): params = dict( lineHeight=self._lineHeightSlider.value() / 100, showKerning=self._showKerning.isChecked(), showMetrics=self._showMetrics.isChecked(), verticalFlip=self._verticalFlip.isChecked(), wrapLines=self._wrapLines.isChecked(), ) self.settingsChanged.emit(params) def _kerningVisibilityChanged(self): self._controlsTriggered() # if showKerning was triggered, it won't apply until we pipe the glyphs # again. do so self._textChanged() def _sliderLineHeightChanged(self, value): QToolTip.showText(QCursor.pos(), str(value / 100), self) def _textChanged(self): leftGlyphs = self.leftTextField.glyphs() rightGlyphs = self.rightTextField.glyphs() glyphs = self.textField.glyphs() ret = [] for glyph in glyphs: ret.extend(leftGlyphs + [glyph] + rightGlyphs) self.glyphsChanged.emit(ret) # -------------- # Public methods # -------------- def setPointSize(self, pointSize): self.comboBox.blockSignals(True) self.comboBox.setEditText(str(pointSize)) self.comboBox.blockSignals(False) def setText(self, text, left=None, right=None): self.leftTextField.setText(left) self.rightTextField.setText(right) self.textField.setEditText(text) def setWrapLines(self, value): self._wrapLines.setChecked(value) self._controlsTriggered() # TODO: more methods # ---------- # Qt methods # ---------- def closeEvent(self, event): super().closeEvent(event) if event.isAccepted(): app = QApplication.instance() app.dispatcher.removeObserver(self, "currentGlyphChanged") def showEvent(self, event): super().showEvent(event) self.textField.setFocus(True)
class DataManager(QWidget): wldsetChanged = QSignal(object) wxdsetChanged = QSignal(object) sig_workdir_changed = QSignal(str) sig_new_console_msg = QSignal(str) def __init__(self, parent=None, projet=None, pm=None, pytesting=False): super(DataManager, self).__init__(parent) self._pytesting = pytesting self._projet = projet self._confirm_before_deleting_dset = True self._wldset = None self._wxdset = None self.setWindowFlags(Qt.Window) self.setWindowIcon(icons.get_icon('master')) self.setMinimumWidth(250) self.weather_avg_graph = None self.new_waterlvl_win = NewDatasetDialog('water level', parent, projet) self.new_waterlvl_win.sig_new_dataset_imported.connect( self.new_wldset_imported) self.new_weather_win = NewDatasetDialog('daily weather', parent, projet) self.new_weather_win.sig_new_dataset_imported.connect( self.new_wxdset_imported) self.setup_manager() self.set_projet(projet) if pm: pm.currentProjetChanged.connect(self.set_projet) self.set_projet(pm.projet) def setup_manager(self): """Setup the layout of the manager.""" layout = QGridLayout(self) layout.setSpacing(5) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.setup_wldset_mngr(), 0, 0) layout.addWidget(self.setup_wxdset_mngr(), 2, 0) def setup_wldset_mngr(self): """Setup the manager for the water level datasets.""" # ---- Toolbar self.wldsets_cbox = QComboBox() self.wldsets_cbox.currentIndexChanged.connect(self.wldset_changed) self.btn_load_wl = QToolButtonSmall(icons.get_icon('importFile')) self.btn_load_wl.setToolTip('Import a new water level dataset...') self.btn_load_wl.clicked.connect(self.import_wldataset) self.btn_del_wldset = QToolButtonSmall('delete_data') self.btn_del_wldset.setToolTip('Delete current dataset.') self.btn_del_wldset.clicked.connect(self.del_current_wldset) wl_toolbar = ToolBarWidget() for widg in [self.btn_load_wl, self.btn_del_wldset]: wl_toolbar.addWidget(widg) # ---- Info Box self.well_info_widget = StrSpinBox() # ---- Main Layout grpbox = QGroupBox('Water Level Dataset : ') layout = QGridLayout(grpbox) layout.setSpacing(5) layout.addWidget(self.wldsets_cbox, 1, 0) layout.addWidget(self.well_info_widget, 2, 0) layout.addWidget(wl_toolbar, 3, 0) return grpbox def setup_wxdset_mngr(self): """Setup the manager for the weather datasets.""" # ---- Toolbar self.wxdsets_cbox = QComboBox() self.wxdsets_cbox.currentIndexChanged.connect(self.wxdset_changed) self.btn_load_meteo = QToolButtonSmall(icons.get_icon('importFile')) self.btn_load_meteo.setToolTip('Import a new weather dataset...') self.btn_load_meteo.clicked.connect(self.import_wxdataset) self.btn_del_wxdset = QToolButtonSmall('delete_data') self.btn_del_wxdset.setToolTip('Delete current dataset.') self.btn_del_wxdset.clicked.connect(self.del_current_wxdset) btn_closest_meteo = QToolButtonSmall(icons.get_icon('closest_meteo')) btn_closest_meteo.setToolTip('<p>Select the weather station closest' ' from the observation well.</p>') btn_closest_meteo.clicked.connect(self.set_closest_wxdset) btn_weather_normals = QToolButtonSmall(icons.get_icon('meteo')) btn_weather_normals.setToolTip( "Show the normals for the current weather dataset.") btn_weather_normals.clicked.connect(self.show_weather_normals) self.btn_export_weather = ExportWeatherButton(workdir=self.workdir) self.btn_export_weather.setIconSize(icons.get_iconsize('small')) wx_toolbar = ToolBarWidget() for widg in [ self.btn_load_meteo, self.btn_del_wxdset, btn_closest_meteo, btn_weather_normals, self.btn_export_weather ]: wx_toolbar.addWidget(widg) # ---- Info Box self.meteo_info_widget = StrSpinBox() # ---- Main Layout grpbox = QGroupBox('Weather Dataset : ') layout = QGridLayout(grpbox) layout.setSpacing(5) layout.addWidget(self.wxdsets_cbox, 1, 0) layout.addWidget(self.meteo_info_widget, 2, 0) layout.addWidget(wx_toolbar, 3, 0) return grpbox @property def workdir(self): """Return the path where the project hdf5 file is saved.""" if self.projet is None: return osp.dirname(os.getcwd()) else: return osp.dirname(self.projet.filename) @property def projet(self): """Return the projet object.""" return self._projet def set_projet(self, projet): """Set the namespace for the projet hdf5 file.""" self._projet = projet self._wldset = None self._wxdset = None if projet is not None: self.update_wldsets(projet.get_last_opened_wldset()) self.update_wxdsets(projet.get_last_opened_wxdset()) self.wldset_changed() self.btn_export_weather.set_model(self.get_current_wxdset()) self.btn_export_weather.set_workdir(self.workdir) self.new_waterlvl_win.set_projet(projet) self.new_weather_win.set_projet(projet) self.sig_workdir_changed.emit(self.workdir) # ---- Utilities def emit_warning(self, msg): btn = QMessageBox.Ok QMessageBox.warning(self, 'Warning', msg, btn) def confirm_del_dataset(self, dsetname, dsettype): """ Show a message box asking the user confirmation before deleting a dataset. Return the user's answer and whether the 'do not show this message again' checkbox has been checked or not. """ msg_box = QMessageBox( QMessageBox.Question, "Delete {} dataset '{}'".format(dsettype, dsetname), ("Do you want to delete the {} dataset <i>{}</i>?<br><br>" "All data will be deleted from the project, but the " "original data files will be preserved.<br>").format( dsettype, dsetname), buttons=QMessageBox.Yes | QMessageBox.Cancel, parent=self) checkbox = QCheckBox("Don't show this message again.") msg_box.setCheckBox(checkbox) reply = msg_box.exec_() return reply, not checkbox.isChecked() # ---- WL Dataset @property def wldsets(self): """Return a list of all the wldset saved in the project.""" return [] if self.projet is None else self.projet.wldsets def wldataset_count(self): """Return the total number of wldset saved in the project.""" return len(self.wldsets) def import_wldataset(self): """Open a dialog window to import a water level dataset from a file.""" if self.projet is None: msg = ("Please first select a valid project or create a new one.") btn = QMessageBox.Ok QMessageBox.warning(self, 'Create dataset', msg, btn) return else: if self._pytesting: self.new_waterlvl_win.show() else: self.new_waterlvl_win.exec_() def new_wldset_imported(self, name, dataset): """ Receives the new water level dataset, saves it in the project and update the GUI. """ print("Saving the new water level dataset in the project...", end=" ") self.projet.add_wldset(name, dataset) self.update_wldsets(name) self.wldset_changed() print("done") def update_wldsets(self, name=None): self.wldsets_cbox.blockSignals(True) self.wldsets_cbox.clear() self.wldsets_cbox.addItems(self.projet.wldsets) if name: self.wldsets_cbox.setCurrentIndex(self.wldsets_cbox.findText(name)) self.wldsets_cbox.blockSignals(False) def update_wldset_info(self): """Update the infos of the wldset.""" wldset = self.get_current_wldset() if wldset is not None: model = [ "Well : %s" % wldset['Well'], "Well ID : %s" % wldset['Well ID'], "Latitude : %0.3f°" % wldset['Latitude'], "Longitude : %0.3f°" % wldset['Longitude'], "Elevation : %0.1f m" % wldset['Elevation'], "Municipality : %s" % wldset['Municipality'], "Province : %s" % wldset['Province'] ] else: model = None self.well_info_widget.set_model(model) def wldset_changed(self): """Handle when the currently selected water level dataset changed.""" QApplication.processEvents() self.update_wldset_info() self.wldsetChanged.emit(self.get_current_wldset()) def get_current_wldset(self): """Return the currently selected water level dataset.""" if self.wldsets_cbox.currentIndex() == -1: self._wldset = None else: cbox_text = self.wldsets_cbox.currentText() if self._wldset is None or self._wldset.name != cbox_text: self._wldset = self.projet.get_wldset(cbox_text) return self._wldset def set_current_wldset(self, name): """Set the current water level from its name.""" self.wldsets_cbox.blockSignals(True) self.wldsets_cbox.setCurrentIndex(self.wldsets_cbox.findText(name)) self.wldsets_cbox.blockSignals(False) self.wldset_changed() def del_current_wldset(self): """Delete the currently selected water level dataset.""" if self.wldsets_cbox.count() > 0: dsetname = self.wldsets_cbox.currentText() if self._confirm_before_deleting_dset: reply, dont_show_again = self.confirm_del_dataset( dsetname, 'water level') if reply == QMessageBox.Cancel: return elif reply == QMessageBox.Yes: self._confirm_before_deleting_dset = dont_show_again self._wldset = None self.projet.del_wldset(dsetname) self.update_wldsets() self.wldset_changed() self.sig_new_console_msg.emit( ("<font color=black>Water level dataset <i>{}</i> deleted " "successfully.</font>").format(dsetname)) # ---- WX Dataset @property def wxdsets(self): """Return a list of all the weather datasets saved in the project.""" return [] if self.projet is None else self.projet.wxdsets def wxdataset_count(self): """Return the total number of weather datasets saved in the project.""" return len(self.wxdsets) def import_wxdataset(self): """Open a dialog window to import a weather dataset from a file.""" if self.projet is None: msg = ("Please first select a valid project or create a new one.") btn = QMessageBox.Ok QMessageBox.warning(self, 'Create dataset', msg, btn) return else: if self._pytesting: self.new_weather_win.show() else: self.new_weather_win.exec_() def new_wxdset_imported(self, name, dataset): """ Receive the new weather dataset, save it in the project and update the GUI. """ print("Saving the new weather dataset in the project.", end=" ") self.projet.add_wxdset(name, dataset) self.update_wxdsets(name) self.wxdset_changed() print("done") def update_wxdsets(self, name=None, silent=False): self.wxdsets_cbox.blockSignals(True) self.wxdsets_cbox.clear() self.wxdsets_cbox.addItems(self.projet.wxdsets) if name: self.wxdsets_cbox.setCurrentIndex(self.wxdsets_cbox.findText(name)) self.wxdsets_cbox.blockSignals(False) def update_wxdset_info(self): """Update the infos of the wxdset.""" wxdset = self.get_current_wxdset() if wxdset is not None: model = [ "Station : %s" % wxdset.metadata['Station Name'], "Station ID : %s" % wxdset.metadata['Station ID'], "Latitude : %0.3f°" % wxdset.metadata['Latitude'], "Longitude : %0.3f°" % wxdset.metadata['Longitude'], "Elevation : %0.1f m" % wxdset.metadata['Elevation'], "Location : %s" % wxdset.metadata['Location'] ] else: model = None self.meteo_info_widget.set_model(model) def wxdset_changed(self): """Handle when the currently selected weather dataset changed.""" QApplication.processEvents() self.update_wxdset_info() self.btn_export_weather.set_model(self.get_current_wxdset()) self.wxdsetChanged.emit(self.get_current_wxdset()) def del_current_wxdset(self): """Delete the currently selected weather dataset.""" if self.wxdsets_cbox.count() > 0: dsetname = self.wxdsets_cbox.currentText() if self._confirm_before_deleting_dset: reply, dont_show_again = self.confirm_del_dataset( dsetname, 'weather') if reply == QMessageBox.Cancel: return elif reply == QMessageBox.Yes: self._confirm_before_deleting_dset = dont_show_again self._wxdset = None self.projet.del_wxdset(dsetname) self.update_wxdsets() self.wxdset_changed() self.sig_new_console_msg.emit( ("<font color=black>Weather dataset <i>{}</i> deleted " "successfully.</font>").format(dsetname)) def get_current_wxdset(self): """Return the currently selected weather dataset dataframe.""" if self.wxdsets_cbox.currentIndex() == -1: self._wxdset = None else: cbox_text = self.wxdsets_cbox.currentText() if self._wxdset is None or self._wxdset.name != cbox_text: self._wxdset = self.projet.get_wxdset(cbox_text) return self._wxdset def set_current_wxdset(self, name): """Set the current weather dataset from its name.""" self.wxdsets_cbox.blockSignals(True) self.wxdsets_cbox.setCurrentIndex(self.wxdsets_cbox.findText(name)) self.wxdsets_cbox.blockSignals(False) self.wxdset_changed() def set_closest_wxdset(self): """ Set the weather dataset of the station that is closest to the groundwater observation well. """ if self._wldset is None or self.wxdataset_count() == 0: return None dist = calc_dist_from_coord(self._wldset['Latitude'], self._wldset['Longitude'], self.projet.get_wxdsets_lat(), self.projet.get_wxdsets_lon()) closest_station = self.wxdsets[np.argmin(dist)] self.set_current_wxdset(closest_station) return closest_station def show_weather_normals(self): """Show the weather normals for the current weather dataset.""" if self.get_current_wxdset() is None: return if self.weather_avg_graph is None: self.weather_avg_graph = WeatherViewer() self.weather_avg_graph.set_workdir(self.workdir) self.weather_avg_graph.set_weather_dataset(self.get_current_wxdset()) self.weather_avg_graph.show()
class SelectDialog(QWidget): """ Show a message box with an input field. Inherits: QDialog Signals: None """ sig_start = pyqtSignal(object) sig_new_config = pyqtSignal(str, str) def __init__(self, *args, parent=None, **kwargs): """ Initialise layout of the widget. Arguments: regex - Regularexpression to analyse parent - Parent widget Returns: None """ super(SelectDialog, self).__init__(parent) self.columns = 10 self.settings = None self.content = [] self.widgets_dict = {} self.widgets = [] self.neutral_name = 'neutral' self.bad_name = 'bad' self.good_name = 'good' self.input_name = 'input_files' self.model_name = 'model.h5' self.config_name = 'config.json' self.default_project_name = 'Project' self.model_out = None self.config_out = None self.current_index = 0 self.layouts = {} self.labels = {} self.button_dict = {} self.current_model = None self.log_folder = None self.good_folder = None self.bad_folder = None self.classes_folder = None self.settings_file = None self.e2proc2d_exec = None self.sp_cinderella_train_exec = None self.sp_cinderella_predict_exec = None self.set_enable = None layout = QVBoxLayout(self) layout_h0 = QHBoxLayout() layout.addLayout(layout_h0) layout_h1 = QHBoxLayout() layout.addLayout(layout_h1) layout_h2 = QHBoxLayout() layout.addLayout(layout_h2) layout_h3 = QHBoxLayout() layout.addLayout(layout_h3) btn_done = QPushButton('Retrain', self) btn_done.clicked.connect(self.retrain) layout_h3.addWidget(btn_done) self.content.append(btn_done) layout_h3.addStretch(1) label_threshold = QLabel('Threshold', self) layout_h3.addWidget(label_threshold) self.content.append(label_threshold) self.threshold_repick = QLineEdit('0.5', self) layout_h3.addWidget(self.threshold_repick) self.content.append(self.threshold_repick) self.button_repick = QPushButton('Repick', self) self.button_repick.clicked.connect(self.repick) layout_h3.addWidget(self.button_repick) self.content.append(self.button_repick) btn = QPushButton('Set', self) btn.clicked.connect(self.set_settings) layout_h3.addWidget(btn) self.content.append(btn) self.edit = QLineEdit(str(self.columns), self) self.edit.editingFinished.connect(self.adjust_all_layouts) layout_h0.addWidget(QLabel('Columns:', self)) layout_h0.addWidget(self.edit) self.content.append(self.edit) self.combo_text = QComboBox(self) self.combo_text.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.combo_text.currentTextChanged.connect(self.set_current_folder) layout_h0.addWidget(self.combo_text) self.content.append(self.combo_text) self.btn_update = QPushButton('Update', self) self.btn_update.clicked.connect(self.start_retrain) layout_h0.addWidget(self.btn_update) self.content.append(self.btn_update) self.prefer_isac_checkbox = QCheckBox('Prefer ISAC', self) self.prefer_isac_checkbox.setToolTip( 'If Project is selected, prefer ISAC over Cinderella runs. This will put all classes in the neutral position' ) layout_h0.addWidget(self.prefer_isac_checkbox) self.content.append(self.prefer_isac_checkbox) for current_name in (self.good_name, self.neutral_name, self.bad_name): self.labels[current_name] = QLabel(current_name, self) self.widgets_dict[current_name] = [] layout_h1.addWidget(self.labels[current_name]) widget = QWidget(self) scroll_area = QScrollArea(self) scroll_area.setWidgetResizable(True) scroll_area.setWidget(widget) scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.layouts[current_name] = QVBoxLayout(widget) self.layouts[current_name].setSpacing(2) self.layouts[current_name].addStretch(1) layout_h2.addWidget(scroll_area) self.sig_start.connect(self.start_retrain) self.adjust_all_layouts() self.enable(False) @pyqtSlot() def set_settings(self): try: name, threshold = self.combo_text.currentText().rsplit('_', 1) except ValueError: tu.message('Please provide a repicking run and not {}.'.format( self.default_project_name)) return model = os.path.join(self.log_folder, name, self.model_name) self.sig_new_config.emit(model, threshold) tu.message('Set model: {}\nSet threshold: {}'.format(model, threshold)) def enable(self, var, use_all=None): if not self.set_enable and var and self.set_enable is not None: var = False for entry in self.content: entry.setEnabled(var) if var: var = False if self.combo_text.currentText() == self.default_project_name: var = True self.btn_update.setEnabled(var) self.prefer_isac_checkbox.setEnabled(var) @pyqtSlot(str) @pyqtSlot() def set_current_folder(self, text=None): if text is None: text = self.combo_text.currentText() var = False if text == self.default_project_name: var = True self.btn_update.setEnabled(var) self.prefer_isac_checkbox.setEnabled(var) self.start_retrain(input_folder=text) def add_combo_item(self, items): current_items = [ self.combo_text.itemText(idx) for idx in range(self.combo_text.count()) ] current_items.extend(items) prev_state = self.combo_text.blockSignals(True) self.combo_text.clear() self.combo_text.addItems(sorted(list(set(current_items)))) self.combo_text.blockSignals(prev_state) @pyqtSlot(object) @pyqtSlot() def start_retrain(self, settings=None, input_folder=None): if settings is not None: self.settings = settings file_names = [ os.path.basename(entry) for entry in glob.glob( os.path.join(self.settings['log_folder'], 'Retrain', 'RUN_*.*')) ] self.add_combo_item([self.default_project_name]) self.add_combo_item(file_names) self.clear() self.project_folder = self.settings['project_folder'] self.log_folder = os.path.join(self.settings['log_folder'], 'Retrain') self.classes_folder = os.path.join(self.log_folder, 'RUN_{0}') self.model_out = os.path.join(self.classes_folder, self.model_name) self.config_out = os.path.join(self.classes_folder, self.config_name) self.good_folder = os.path.join(self.classes_folder, self.good_name) self.bad_folder = os.path.join(self.classes_folder, self.bad_name) self.settings_file = os.path.join(self.log_folder, 'tmp_settings.json') self.e2proc2d_exec = self.settings['Path']['e2proc2d.py'] self.sp_cinderella_train_exec = self.settings['Path'][ 'sp_cinderella_train.py'] try: self.sp_cinderella_predict_exec = self.settings['Path'][ self.settings['Copy']['Select2d']] except KeyError: print( 'In order to use the retrain tool, please provide a program in Copy->Select2d and press: Monitor Start -> Monitor Stop' ) self.set_enable = False return else: self.set_enable = True for folder_name in (self.log_folder, ): tu.mkdir_p(folder_name) class_2d_folder = [] select_2d_folder = [] if input_folder is None or input_folder == self.default_project_name: for key in self.settings: if key.startswith('scratch_'): continue if 'class2d' in key.lower(): try: class_2d_folder.extend([ entry for entry in glob.glob( os.path.join( self.settings[key], '*', )) if os.path.isdir(entry) and not os.path.basename( entry).startswith('jpg') and not os.path. basename(entry).startswith('overview_plots') ]) except IndexError: pass elif 'select2d' in key.lower(): try: select_2d_folder.extend([ entry for entry in glob.glob( os.path.join( self.settings[key], '*', )) if os.path.isdir(entry) and not os.path.basename( entry).startswith('jpg') and not os.path. basename(entry).startswith('overview_plots') ]) except IndexError: pass if self.prefer_isac_checkbox.isChecked(): select_basenames = tuple( [os.path.basename(entry) for entry in class_2d_folder]) for entry in select_2d_folder[:]: if os.path.basename(entry) in select_basenames: select_2d_folder.remove(entry) else: select_basenames = tuple( [os.path.basename(entry) for entry in select_2d_folder]) for entry in class_2d_folder[:]: if os.path.basename(entry) in select_basenames: class_2d_folder.remove(entry) else: select_2d_folder.append(os.path.join(self.log_folder, input_folder)) self.fill(class_2d_folder) self.fill(select_2d_folder, cinderella=True) for label_name in self.labels: for idx in reversed(range(self.current_index, len(self.widgets))): button = self.widgets[idx] self.widgets.remove(button) button.setParent(None) self.adjust_all_layouts() def fill(self, folder, cinderella=False): if cinderella: labels = (self.good_name, self.bad_name) else: labels = (self.neutral_name, ) button_dict = {} for sub_folder in folder: for label_name in labels: suffix = '_{}'.format(label_name) if cinderella else '' for file_name in sorted( glob.glob( os.path.join(sub_folder, 'png{}'.format(suffix), '*'))): match = re.search('/([^/]*)-(\d*)\.+png', file_name) if match is None: continue class_id = int(match.group(2)) - 1 class_averages = os.path.join( sub_folder, '' if cinderella else 'ISAC2', match.group(1)) try: button = self.widgets[self.current_index] button.isac_class_averages = class_averages button.isac_class_id = class_id button.current_layout = label_name except KeyError: button = MyPushButton(label_name, class_averages, class_id, self) button.sig_click.connect(self.handle_change) self.widgets.append(button) except IndexError: button = MyPushButton(label_name, class_averages, class_id, self) button.sig_click.connect(self.handle_change) self.widgets.append(button) button.setIconSize(QSize(50, 50)) button.setToolTip( 'Class averages: {}\nClass id: {}'.format( class_averages, class_id)) button.setIcon(QIcon(file_name)) button.setStyleSheet( 'QPushButton {color: rgba(0, 0, 0 ,0); background-color: rgba(0, 0, 0, 0); border: 0px; border-color: rgba(0, 0, 0, 0); min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px}' ) button_dict.setdefault(class_averages, []).append(button) self.current_index += 1 try: with open(self.settings_file, 'r') as read: self.button_dict = json.load(read) except FileNotFoundError: pass for file_name, buttons in button_dict.items(): try: update = os.path.getmtime(file_name) < os.path.getmtime( self.settings_file) except FileNotFoundError: update = False for button in buttons: if update: try: button.current_layout = self.button_dict[ self.combo_text.currentText()][file_name][str( button.isac_class_id)] except KeyError: pass self.add_to_layout( button.current_layout, just_add=button, ) @pyqtSlot(object, object) def handle_change(self, sender, event): if sender.current_layout == self.neutral_name: if event.button() == Qt.LeftButton: self.switch_layout(sender, self.good_name) elif event.button() == Qt.RightButton: self.switch_layout(sender, self.bad_name) else: self.switch_layout(sender, self.neutral_name) @pyqtSlot() def adjust_all_layouts(self): try: self.columns = int(self.edit.text()) except Exception as e: print(e) self.edit.setText(str(self.columns)) return for name in self.layouts: self.add_to_layout(name) def add_to_layout(self, layout_name, add=None, remove=None, clear=False, just_add=None): if just_add: self.widgets_dict[layout_name].append(just_add) self.layouts[layout_name].addWidget(just_add) return layout_count = self.layouts[layout_name].count() for i in reversed(range(layout_count)): item = self.layouts[layout_name].itemAt(i) if isinstance(item, QSpacerItem): self.layouts[layout_name].removeItem(item) elif isinstance(item, QWidgetItem): self.layouts[layout_name].removeItem(item) #if clear: # item.widget().setParent(None) elif isinstance(item, QHBoxLayout): for j in reversed(range(item.count())): item_j = item.itemAt(j) if isinstance(item_j, QSpacerItem): item.removeItem(item_j) continue elif isinstance(item_j, QWidgetItem): pass #if clear: # item_j.widget().setParent(None) elif item_j is None: pass else: assert False, item_j self.layouts[layout_name].removeItem(item) else: assert False, item if clear: self.widgets_dict[layout_name] = [] self.current_index = 0 return if remove is not None: self.widgets_dict[layout_name].remove(remove) if add is not None: self.widgets_dict[layout_name].append(add) self.labels[layout_name].setText('{}: {}'.format( layout_name, len(self.widgets_dict[layout_name]))) a = np.array([(entry.isac_class_averages, entry.isac_class_id) for entry in self.widgets_dict[layout_name]], dtype='U5000,i8') indices = np.argsort(a, order=['f0', 'f1']) self.widgets_dict[layout_name] = np.array( self.widgets_dict[layout_name], dtype=np.object)[indices].tolist() layout = None for i in range(len(self.widgets_dict[layout_name])): i_hor = i % self.columns if i_hor == 0: if layout is not None: layout.addStretch(1) layout = QHBoxLayout() self.layouts[layout_name].addLayout(layout) layout.addWidget(self.widgets_dict[layout_name][i]) self.button_dict.setdefault( self.combo_text.currentText(), {}).setdefault( self.widgets_dict[layout_name][i].isac_class_averages, {})[str(self.widgets_dict[layout_name] [i].isac_class_id)] = layout_name if layout is not None: layout.addStretch(1) self.layouts[layout_name].addStretch(1) if self.widgets_dict[layout_name]: with open(self.settings_file, 'w') as write: json.dump(self.button_dict, write, indent=1) return self.widgets_dict[layout_name] def clear(self): for name in self.layouts: self.add_to_layout(name, clear=True) def switch_layout(self, sender, layout_name): self.add_to_layout(sender.current_layout, remove=sender) self.add_to_layout(layout_name, add=sender) sender.current_layout = layout_name @pyqtSlot() def retrain(self): time_string = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S') classes_folder = self.classes_folder.format(time_string) good_folder = self.good_folder.format(time_string) bad_folder = self.bad_folder.format(time_string) model_out = self.model_out.format(time_string) config_out = self.config_out.format(time_string) original_folder = os.path.join(classes_folder, self.input_name) tu.mkdir_p(original_folder) self.current_model = model_out config_file = os.path.join(os.path.dirname(__file__), 'templates', 'cinderella_config.json') with open(config_file, 'r') as read: content = read.read() content = content.replace('XXXGOODXXX', good_folder) content = content.replace('XXXBADXXX', bad_folder) content = content.replace('XXXMODELXXX', model_out) with open(config_out, 'w') as write: write.write(content) for current_name, out_dir_classes in ((self.bad_name, bad_folder), (self.good_name, good_folder)): os.makedirs(out_dir_classes, exist_ok=True) index_dict = {} widgets = self.add_to_layout(current_name) for widget in widgets: index_dict.setdefault(widget.isac_class_averages, []).append(widget.isac_class_id) for file_name, index_list in index_dict.items(): out_filename = file_name.replace(self.log_folder, '').replace( self.project_folder, '').replace('/', '_') out_symlink = os.path.join(original_folder, out_filename) print(out_filename) print(out_symlink) if not os.path.islink(out_symlink): tu.symlink_rel(file_name, out_symlink) out_file = os.path.join( classes_folder, '{}_{}_list.txt'.format(out_filename, current_name)) with open(out_file, 'w') as write: write.write('\n'.join(map(str, index_list))) cmd = '{} {} {} --list={}'.format( self.e2proc2d_exec, file_name, os.path.join(out_dir_classes, out_filename), out_file) print('Execute:', cmd) try: subprocess.call(cmd.split()) except Exception as e: print(e) cmd = '{} -c {}'.format(self.sp_cinderella_train_exec, config_out) print('Execute:', cmd) try: idx = subprocess.call(cmd.split()) except Exception as e: print(e) else: if not idx: self.repick(time_string) @pyqtSlot() def repick(self, time_string=None): try: if time_string is None: classes_folder = os.path.join( self.log_folder, self.combo_text.currentText().rsplit('_', 1)[0]) else: classes_folder = self.classes_folder.format(time_string) except ValueError: tu.message( 'Could not find model for current entry: {}.\nPlease provide a repicking run as starting point.' .format(self.combo_text.currentText())) return if not os.path.isdir(classes_folder): print(classes_folder) tu.message( 'Could not find model for current combo: {}.\nPlease provide a repicking run as starting point.' .format(self.combo_text.currentText())) return original_folder = os.path.join(classes_folder, self.input_name) model_out = os.path.join(classes_folder, self.model_name) threshold = float(self.threshold_repick.text()) output_folder = '{}_{}'.format(classes_folder, threshold) if not os.path.isfile(model_out): print(classes_folder) tu.message( 'Could not find model for current combo: {}.\nPlease provide a repicking run as starting point.' .format(self.combo_text.currentText())) return try: shutil.rmtree(output_folder) except Exception: pass cmd = '{} -i {} -o {} -w {} -t {}'.format( self.sp_cinderella_predict_exec, original_folder, output_folder, model_out, threshold, ) print('Execute:', cmd) try: subprocess.call(cmd.split()) except Exception as e: print(e) for entry in glob.glob(os.path.join(output_folder, '*.txt')): if not entry.endswith('_index_confidence.txt'): continue file_name = entry.rsplit('_index_confidence.txt', 1)[0] for suffix in ('_good', '_bad'): out_folder = os.path.join(output_folder, 'png{}'.format(suffix)) in_file = '{}{}.hdf'.format(file_name, suffix) tu.mkdir_p(out_folder) cmd = '{} {} {} --outmode=uint16 --unstacking'.format( self.e2proc2d_exec, in_file, os.path.join(out_folder, '{}.png'.format(os.path.basename(in_file))), ) print('Execute:', cmd) try: subprocess.call(cmd.split()) except Exception as e: print(e) self.add_combo_item([os.path.basename(output_folder)]) cur_text = self.combo_text.blockSignals(True) self.combo_text.setCurrentText(os.path.basename(output_folder)) self.combo_text.blockSignals(cur_text) self.combo_text.currentTextChanged.emit( os.path.basename(output_folder)) @pyqtSlot() def confirm(self): self.sig_new_config.emit(self.model_out, self.threshold)
class backgroundDlg(ArtisanResizeablDialog): def __init__(self, parent = None, aw = None, activeTab = 0): super(backgroundDlg,self).__init__(parent, aw) self.setWindowTitle(QApplication.translate("Form Caption","Profile Background", None)) self.setModal(True) settings = QSettings() if settings.contains("BackgroundGeometry"): self.restoreGeometry(settings.value("BackgroundGeometry")) #TAB 1 self.pathedit = QLineEdit(self.aw.qmc.backgroundpath) self.pathedit.setStyleSheet("background-color:'lightgrey';") self.pathedit.setReadOnly(True) self.pathedit.setFocusPolicy(Qt.NoFocus) self.filename = "" self.backgroundCheck = QCheckBox(QApplication.translate("CheckBox","Show", None)) self.backgroundDetails = QCheckBox(QApplication.translate("CheckBox","Annotations", None)) self.backgroundeventsflag = QCheckBox(QApplication.translate("CheckBox","Events", None)) self.backgroundDeltaETflag = QCheckBox() backgroundDeltaETflagLabel = QLabel(deltaLabelPrefix + QApplication.translate("Label","ET", None)) self.backgroundDeltaBTflag = QCheckBox() backgroundDeltaBTflagLabel = QLabel(deltaLabelPrefix + QApplication.translate("Label","BT", None)) self.backgroundETflag = QCheckBox(QApplication.translate("CheckBox","ET", None)) self.backgroundBTflag = QCheckBox(QApplication.translate("CheckBox","BT", None)) self.backgroundFullflag = QCheckBox(QApplication.translate("CheckBox","Show Full", None)) self.backgroundCheck.setChecked(self.aw.qmc.background) self.backgroundDetails.setChecked(self.aw.qmc.backgroundDetails) self.backgroundeventsflag.setChecked(self.aw.qmc.backgroundeventsflag) self.backgroundDeltaETflag.setChecked(self.aw.qmc.DeltaETBflag) self.backgroundDeltaBTflag.setChecked(self.aw.qmc.DeltaBTBflag) self.backgroundETflag.setChecked(self.aw.qmc.backgroundETcurve) self.backgroundBTflag.setChecked(self.aw.qmc.backgroundBTcurve) self.backgroundFullflag.setChecked(self.aw.qmc.backgroundShowFullflag) loadButton = QPushButton(QApplication.translate("Button","Load", None)) loadButton.setFocusPolicy(Qt.NoFocus) delButton = QPushButton(QApplication.translate("Button","Delete", None)) delButton.setFocusPolicy(Qt.NoFocus) # connect the ArtisanDialog standard OK/Cancel buttons self.dialogbuttons.accepted.connect(self.accept) self.dialogbuttons.removeButton(self.dialogbuttons.button(QDialogButtonBox.Cancel)) alignButton = QPushButton(QApplication.translate("Button","Align", None)) alignButton.setFocusPolicy(Qt.NoFocus) self.alignComboBox = QComboBox() alignnames = [ QApplication.translate("Label","CHARGE", None), QApplication.translate("Label","DRY", None), QApplication.translate("Label","FCs", None), QApplication.translate("Label","FCe", None), QApplication.translate("Label","SCs", None), QApplication.translate("Label","SCe", None), QApplication.translate("Label","DROP", None), QApplication.translate("Label","ALL", None), ] self.alignComboBox.addItems(alignnames) self.alignComboBox.setCurrentIndex(self.aw.qmc.alignEvent) self.alignComboBox.currentIndexChanged.connect(self.changeAlignEventidx) loadButton.clicked.connect(self.load) alignButton.clicked.connect(self.timealign) self.speedSpinBox = QSpinBox() self.speedSpinBox.setAlignment(Qt.AlignRight) self.speedSpinBox.setRange(1,90) self.speedSpinBox.setSingleStep(5) self.speedSpinBox.setValue(self.aw.qmc.backgroundmovespeed) curvenames = [""] # first entry is the empty one, no extra curve displayed for i in range(min(len(self.aw.qmc.extraname1B),len(self.aw.qmc.extraname2B),len(self.aw.qmc.extratimexB))): curvenames.append("B" + str(2*i+3) + ": " + self.aw.qmc.extraname1B[i]) curvenames.append("B" + str(2*i+4) + ": " + self.aw.qmc.extraname2B[i]) self.xtcurvelabel = QLabel(QApplication.translate("Label", "Extra 1",None)) self.xtcurveComboBox = QComboBox() self.xtcurveComboBox.setToolTip(QApplication.translate("Tooltip","For loaded backgrounds with extra devices only",None)) self.xtcurveComboBox.setMinimumWidth(120) self.xtcurveComboBox.addItems(curvenames) if self.aw.qmc.xtcurveidx < len(curvenames): self.xtcurveComboBox.setCurrentIndex(self.aw.qmc.xtcurveidx) self.xtcurveComboBox.currentIndexChanged.connect(self.changeXTcurveidx) self.ytcurvelabel = QLabel(QApplication.translate("Label", "Extra 2",None)) self.ytcurveComboBox = QComboBox() self.ytcurveComboBox.setToolTip(QApplication.translate("Tooltip","For loaded backgrounds with extra devices only",None)) self.ytcurveComboBox.setMinimumWidth(120) self.ytcurveComboBox.addItems(curvenames) if self.aw.qmc.ytcurveidx < len(curvenames): self.ytcurveComboBox.setCurrentIndex(self.aw.qmc.ytcurveidx) self.ytcurveComboBox.currentIndexChanged.connect(self.changeYTcurveidx) self.upButton = QPushButton(QApplication.translate("Button","Up",None)) self.upButton.setFocusPolicy(Qt.NoFocus) self.downButton = QPushButton(QApplication.translate("Button","Down",None)) self.downButton.setFocusPolicy(Qt.NoFocus) self.leftButton = QPushButton(QApplication.translate("Button","Left",None)) self.leftButton.setFocusPolicy(Qt.NoFocus) self.rightButton = QPushButton(QApplication.translate("Button","Right",None)) self.rightButton.setFocusPolicy(Qt.NoFocus) self.backgroundCheck.clicked.connect(self.readChecks) self.backgroundDetails.clicked.connect(self.readChecks) self.backgroundeventsflag.clicked.connect(self.readChecks) self.backgroundDeltaETflag.clicked.connect(self.readChecks) self.backgroundDeltaBTflag.clicked.connect(self.readChecks) self.backgroundETflag.clicked.connect(self.readChecks) self.backgroundBTflag.clicked.connect(self.readChecks) self.backgroundFullflag.clicked.connect(self.readChecks) delButton.clicked.connect(self.delete) self.upButton.clicked.connect(self.moveUp) self.downButton.clicked.connect(self.moveDown) self.leftButton.clicked.connect(self.moveLeft) self.rightButton.clicked.connect(self.moveRight) #TAB 2 EVENTS #table for showing events self.eventtable = QTableWidget() self.eventtable.setTabKeyNavigation(True) self.createEventTable() self.copyeventTableButton = QPushButton(QApplication.translate("Button", "Copy Table",None)) self.copyeventTableButton.setToolTip(QApplication.translate("Tooltip","Copy table to clipboard, OPTION or ALT click for tabular text",None)) self.copyeventTableButton.setFocusPolicy(Qt.NoFocus) self.copyeventTableButton.setMaximumSize(self.copyeventTableButton.sizeHint()) self.copyeventTableButton.setMinimumSize(self.copyeventTableButton.minimumSizeHint()) self.copyeventTableButton.clicked.connect(self.copyEventTabletoClipboard) #TAB 3 DATA #table for showing data self.datatable = QTableWidget() self.datatable.setTabKeyNavigation(True) self.createDataTable() self.copydataTableButton = QPushButton(QApplication.translate("Button", "Copy Table",None)) self.copydataTableButton.setToolTip(QApplication.translate("Tooltip","Copy table to clipboard, OPTION or ALT click for tabular text",None)) self.copydataTableButton.setFocusPolicy(Qt.NoFocus) self.copydataTableButton.setMaximumSize(self.copydataTableButton.sizeHint()) self.copydataTableButton.setMinimumSize(self.copydataTableButton.minimumSizeHint()) self.copydataTableButton.clicked.connect(self.copyDataTabletoClipboard) #TAB 4 self.replayComboBox = QComboBox() replayVariants = [ QApplication.translate("Label","by time", None), QApplication.translate("Label","by BT", None), QApplication.translate("Label","by ET", None), ] self.replayComboBox.addItems(replayVariants) self.replayComboBox.setCurrentIndex(self.aw.qmc.replayType) self.replayComboBox.currentIndexChanged.connect(self.changeReplayTypeidx) self.backgroundReproduce = QCheckBox(QApplication.translate("CheckBox","Playback Aid",None)) self.backgroundReproduce.setChecked(self.aw.qmc.backgroundReproduce) self.backgroundReproduce.setFocusPolicy(Qt.NoFocus) self.backgroundReproduce.stateChanged.connect(self.setreproduce) self.backgroundReproduceBeep = QCheckBox(QApplication.translate("CheckBox","Beep",None)) self.backgroundReproduceBeep.setChecked(self.aw.qmc.backgroundReproduce) self.backgroundReproduceBeep.setFocusPolicy(Qt.NoFocus) self.backgroundReproduceBeep.stateChanged.connect(self.setreproduceBeep) self.backgroundPlaybackEvents = QCheckBox(QApplication.translate("CheckBox","Playback Events",None)) self.backgroundPlaybackEvents.setChecked(self.aw.qmc.backgroundPlaybackEvents) self.backgroundPlaybackEvents.setFocusPolicy(Qt.NoFocus) self.backgroundPlaybackEvents.stateChanged.connect(self.setplaybackevent) self.backgroundPlaybackDROP = QCheckBox(QApplication.translate("CheckBox","Playback DROP",None)) self.backgroundPlaybackDROP.setChecked(self.aw.qmc.backgroundPlaybackDROP) self.backgroundPlaybackDROP.setFocusPolicy(Qt.NoFocus) self.backgroundPlaybackDROP.stateChanged.connect(self.setplaybackdrop) etimelabel =QLabel(QApplication.translate("Label", "Text Warning",None)) etimeunit =QLabel(QApplication.translate("Label", "sec",None)) self.etimeSpinBox = QSpinBox() self.etimeSpinBox.setRange(1,60) self.etimeSpinBox.setValue(self.aw.qmc.detectBackgroundEventTime) self.etimeSpinBox.valueChanged.connect(self.setreproduce) #LAYOUT MANAGERS movelayout = QGridLayout() movelayout.addWidget(self.upButton,0,1) movelayout.addWidget(self.leftButton,1,0) movelayout.addWidget(self.speedSpinBox,1,1) movelayout.addWidget(self.rightButton,1,2) movelayout.addWidget(self.downButton,2,1) movelayout.setSpacing(20) checkslayout1 = QHBoxLayout() checkslayout1.addStretch() checkslayout1.addWidget(self.backgroundCheck) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDetails) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundeventsflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundETflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundBTflag) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDeltaETflag) checkslayout1.addSpacing(3) checkslayout1.addWidget(backgroundDeltaETflagLabel) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundDeltaBTflag) checkslayout1.addSpacing(3) checkslayout1.addWidget(backgroundDeltaBTflagLabel) checkslayout1.addSpacing(5) checkslayout1.addWidget(self.backgroundFullflag) checkslayout1.addStretch() layout = QGridLayout() layoutBoxedH = QHBoxLayout() layoutBoxedH.addStretch() layoutBoxedH.addLayout(movelayout) layoutBoxedH.addLayout(layout) layoutBoxedH.addStretch() layoutBoxed = QVBoxLayout() layoutBoxed.addStretch() layoutBoxed.addLayout(checkslayout1) layoutBoxed.addStretch() layoutBoxed.addLayout(layoutBoxedH) layoutBoxed.addStretch() alignButtonBoxed = QHBoxLayout() alignButtonBoxed.addWidget(self.xtcurvelabel) alignButtonBoxed.addWidget(self.xtcurveComboBox) alignButtonBoxed.addSpacing(10) alignButtonBoxed.addWidget(self.ytcurvelabel) alignButtonBoxed.addWidget(self.ytcurveComboBox) alignButtonBoxed.addStretch() alignButtonBoxed.addWidget(alignButton) alignButtonBoxed.addWidget(self.alignComboBox) tab4content = QHBoxLayout() tab4content.addWidget(self.backgroundReproduce) tab4content.addSpacing(10) tab4content.addWidget(self.backgroundReproduceBeep) tab4content.addSpacing(10) tab4content.addWidget(etimelabel) tab4content.addWidget(self.etimeSpinBox) tab4content.addWidget(etimeunit) tab4content.addSpacing(20) tab4content.addStretch() tab4content.addWidget(self.backgroundPlaybackEvents) tab4content.addSpacing(10) tab4content.addWidget(self.backgroundPlaybackDROP) tab4content.addSpacing(10) tab4content.addWidget(self.replayComboBox) tab1layout = QVBoxLayout() tab1layout.addLayout(layoutBoxed) # tab1layout.addStretch() tab1layout.addLayout(alignButtonBoxed) tab1layout.addLayout(tab4content) tab1layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom eventbuttonLayout = QHBoxLayout() eventbuttonLayout.addWidget(self.copyeventTableButton) eventbuttonLayout.addStretch() tab2layout = QVBoxLayout() tab2layout.addWidget(self.eventtable) tab2layout.addLayout(eventbuttonLayout) tab2layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom databuttonLayout = QHBoxLayout() databuttonLayout.addWidget(self.copydataTableButton) databuttonLayout.addStretch() tab3layout = QVBoxLayout() tab3layout.addWidget(self.datatable) tab3layout.addLayout(databuttonLayout) tab3layout.setContentsMargins(5, 0, 5, 0) # left, top, right, bottom #tab layout tab1layout.setSpacing(5) self.TabWidget = QTabWidget() C1Widget = QWidget() C1Widget.setLayout(tab1layout) self.TabWidget.addTab(C1Widget,QApplication.translate("Tab","Config",None)) C2Widget = QWidget() C2Widget.setLayout(tab2layout) self.TabWidget.addTab(C2Widget,QApplication.translate("Tab","Events",None)) C3Widget = QWidget() C3Widget.setLayout(tab3layout) self.TabWidget.addTab(C3Widget,QApplication.translate("Tab","Data",None)) buttonLayout = QHBoxLayout() buttonLayout.addWidget(loadButton) buttonLayout.addWidget(delButton) buttonLayout.addStretch() buttonLayout.addWidget(self.dialogbuttons) mainLayout = QVBoxLayout() mainLayout.addWidget(self.TabWidget) mainLayout.addWidget(self.pathedit) mainLayout.addLayout(buttonLayout) mainLayout.setContentsMargins(5, 10, 5, 5) # left, top, right, bottom self.setLayout(mainLayout) if platform.system() == 'Windows': self.dialogbuttons.button(QDialogButtonBox.Ok) else: self.dialogbuttons.button(QDialogButtonBox.Ok).setFocus() self.TabWidget.setCurrentIndex(activeTab) @pyqtSlot(bool) def timealign(self,_): self.aw.qmc.timealign() #keyboard presses. There must not be widgets (pushbuttons, comboboxes, etc) in focus in order to work def keyPressEvent(self,event): if event.matches(QKeySequence.Copy): if self.TabWidget.currentIndex() == 2: # datatable self.aw.copy_cells_to_clipboard(self.datatable) self.aw.sendmessage(QApplication.translate("Message","Data table copied to clipboard",None)) else: super(backgroundDlg,self).keyPressEvent(event) @pyqtSlot() def accept(self): self.aw.qmc.backgroundmovespeed = self.speedSpinBox.value() self.close() def closeEvent(self,_): settings = QSettings() #save window geometry settings.setValue("BackgroundGeometry",self.saveGeometry()) self.aw.backgroundDlg_activeTab = self.TabWidget.currentIndex() def getColorIdx(self,c): try: return self.defaultcolorsmapped.index(c) except Exception: try: return self.colors.index(c) + 5 except Exception: return 0 @pyqtSlot(int) def setplaybackevent(self,_): s = None if self.backgroundPlaybackEvents.isChecked(): self.aw.qmc.backgroundPlaybackEvents = True msg = QApplication.translate("Message","Playback Events set ON",None) else: self.aw.qmc.backgroundPlaybackEvents = False msg = QApplication.translate("StatusBar","Playback Events set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) @pyqtSlot(int) def setplaybackdrop(self,_): s = None if self.backgroundPlaybackDROP.isChecked(): self.aw.qmc.backgroundPlaybackDROP = True msg = QApplication.translate("Message","Playback DROP set ON",None) else: self.aw.qmc.backgroundPlaybackDROP = False msg = QApplication.translate("StatusBar","Playback DROP set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) @pyqtSlot(int) def setreproduceBeep(self,_): if self.backgroundReproduceBeep.isChecked(): self.aw.qmc.backgroundReproduceBeep = True else: self.aw.qmc.backgroundReproduceBeep = False @pyqtSlot(int) def setreproduce(self,_): self.aw.qmc.detectBackgroundEventTime = self.etimeSpinBox.value() s = None if self.backgroundReproduce.isChecked(): self.aw.qmc.backgroundReproduce = True msg = QApplication.translate("Message","Playback Aid set ON at {0} secs",None).format(str(self.aw.qmc.detectBackgroundEventTime)) else: self.aw.qmc.backgroundReproduce = False msg = QApplication.translate("StatusBar","Playback Aid set OFF",None) s = "background-color:'transparent';" self.aw.sendmessage(msg, style=s) def adjustcolor(self,curve): curve = str(curve).lower() etcolor = str(self.metcolorComboBox.currentText()).lower() btcolor = str(self.btcolorComboBox.currentText()).lower() deltabtcolor = str(self.deltabtcolorComboBox.currentText()).lower() deltaetcolor = str(self.deltaetcolorComboBox.currentText()).lower() xtcolor = str(self.xtcolorComboBox.currentText()).lower() defaults = ["et","bt","deltaet","deltabt"] if curve == "et": if etcolor in defaults: self.aw.qmc.backgroundmetcolor = self.aw.qmc.palette[etcolor] else: self.aw.qmc.backgroundmetcolor = etcolor elif curve == "bt": if btcolor in defaults: self.aw.qmc.backgroundbtcolor = self.aw.qmc.palette[btcolor] else: self.aw.qmc.backgroundbtcolor = btcolor elif curve == "deltaet": if deltaetcolor in defaults: self.aw.qmc.backgrounddeltaetcolor = self.aw.qmc.palette[deltaetcolor] else: self.aw.qmc.backgrounddeltaetcolor = deltaetcolor elif curve == "deltabt": if deltabtcolor in defaults: self.aw.qmc.backgrounddeltabtcolor = self.aw.qmc.palette[deltabtcolor] else: self.aw.qmc.backgrounddeltabtcolor = deltabtcolor elif curve == "xt": if xtcolor in defaults: self.aw.qmc.backgroundxtcolor = self.aw.qmc.palette[xtcolor] else: self.aw.qmc.backgroundxtcolor = xtcolor self.aw.qmc.redraw(recomputeAllDeltas=False) @pyqtSlot(bool) def delete(self,_): self.pathedit.setText("") # we should not overwrite the users app settings here, right: # but we have to deactivate the show flag self.backgroundCheck.setChecked(False) self.aw.qmc.background = False self.aw.qmc.backgroundprofile = None self.xtcurveComboBox.blockSignals(True) self.xtcurveComboBox.clear() self.aw.deleteBackground() self.eventtable.clear() self.createEventTable() self.createDataTable() self.aw.qmc.resetlinecountcaches() self.xtcurveComboBox.blockSignals(False) self.aw.qmc.redraw(recomputeAllDeltas=False) @pyqtSlot(bool) def moveUp(self,_): self.upButton.setDisabled(True) self.move("up") self.upButton.setDisabled(False) @pyqtSlot(bool) def moveDown(self,_): self.downButton.setDisabled(True) self.move("down") self.downButton.setDisabled(False) @pyqtSlot(bool) def moveLeft(self,_): self.leftButton.setDisabled(True) self.move("left") self.leftButton.setDisabled(False) @pyqtSlot(bool) def moveRight(self,_): self.rightButton.setDisabled(True) self.move("right") self.rightButton.setDisabled(False) def move(self,m): step = self.speedSpinBox.value() self.aw.qmc.movebackground(m,step) self.createEventTable() self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False) def readChecks(self): self.aw.qmc.background = bool(self.backgroundCheck.isChecked()) self.aw.qmc.backgroundDetails = bool(self.backgroundDetails.isChecked()) self.aw.qmc.backgroundeventsflag = bool(self.backgroundeventsflag.isChecked()) self.aw.qmc.DeltaETBflag = bool(self.backgroundDeltaETflag.isChecked()) self.aw.qmc.DeltaBTBflag = bool(self.backgroundDeltaBTflag.isChecked()) self.aw.qmc.backgroundETcurve = bool(self.backgroundETflag.isChecked()) self.aw.qmc.backgroundBTcurve = bool(self.backgroundBTflag.isChecked()) self.aw.qmc.backgroundShowFullflag = bool(self.backgroundFullflag.isChecked()) self.aw.qmc.redraw(recomputeAllDeltas=True) @pyqtSlot(int) def changeAlignEventidx(self,i): self.aw.qmc.alignEvent = i @pyqtSlot(int) def changeReplayTypeidx(self,i): self.aw.qmc.replayType = i @pyqtSlot(int) def changeXTcurveidx(self,i): self.aw.qmc.xtcurveidx = i self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False,smooth=True) @pyqtSlot(int) def changeYTcurveidx(self,i): self.aw.qmc.ytcurveidx = i self.createDataTable() self.aw.qmc.redraw(recomputeAllDeltas=False,smooth=True) @pyqtSlot(bool) def load(self,_): self.filename = self.aw.ArtisanOpenFileDialog(msg=QApplication.translate("Message","Load Background",None),ext_alt=".alog") if len(self.filename) == 0: return self.aw.sendmessage(QApplication.translate("Message","Reading background profile...",None)) self.aw.qmc.resetlinecountcaches() self.aw.loadbackground(self.filename) # reset XT curve popup curvenames = [""] # first entry is the empty one (no extra curve displayed) for i in range(min(len(self.aw.qmc.extraname1B),len(self.aw.qmc.extraname2B),len(self.aw.qmc.extratimexB))): curvenames.append("B" + str(2*i+3) + ": " + self.aw.qmc.extraname1B[i]) curvenames.append("B" + str(2*i+4) + ": " + self.aw.qmc.extraname2B[i]) self.xtcurveComboBox.blockSignals(True) self.xtcurveComboBox.clear() self.xtcurveComboBox.addItems(curvenames) if self.aw.qmc.xtcurveidx < len(curvenames): self.xtcurveComboBox.setCurrentIndex(self.aw.qmc.xtcurveidx) self.xtcurveComboBox.blockSignals(False) self.ytcurveComboBox.blockSignals(True) self.ytcurveComboBox.clear() self.ytcurveComboBox.addItems(curvenames) if self.aw.qmc.ytcurveidx < len(curvenames): self.ytcurveComboBox.setCurrentIndex(self.aw.qmc.ytcurveidx) self.ytcurveComboBox.blockSignals(False) self.pathedit.setText(self.filename) self.backgroundCheck.setChecked(True) self.aw.qmc.timealign(redraw=False) self.readChecks() self.createEventTable() self.createDataTable() def createEventTable(self): ndata = len(self.aw.qmc.backgroundEvents) # self.eventtable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.eventtable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.eventtable.clearSelection() # this seems to work also for Ubuntu 16.04 self.eventtable.setRowCount(ndata) self.eventtable.setColumnCount(6) self.eventtable.setHorizontalHeaderLabels([QApplication.translate("Table","Time",None), QApplication.translate("Table", "ET", None), QApplication.translate("Table", "BT", None), QApplication.translate("Table","Description",None), QApplication.translate("Table","Type",None), QApplication.translate("Table","Value",None)]) self.eventtable.setAlternatingRowColors(True) self.eventtable.setEditTriggers(QTableWidget.NoEditTriggers) self.eventtable.setSelectionBehavior(QTableWidget.SelectRows) self.eventtable.setSelectionMode(QTableWidget.ExtendedSelection) self.eventtable.setShowGrid(True) self.eventtable.verticalHeader().setSectionResizeMode(2) if self.aw.qmc.timeindex[0] != -1: start = self.aw.qmc.timex[self.aw.qmc.timeindex[0]] else: start = 0 for i in range(ndata): timez = QTableWidgetItem(stringfromseconds(self.aw.qmc.timeB[self.aw.qmc.backgroundEvents[i]]-start)) timez.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) if self.aw.qmc.LCDdecimalplaces: fmtstr = "%.1f" else: fmtstr = "%.0f" etline = QTableWidgetItem(fmtstr%(self.aw.qmc.temp1B[self.aw.qmc.backgroundEvents[i]]) + self.aw.qmc.mode) etline.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) btline = QTableWidgetItem(fmtstr%(self.aw.qmc.temp2B[self.aw.qmc.backgroundEvents[i]]) + self.aw.qmc.mode) btline.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) description = QTableWidgetItem(self.aw.qmc.backgroundEStrings[i]) etype = QTableWidgetItem(self.aw.qmc.Betypesf(self.aw.qmc.backgroundEtypes[i])) evalue = QTableWidgetItem(self.aw.qmc.eventsvalues(self.aw.qmc.backgroundEvalues[i])) evalue.setTextAlignment(Qt.AlignRight + Qt.AlignVCenter) #add widgets to the table self.eventtable.setItem(i,0,timez) self.eventtable.setItem(i,1,etline) self.eventtable.setItem(i,2,btline) self.eventtable.setItem(i,3,description) self.eventtable.setItem(i,4,etype) self.eventtable.setItem(i,5,evalue) # improve width of Time column self.eventtable.setColumnWidth(1,175) header = self.eventtable.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(1, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.Fixed) header.setSectionResizeMode(3, QHeaderView.Stretch) header.setSectionResizeMode(4, QHeaderView.Fixed) header.setSectionResizeMode(5, QHeaderView.Fixed) self.eventtable.resizeColumnsToContents() self.eventtable.setColumnWidth(1,65) self.eventtable.setColumnWidth(2,65) def createDataTable(self): try: #### lock shared resources ##### self.aw.qmc.samplingsemaphore.acquire(1) ndata = len(self.aw.qmc.timeB) self.datatable.clear() # this crashes Ubuntu 16.04 # if ndata != 0: # self.datatable.clearContents() # this crashes Ubuntu 16.04 if device table is empty and also sometimes else self.datatable.clearSelection() # this seems to work also for Ubuntu 16.04 if self.aw.qmc.timeindexB[0] != -1 and len(self.aw.qmc.timeB) > self.aw.qmc.timeindexB[0]: start = self.aw.qmc.timeB[self.aw.qmc.timeindexB[0]] else: start = 0 self.datatable.setRowCount(ndata) headers = [QApplication.translate("Table","Time",None), QApplication.translate("Table","ET",None), QApplication.translate("Table","BT",None), deltaLabelUTF8 + QApplication.translate("Table","ET",None), deltaLabelUTF8 + QApplication.translate("Table","BT",None)] xtcurve = False # no XT curve if self.aw.qmc.xtcurveidx > 0: # 3rd background curve set? idx3 = self.aw.qmc.xtcurveidx - 1 n3 = idx3 // 2 if len(self.aw.qmc.temp1BX) > n3 and len(self.aw.qmc.extratimexB) > n3: xtcurve = True if self.aw.qmc.xtcurveidx % 2: headers.append(self.aw.qmc.extraname1B[n3]) else: headers.append(self.aw.qmc.extraname2B[n3]) ytcurve = False # no YT curve if self.aw.qmc.ytcurveidx > 0: # 4th background curve set? idx4 = self.aw.qmc.ytcurveidx - 1 n4 = idx4 // 2 if len(self.aw.qmc.temp1BX) > n4 and len(self.aw.qmc.extratimexB) > n4: ytcurve = True if self.aw.qmc.ytcurveidx % 2: headers.append(self.aw.qmc.extraname1B[n4]) else: headers.append(self.aw.qmc.extraname2B[n4]) headers.append("") # dummy column that stretches self.datatable.setColumnCount(len(headers)) self.datatable.setHorizontalHeaderLabels(headers) self.datatable.setAlternatingRowColors(True) self.datatable.setEditTriggers(QTableWidget.NoEditTriggers) self.datatable.setSelectionBehavior(QTableWidget.SelectRows) self.datatable.setSelectionMode(QTableWidget.ExtendedSelection) # QTableWidget.SingleSelection, ContiguousSelection, MultiSelection self.datatable.setShowGrid(True) self.datatable.verticalHeader().setSectionResizeMode(2) for i in range(ndata): Rtime = QTableWidgetItem(stringfromseconds(self.aw.qmc.timeB[i]-start)) Rtime.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if self.aw.qmc.LCDdecimalplaces: fmtstr = "%.1f" else: fmtstr = "%.0f" ET = QTableWidgetItem(fmtstr%self.aw.qmc.temp1B[i]) BT = QTableWidgetItem(fmtstr%self.aw.qmc.temp2B[i]) ET.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) BT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if i: d = (self.aw.qmc.timeB[i]-self.aw.qmc.timeB[i-1]) if d == 0: dET = 0. dBT = 0. else: dET = (60*(self.aw.qmc.temp1B[i]-self.aw.qmc.temp1B[i-1])/d) dBT = (60*(self.aw.qmc.temp2B[i]-self.aw.qmc.temp2B[i-1])/d) deltaET = QTableWidgetItem("%.1f"%dET) deltaBT = QTableWidgetItem("%.1f"%dBT) else: deltaET = QTableWidgetItem("--") deltaBT = QTableWidgetItem("--") deltaET.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) deltaBT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) self.datatable.setItem(i,0,Rtime) if i: #identify by color and add notation if i == self.aw.qmc.timeindexB[0] != -1: self.datatable.item(i,0).setBackground(QColor('#f07800')) text = QApplication.translate("Table", "CHARGE",None) elif i == self.aw.qmc.timeindexB[1]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "DRY END",None) elif i == self.aw.qmc.timeindexB[2]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "FC START",None) elif i == self.aw.qmc.timeindexB[3]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "FC END",None) elif i == self.aw.qmc.timeindexB[4]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "SC START",None) elif i == self.aw.qmc.timeindexB[5]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "SC END",None) elif i == self.aw.qmc.timeindexB[6]: self.datatable.item(i,0).setBackground(QColor('#f07800')) text = QApplication.translate("Table", "DROP",None) elif i == self.aw.qmc.timeindexB[7]: self.datatable.item(i,0).setBackground(QColor('orange')) text = QApplication.translate("Table", "COOL",None) elif i in self.aw.qmc.backgroundEvents: self.datatable.item(i,0).setBackground(QColor('yellow')) index = self.aw.qmc.backgroundEvents.index(i) text = QApplication.translate("Table", "#{0} {1}{2}",None).format(str(index+1),self.aw.qmc.Betypesf(self.aw.qmc.backgroundEtypes[index])[0],self.aw.qmc.eventsvalues(self.aw.qmc.backgroundEvalues[index])) else: text = "" Rtime.setText(text + " " + Rtime.text()) self.datatable.setItem(i,1,ET) self.datatable.setItem(i,2,BT) self.datatable.setItem(i,3,deltaET) self.datatable.setItem(i,4,deltaBT) if xtcurve and len(self.aw.qmc.temp1BX[n3]) > i: # an XT column is availble, fill it with data if self.aw.qmc.xtcurveidx % 2: XT = QTableWidgetItem("%.0f"%self.aw.qmc.temp1BX[n3][i]) else: XT = QTableWidgetItem("%.0f"%self.aw.qmc.temp2BX[n3][i]) XT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) self.datatable.setItem(i,5,XT) if ytcurve and len(self.aw.qmc.temp1BX[n4]) > i: # an YT column is availble, fill it with data if self.aw.qmc.ytcurveidx % 2: YT = QTableWidgetItem("%.0f"%self.aw.qmc.temp1BX[n4][i]) else: YT = QTableWidgetItem("%.0f"%self.aw.qmc.temp2BX[n4][i]) YT.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) if xtcurve: self.datatable.setItem(i,6,YT) else: self.datatable.setItem(i,5,YT) header = self.datatable.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(1, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.Fixed) header.setSectionResizeMode(3, QHeaderView.Fixed) header.setSectionResizeMode(4, QHeaderView.Fixed) if (xtcurve and not ytcurve) or (ytcurve and not xtcurve): header.setSectionResizeMode(5, QHeaderView.Fixed) header.setSectionResizeMode(6, QHeaderView.Stretch) elif xtcurve and ytcurve: header.setSectionResizeMode(5, QHeaderView.Fixed) header.setSectionResizeMode(6, QHeaderView.Fixed) header.setSectionResizeMode(7, QHeaderView.Stretch) else: header.setSectionResizeMode(5, QHeaderView.Stretch) self.datatable.resizeColumnsToContents() finally: if self.aw.qmc.samplingsemaphore.available() < 1: self.aw.qmc.samplingsemaphore.release(1) @pyqtSlot(bool) def copyDataTabletoClipboard(self,_=False): self.datatable.selectAll() self.aw.copy_cells_to_clipboard(self.datatable,adjustment=7) self.datatable.clearSelection() self.aw.sendmessage(QApplication.translate("Message","Data table copied to clipboard",None)) @pyqtSlot(bool) def copyEventTabletoClipboard(self,_=False): self.aw.copy_cells_to_clipboard(self.eventtable,adjustment=0) self.aw.sendmessage(QApplication.translate("Message","Event table copied to clipboard",None))
class ParamsByType(QWidget, MooseWidget): """ Has a QComboBox for the different allowed types. On switching type a new ParamsByGroup is shown. """ needBlockList = pyqtSignal(list) blockRenamed = pyqtSignal(object, str) changed = pyqtSignal() def __init__(self, block, type_block_map, **kwds): """ Constructor. Input: block[BlockInfo]: The block to show. """ super(ParamsByType, self).__init__(**kwds) self.block = block self.combo = QComboBox() self.types = [] self.type_params_map = {} self.type_block_map = type_block_map self.table_stack = QStackedWidget() self.type_table_map = {} for t in sorted(self.block.types.keys()): self.types.append(t) params_list = [] for p in self.block.parameters_list: params_list.append(self.block.parameters[p]) t_block = self.block.types[t] for p in t_block.parameters_list: params_list.append(t_block.parameters[p]) self.type_params_map[t] = params_list self.combo.addItems(sorted(self.block.types.keys())) self.combo.currentTextChanged.connect(self.setBlockType) self.top_layout = WidgetUtils.addLayout(vertical=True) self.top_layout.addWidget(self.combo) self.top_layout.addWidget(self.table_stack) self.setLayout(self.top_layout) self.user_params = [] self.setDefaultBlockType() self.setup() def _syncUserParams(self, current, to): """ Sync user added parameters that are on the main block into each type ParamsByGroup. Input: current[ParamsByGroup]: The current group parameter table to[ParamsByGroup]: The new group parameter table """ ct = current.findTable("Main") tot = to.findTable("Main") if not ct or not tot or ct == tot: return tot.removeUserParams() params = ct.getUserParams() tot.addUserParams(params) to.syncParamsFrom(current) # Make sure the name parameter stays the same idx = ct.findRow("Name") if idx >= 0: name = ct.item(idx, 1).text() idx = tot.findRow("Name") if idx >= 0: tot.item(idx, 1).setText(name) def currentType(self): return self.combo.currentText() def save(self): """ Look at the user params in self.block.parameters. update the type tables Save type on block """ t = self.getTable() if t: t.save() self.block.setBlockType(self.combo.currentText()) def reset(self): t = self.getTable() t.reset() def getOrCreateTypeTable(self, type_name): """ Gets the table for the type name or create it if it doesn't exist. Input: type_name[str]: Name of the type Return: ParamsByGroup: The parameters corresponding to the type """ t = self.type_table_map.get(type_name) if t: return t t = ParamsByGroup(self.block, self.type_params_map.get(type_name, self.block.orderedParameters()), self.type_block_map) t.needBlockList.connect(self.needBlockList) t.blockRenamed.connect(self.blockRenamed) t.changed.connect(self.changed) self.type_table_map[type_name] = t self.table_stack.addWidget(t) return t def setDefaultBlockType(self): param = self.block.getParamInfo("type") if param and param.value: self.setBlockType(param.value) elif self.block.types: self.setBlockType(sorted(self.block.types.keys())[0]) def setBlockType(self, type_name): if type_name not in self.block.types: return t = self.getOrCreateTypeTable(type_name) t.updateWatchers() self.combo.blockSignals(True) self.combo.setCurrentText(type_name) self.combo.blockSignals(False) t.updateType(type_name) current = self.table_stack.currentWidget() self._syncUserParams(current, t) self.table_stack.setCurrentWidget(t) self.changed.emit() def addUserParam(self, param): t = self.table_stack.currentWidget() t.addUserParam(param) def setWatchedBlockList(self, path, children): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.setWatchedBlockList(path, children) def updateWatchers(self): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.updateWatchers() def getTable(self): return self.table_stack.currentWidget() def paramValue(self, name): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) if t.paramValue(name): return t.paramValue(name)
class FrameCheckOption(QWidget): def __init__(self, parent: QObject, controller: TouchManagerController, model: TouchManagerModel): super(QWidget, self).__init__() self.parent = parent self.model = model self.controller = controller self.main_lay = QVBoxLayout() self.bottom_lay = QVBoxLayout() self.scroll_wid = QWidget() self.scrollable = QScrollArea() self.lay = QVBoxLayout() self.lbls = [] self.lblsColors = [] self.lblImageColors = [] self.rBtns = [] self.btnAddCoord = QPushButton() self.aroundLbl = QLabel() self.cBoxAround = QComboBox() self.initMainUI() self.initUI({ 'coordinates': [[0.5, 0.5]], 'values': [[255, 255, 255]], 'around': 5, 'currentScreenColors': [[255, 255, 255]] }) self.initConnectors() def initMainUI(self): self.setLayout(self.main_lay) self.aroundLbl.setText("Around factor:") self.cBoxAround.addItems(str(i) for i in range(self.model.MAX_AROUND)) self.cBoxAround.setFixedHeight(20) self.cBoxAround.setMaximumWidth(100) self.cBoxAround.currentIndexChanged.connect( self.controller.requestChangeAround) self.btnAddCoord.setText("add coordinate") self.btnAddCoord.clicked.connect( self.controller.requestFrameCheckCoordAdd) self.lay.setAlignment(Qt.AlignTop) self.scroll_wid.setLayout(self.lay) self.scrollable.setWidgetResizable(True) self.scrollable.setWidget(self.scroll_wid) self.scrollable.setContentsMargins(0, 0, 0, 0) self.main_lay.addWidget(self.scrollable) self.bottom_lay.addWidget(self.btnAddCoord) h_lay = QHBoxLayout() h_lay.addWidget(self.aroundLbl) h_lay.addWidget(self.cBoxAround) self.bottom_lay.addLayout(h_lay) self.main_lay.addLayout(self.bottom_lay) def _clearLayout(self): self.lbls.clear() self.lbls = [] self.rBtns.clear() self.rBtns = [] self.lblsColors.clear() self.lblsColors = [] self.lblImageColors.clear() self.lblImageColors = [] for i in reversed(range(self.lay.count())): row_lay = self.lay.takeAt(i) for j in reversed(range(row_lay.count())): row_lay.itemAt(j).widget().setParent(None) row_lay.setParent(None) def _setAroundSafe(self, around): self.cBoxAround.blockSignals(True) self.cBoxAround.setCurrentIndex(around) self.cBoxAround.blockSignals(False) def initUI(self, newData: dict): if 'around' in newData: self._setAroundSafe(newData['around']) coords_num = len(newData['coordinates']) l = len(newData['currentScreenColors']) w, h = self.controller.current_image_size for i in range(coords_num): lay_row = QHBoxLayout() coord = newData['coordinates'][i] x, y = int((coord[0] * w)), int((coord[1] * h)) lbl_point = QLabel("%15s" % ("C%d: %4d , %4d" % (i, x, y))) lbl_point.setFixedWidth(80) lay_row.addWidget(lbl_point) # lay_row.addWidget(QLabel("X: %4d" % (coord[0] * w))) # lay_row.addWidget(QLabel("Y: %4d" % (coord[1] * h))) colors = newData['values'][i] lblColor = QLabel("") lblColor.setStyleSheet("background-color: rgb({},{},{});".format( colors[0], colors[1], colors[2])) lblColor.mousePressEvent = (partial(self.onManualChoose, i)) lblColor.setToolTip("Target value RGB=(%d, %d, %d)" % (colors[0], colors[1], colors[2])) lblColor.setMaximumWidth(40) self.lblsColors.append(lblColor) btnSet = QPushButton("set->") btnSet.setMaximumWidth(45) btnSet.clicked.connect( partial( self.controller.requestSetCurrentColorToFrameCheckColor, i)) lblimgColor = QLabel("") lblimgColor.setMaximumWidth(20) color_ = newData['currentScreenColors'][i] lblimgColor.setStyleSheet( "background-color: rgb({},{},{});".format( color_[0], color_[1], color_[2])) lblimgColor.setToolTip("Current screenshot RGB=(%d, %d, %d)" % (color_[0], color_[1], color_[2])) lblimgColor.setToolTipDuration(20 * 1000) self.lblImageColors.append(lblimgColor) lay_row.addWidget(lblimgColor) lay_row.addWidget(btnSet) lay_row.addWidget(lblColor) rbtn = QRadioButton() self.rBtns.append(rbtn) rbtn.setText("") rbtn.setFixedWidth(20) rbtn.toggled.connect( partial(self.controller.onCoordinateSelected, i)) lay_row.addWidget(rbtn) self.lay.addLayout(lay_row) if len(self.rBtns) > 0: self.rBtns[self.controller.selectedCoordinateIndex].blockSignals( True) self.rBtns[self.controller.selectedCoordinateIndex].setChecked( True) self.rBtns[self.controller.selectedCoordinateIndex].blockSignals( False) def onManualChoose(self, i, event): self.controller.rquestFrameCheckCoordinateColorManualChange(i) def updateCurrentColors(self, colors_img): num = min(len(colors_img), len(self.lblImageColors)) for i in range(num): color = colors_img[i] self.lblImageColors[i].setStyleSheet( "background-color: rgb({},{},{});".format( color[0], color[1], color[2])) def initConnectors(self): self.controller.onCurrentScreenColorsChanged.connect( self.updateCurrentColors) return # for i, rbtn in enumerate(self.rBtns): # self.rbtn.toggled.connect(partial(self.controller.onCoordinateSelected, i)) def changeData(self, new_data): self._clearLayout() self.initUI(new_data) if 'around' in new_data: if self.cBoxAround.currentIndex != new_data['around']: self._setAroundSafe(new_data['around']) def deleteLater(self): self.controller.onCurrentScreenColorsChanged.disconnect( self.updateCurrentColors) super(FrameCheckOption, self).deleteLater()
class SnapshotCompareWidget(QWidget): pvs_filtered = QtCore.pyqtSignal(set) restore_requested = QtCore.pyqtSignal(list) rgx_icon = None def __init__(self, snapshot, common_settings, parent=None, **kw): super().__init__(parent, **kw) self.snapshot = snapshot self.common_settings = common_settings # ----------- PV Table ------------- # PV table consist of: # self.model: holding the data, values, being updated by PV callbacks, etc # self._proxy: a proxy model implementing the filter functionality # self.view: visual representation of the PV table self.view = SnapshotPvTableView(self) self.view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.view.restore_requested.connect(self._handle_restore_request) self.model = SnapshotPvTableModel(snapshot, self) self.model.file_parse_errors.connect(self._show_snapshot_parse_errors) self._proxy = SnapshotPvFilterProxyModel(self) self._proxy.setSourceModel(self.model) self._proxy.filtered.connect(self.pvs_filtered) # Build model and set default visualization on view (column widths, etc) self.model.set_pvs(snapshot.pvs.values()) self.view.setModel(self._proxy) # ---------- Filter control elements --------------- # - text input to filter by name # - drop down to filter by compare status # - check box to select if showing pvs with incomplete data if SnapshotCompareWidget.rgx_icon is None: SnapshotCompareWidget.rgx_icon = QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), "images/rgx.png")) # #### PV name filter pv_filter_label = QLabel("Filter:", self) pv_filter_label.setAlignment(Qt.AlignCenter | Qt.AlignRight) self.pv_filter_sel = QComboBox(self) self.pv_filter_sel.setEditable(True) self.pv_filter_sel.setIconSize(QtCore.QSize(35, 15)) self.pv_filter_inp = self.pv_filter_sel.lineEdit() self.pv_filter_inp.setPlaceholderText("Filter by PV name") policy = self.pv_filter_sel.sizePolicy() policy.setHorizontalPolicy(policy.Expanding) self.pv_filter_sel.setSizePolicy(policy) self.pv_filter_sel.currentIndexChanged.connect( self._predefined_filter_selected) self.pv_filter_inp.textChanged.connect(self._create_name_filter) self._populate_filter_list() # Prepare pallets to color the pv name filter input if rgx not valid self._inp_palette_ok = self.pv_filter_inp.palette() self._inp_palette_err = QPalette() self._inp_palette_err.setColor(QPalette.Base, QColor("#F39292")) # Create a PV name filter layout and add items pv_filter_layout = QHBoxLayout() pv_filter_layout.setSpacing(10) pv_filter_layout.addWidget(pv_filter_label) pv_filter_layout.addWidget(self.pv_filter_sel) # #### Regex selector self.regex = QCheckBox("Regex", self) self.regex.stateChanged.connect(self._handle_regex_change) # #### Selector for comparison filter self.compare_filter_inp = QComboBox(self) self.compare_filter_inp.addItems( ["Show all", "Different only", "Equal only"]) self.compare_filter_inp.currentIndexChanged.connect( self._proxy.set_eq_filter) self.compare_filter_inp.setMaximumWidth(200) # ### Show disconnected selector self.show_disconn_inp = QCheckBox("Show disconnected PVs.", self) self.show_disconn_inp.setChecked(True) self.show_disconn_inp.stateChanged.connect( self._proxy.set_disconn_filter) self.show_disconn_inp.setMaximumWidth(500) # Tolerance setting tol_label = QLabel("Tolerance:") tol = QSpinBox() tol.setRange(1, 1000000) tol.setValue(1) tol.valueChanged[int].connect(self.model.change_tolerance) self.model.change_tolerance(tol.value()) # ### Put all tolerance and filter selectors in one layout filter_layout = QHBoxLayout() filter_layout.addWidget(tol_label) filter_layout.addWidget(tol) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addLayout(pv_filter_layout) filter_layout.addWidget(self.regex) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addWidget(self.compare_filter_inp) filter_layout.addWidget(self.show_disconn_inp) filter_layout.setAlignment(Qt.AlignLeft) filter_layout.setSpacing(10) # ------- Build main layout --------- layout = QVBoxLayout(self) layout.setContentsMargins(10, 10, 10, 10) layout.addLayout(filter_layout) layout.addWidget(self.view) self.setLayout(layout) def _populate_filter_list(self): predefined_filters = self.common_settings['predefined_filters'] self.pv_filter_sel.blockSignals(True) self.pv_filter_sel.clear() self.pv_filter_sel.addItem(None) for rgx in predefined_filters.get('rgx-filters', list()): self.pv_filter_sel.addItem(SnapshotCompareWidget.rgx_icon, rgx) self.pv_filter_sel.addItems(predefined_filters.get('filters', list())) self.pv_filter_sel.blockSignals(False) def _handle_regex_change(self, state): txt = self.pv_filter_inp.text() if state and txt.strip() == '': self.pv_filter_inp.setText('.*') elif not state and txt.strip() == '.*': self.pv_filter_inp.setText('') else: self._create_name_filter(txt) def _create_name_filter(self, txt): if self.regex.isChecked(): try: srch_filter = re.compile(txt) self.pv_filter_inp.setPalette(self._inp_palette_ok) except: # Syntax error (happens a lot during typing an expression). In such cases make compiler which will # not match any pv name srch_filter = re.compile("") self.pv_filter_inp.setPalette(self._inp_palette_err) else: srch_filter = txt self.pv_filter_inp.setPalette(self._inp_palette_ok) self._proxy.set_name_filter(srch_filter) def _show_snapshot_parse_errors(self, errors): show_snapshot_parse_errors(self, errors) def new_selected_files(self, selected_files): self.model.clear_snap_files() self.model.add_snap_files(selected_files) self._proxy.apply_filter() def clear_snap_files(self): self.model.clear_snap_files() def handle_new_snapshot_instance(self, snapshot): self.snapshot = snapshot self.model.snapshot = snapshot self.model.set_pvs(snapshot.pvs.values()) self.view.sortByColumn(0, Qt.AscendingOrder) # default sorting self._populate_filter_list() def _handle_restore_request(self, pvs_list): self.restore_requested.emit(pvs_list) def _predefined_filter_selected(self, idx): txt = self.pv_filter_inp.text() if idx == 0: # First empty option; the menu is always reset to this. return if not self.pv_filter_sel.itemIcon(idx).isNull(): # Set back to first index, to get rid of the icon. Set to regex and # pass text of filter to the input self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(True) self.pv_filter_inp.setText(txt) else: # Imitate same behaviour self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(False) self.pv_filter_inp.setText(txt) def filter_update(self): self._proxy.apply_filter()
class tb_optical_model(QWidget): def __init__(self): QWidget.__init__(self) self.dump_dir=os.path.join(get_sim_path(),"light_dump") self.layout=QVBoxLayout() label=QLabel(_("Optical model")+":") self.layout.addWidget(label) self.cb = QComboBox() self.cb.activated.connect(self.on_cb_model_changed) self.layout.addWidget(self.cb) self.update() self.setLayout(self.layout) self.show() def get_text(self): out=self.cb.currentText() out=out.split(" ")[0] return out def file_name_set_start(self,start): self.start=start def file_name_set_end(self,end): self.end=end def find_models(self): ret=[] path=get_plugins_path() for file in glob.glob(os.path.join(path,"*")): file_name=os.path.basename(file) if file_name.startswith("light_"): if file_name.endswith(".dll") or file_name.endswith(".so"): ret.append(os.path.basename(file_name[6:]).split(".")[0]) return ret def on_cb_model_changed(self): cb_text=self.cb.currentText() inp_update_token_value("light.inp", "#light_model", cb_text) def update(self): self.cb.blockSignals(True) self.cb.clear() models=self.find_models() if len(models)==0: error_dlg(self,_("I can't find any optical plugins, I think the model is not installed properly.")) return for i in range(0, len(models)): self.cb.addItem(models[i]) used_model=inp_get_token_value(os.path.join(get_sim_path(),"light.inp"), "#light_model") print(models,used_model) if models.count(used_model)==0: used_model="exp" inp_update_token_value(os.path.join(get_sim_path(),"light.inp"), "#light_model","exp") self.cb.setCurrentIndex(self.cb.findText(used_model)) else: self.cb.setCurrentIndex(self.cb.findText(used_model)) self.cb.blockSignals(False)
class PropertyWidget(QWidget): """display widget for tcam property""" def __init__(self, data: TcamCaptureData, prop: Prop): super().__init__() self.tcam = data.tcam self.signals = data.signals self.prop = prop self.setup_ui() def __repr__(self): return repr((self.prop.name, self.prop.valuetype, self.prop.category, self.prop.group)) def setup_ui(self): self.layout = QHBoxLayout() if self.prop.name != self.prop.group: self.spacer = QSpacerItem(30, 10) self.layout.addItem(self.spacer) self.setLayout(self.layout) if self.prop.valuetype != "boolean": self.name_label = QtWidgets.QLabel(self.prop.name) self.layout.addWidget(self.name_label) if self.prop.valuetype == "integer": self.value_label = QtWidgets.QLabel(str(self.prop.value)) self.layout.addWidget(self.value_label) self.sld = QSlider(Qt.Horizontal, self) self.sld.setFocusPolicy(Qt.NoFocus) self.sld.setRange(self.prop.minval, self.prop.maxval) self.sld.setValue(self.prop.value) self.sld.setGeometry(30, 40, 100, 30) self.sld.valueChanged[int].connect(self.set_property) self.layout.addWidget(self.sld) elif self.prop.valuetype == "double": self.value_label = QtWidgets.QLabel(str(self.prop.value)) self.layout.addWidget(self.value_label) self.sld = QSlider(Qt.Horizontal, self) self.sld.setFocusPolicy(Qt.NoFocus) self.sld.setRange(self.prop.minval * 1000, self.prop.maxval * 1000) self.sld.valueChanged[int].connect(self.set_property) self.sld.setGeometry(30, 40, 100, 30) self.layout.addWidget(self.sld) elif self.prop.valuetype == "button": self.checkbox = QPushButton(self) self.checkbox.clicked.connect(self.set_property) self.layout.addWidget(self.checkbox) elif self.prop.valuetype == "boolean": self.toggle = QPushButton(self.prop.name) self.toggle.setCheckable(True) if self.prop.value: self.toggle.toggle() self.toggle.toggled.connect(self.button_clicked) self.layout.addWidget(self.toggle) elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo = QComboBox(self) entry_list = self.tcam.get_tcam_menu_entries(self.prop.name) for e in entry_list: self.combo.addItem(e) self.combo.setCurrentText(self.prop.value) self.combo.currentIndexChanged['QString'].connect( self.set_property) self.layout.addWidget(self.combo) def button_clicked(self): log.debug("button clicked") self.signals.change_property.emit(self.tcam, self.prop.name, self.toggle.isChecked(), self.prop.valuetype) def set_property(self, value): if self.prop.valuetype == "integer": self.value_label.setText(str(value)) if self.prop.valuetype == "double": self.value_label.setText(str(value / 1000)) self.signals.change_property.emit(self.tcam, self.prop.name, float(value) / 1000, self.prop.valuetype) return self.signals.change_property.emit(self.tcam, self.prop.name, value, self.prop.valuetype) def update(self, prop: Prop): self.prop = prop if self.prop.valuetype == "integer": self.value_label.setText(str(self.prop.value)) self.sld.blockSignals(True) self.sld.setValue(self.prop.value) self.sld.blockSignals(False) elif self.prop.valuetype == "double": self.sld.blockSignals(True) self.value_label.setText("{:.3f}".format(self.prop.value)) self.sld.setValue((self.prop.value * 1000)) self.sld.blockSignals(False) elif self.prop.valuetype == "button": pass elif self.prop.valuetype == "boolean": if self.prop.value and not self.toggle.isChecked(): self.toggle.blockSignals(True) self.toggle.toggle() self.toggle.blockSignals(False) elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo.blockSignals(True) self.combo.setCurrentText(prop.value) self.combo.blockSignals(False)
class PropertyWidget(QWidget): value_changed = pyqtSignal(object) """display widget for tcam property""" def __init__(self, data: TcamCaptureData, prop: Prop, app_property: bool=False): super().__init__() self.app_property = app_property self.tcam = data.tcam self.signals = data.signals self.prop = prop self.setup_ui() def __repr__(self): return repr((self.prop.name, self.prop.valuetype, self.prop.category, self.prop.group)) def setup_ui(self): self.layout = QHBoxLayout() self.setLayout(self.layout) if self.prop.valuetype == "integer": self.sld = QSlider(Qt.Horizontal, self) self.sld.setFocusPolicy(Qt.NoFocus) try: self.sld.setRange(self.prop.minval, self.prop.maxval) self.sld.setValue(self.prop.value) except OverflowError: log.error("Property {} reported a range that could not be handled".format(self.prop.name)) self.sld.setSingleStep(self.prop.step) self.sld.valueChanged[int].connect(self.set_property) self.layout.addWidget(self.sld) self.value_box = QSpinBox(self) try: self.value_box.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("Property {} reported a range that could not be handled".format(self.prop.name)) self.value_box.setSingleStep(self.prop.step) self.value_box.setValue(self.prop.value) self.value_box.valueChanged[int].connect(self.set_property_box) self.value_box.setKeyboardTracking(False) self.layout.addWidget(self.value_box) elif self.prop.valuetype == "double": self.sld = QSlider(Qt.Horizontal, self) self.sld.setFocusPolicy(Qt.NoFocus) try: self.sld.setRange(self.prop.minval, self.prop.maxval) self.sld.setValue(self.prop.value) except OverflowError: log.error("Property {} reported a range that could not be handled".format(self.prop.name)) self.sld.setSingleStep(self.prop.step * 1000) self.sld.valueChanged[int].connect(self.set_property) self.sld.setGeometry(30, 40, 100, 30) self.layout.addWidget(self.sld) self.value_box = QDoubleSpinBox(self) try: self.value_box.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("Property {} reported a range that could not be handled".format(self.prop.name)) self.value_box.setSingleStep(self.prop.step) self.value_box.setValue(self.prop.value) self.value_box.valueChanged[float].connect(self.set_property_box) self.value_box.setKeyboardTracking(False) self.layout.addWidget(self.value_box) elif self.prop.valuetype == "button": self.checkbox = QPushButton(self) self.checkbox.clicked.connect(self.set_property) self.layout.addWidget(self.checkbox) elif self.prop.valuetype == "boolean": self.toggle = QCheckBox(self) self.toggle.setCheckable(True) if self.prop.value: self.toggle.toggle() self.toggle.toggled.connect(self.button_clicked) self.layout.addWidget(self.toggle) elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo = QComboBox(self) entry_list = self.tcam.get_tcam_menu_entries(self.prop.name) for e in entry_list: self.combo.addItem(e) self.combo.setCurrentText(self.prop.value) self.combo.currentIndexChanged['QString'].connect(self.set_property) self.layout.addWidget(self.combo) def button_clicked(self): log.debug("button clicked") self.signals.change_property.emit(self.tcam, self.prop.name, self.toggle.isChecked(), self.prop.valuetype) self.value_changed.emit(self) def set_property(self, value, emit_value_changed=True): self.prop.value = value if self.prop.valuetype == "integer": self.update_box_value(self.value_box, value) if self.prop.valuetype == "double": self.update_box_value(self.value_box, value) self.signals.change_property.emit(self.tcam, self.prop.name, float(value), self.prop.valuetype) return self.signals.change_property.emit(self.tcam, self.prop.name, value, self.prop.valuetype) if emit_value_changed: self.value_changed.emit(self) def set_property_box(self, value): if self.prop.valuetype == "integer": self.update_slider_value(self.sld, value) if self.prop.valuetype == "double": self.update_slider_value(self.sld, value) self.signals.change_property.emit(self.tcam, self.prop.name, float(value), self.prop.valuetype) return self.signals.change_property.emit(self.tcam, self.prop.name, value, self.prop.valuetype) self.value_changed.emit(self) def update_box_value(self, box, value): box.blockSignals(True) box.setValue(value) box.blockSignals(False) def update_box_range(self, box, minval, maxval): """""" box.blockSignals(True) box.setRange(self.prop.minval, self.prop.maxval) box.blockSignals(False) def update_slider_value(self, slider, value): slider.blockSignals(True) try: slider.setValue(value) except OverflowError: log.error("A slider had a value outside of the integer range. That should no happen.") finally: slider.blockSignals(False) def update_slider_range(self, slider, minval, maxval): """""" self.sld.blockSignals(True) try: self.sld.setRange(self.prop.minval, self.prop.maxval) except OverflowError: log.error("A slider had a value outside of the integer range. That should no happen.") finally: self.sld.blockSignals(False) def update(self, prop: Prop): emit_value_changed = False if self.prop.value != prop.value: emit_value_changed = True self.prop = prop if self.prop.valuetype == "integer": self.update_slider_value(self.sld, self.prop.value) self.update_slider_range(self.sld, self.prop.minval, self.prop.maxval) self.update_box_range(self.value_box, self.prop.minval, self.prop.maxval) self.update_box_value(self.value_box, self.prop.value) elif self.prop.valuetype == "double": self.update_slider_range(self.sld, self.prop.minval, self.prop.maxval) self.update_slider_value(self.sld, self.prop.value) self.update_box_range(self.value_box, self.prop.minval, self.prop.maxval) self.update_box_value(self.value_box, self.prop.value) elif self.prop.valuetype == "button": pass elif self.prop.valuetype == "boolean": if self.prop.value and not self.toggle.isChecked(): self.toggle.blockSignals(True) self.toggle.toggle() self.toggle.blockSignals(False) elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo.blockSignals(True) self.combo.setCurrentText(prop.value) self.combo.blockSignals(False) if emit_value_changed: self.value_changed.emit(self) def reset(self): if self.prop.valuetype == "integer": self.update_box_value(self.value_box, self.prop.defval) self.sld.setValue(self.prop.defval) elif self.prop.valuetype == "double": self.update_box_value(self.value_box, self.prop.defval) self.sld.setValue(self.prop.defval * 1000) elif self.prop.valuetype == "button": pass elif self.prop.valuetype == "boolean": if self.prop.defval and not self.toggle.isChecked(): self.toggle.toggle() elif self.prop.valuetype == "string": pass elif self.prop.valuetype == "enum": self.combo.setCurrentText(self.prop.defval) self.value_changed.emit(self)
class HistoryManagerWidget(QWidget): def __init__(self): super().__init__() self.listHistory = [] self.nam = QtNetwork.QNetworkAccessManager() self.setting = QSettings() self.searchLineEdit = QLineEdit() self.tableView = QTableView() self.columnComboBox = QComboBox() self.searchLabel = QLabel() self.searchLabel.setText("Filter") self.model = QStandardItemModel(self) searchHbox = QHBoxLayout() searchHbox.addWidget(self.searchLabel) searchHbox.addWidget(self.searchLineEdit) searchHbox.addWidget(self.columnComboBox) self.header = "Order ID;Order Status;Card ID;Menu ID;Menu Name;Price;Qty;" \ "Item Status;Table Number;Order Time;Modified Time".split(";") self.model.setHorizontalHeaderLabels(self.header) self.tableView.horizontalHeader().setStretchLastSection(True) hboxLayout = QHBoxLayout() self.backButton = QPushButton() self.backButton.setText("Back") self.refreshButton = QPushButton() self.refreshButton.setText("Refresh") self.refreshButton.clicked.connect(self.refresh) self.saveCSVButton = QPushButton() self.saveCSVButton.setText("Save as CSV") self.saveCSVButton.clicked.connect(self.saveCSV) hboxLayout.addWidget(self.backButton) hboxLayout.addWidget(self.refreshButton) hboxLayout.addWidget(self.saveCSVButton) widgetTitleLabel = QLabel() widgetTitleLabel.setAlignment(Qt.AlignCenter) widgetTitleLabel.setText("History Manager") mainLayout = QVBoxLayout() mainLayout.addWidget(widgetTitleLabel) mainLayout.addLayout(searchHbox) mainLayout.addWidget(self.tableView) mainLayout.addLayout(hboxLayout) self.proxy = QSortFilterProxyModel(self) self.proxy.setSourceModel(self.model) self.tableView.setModel(self.proxy) self.columnComboBox.addItems(self.header) self.searchLineEdit.textChanged.connect(self.on_lineEdit_textChanged) self.columnComboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged) self.horizontalHeader = self.tableView.horizontalHeader() self.setLayout(mainLayout) def saveCSV(self): data = "" rows = self.model.rowCount() columns = self.model.columnCount() if rows>0: for title in self.header: data += title data += "," data += "\n" for i in range(rows): for j in range(columns): index = self.model.index(i,j) # print(str(self.model.data(index))) data += str(self.model.data(index)) data += "," data += "\n" name, _ = QFileDialog.getSaveFileName(self, 'Save File', "History SELFO.csv", "csv(*.csv)") if name: file = open(name, 'w') file.write(data) file.close() else: QMessageBox.critical(self, "Error", "No Data") def clear(self): self.listHistory.clear() self.model.clear() self.model.setHorizontalHeaderLabels(self.header) def refresh(self): self.clear() self.doRequestOrderDetail() def populateList(self): for rowName in range(len(self.listHistory)): self.model.invisibleRootItem().appendRow( [QStandardItem("{}".format(self.listHistory[rowName][column])) for column in range(len(self.header)) ] ) self.tableView.resizeColumnsToContents() def closeHistoryManager(self): self.mainWindow.stackedWidget.removeWidget(self) def doRequestOrderDetail(self): url = self.setting.value("baseURL", "") url += "/orderdetail/?sellerID=" + str(self.setting.value("sellerID", "")) \ # + "&itemStatus=" + "placed" req = QtNetwork.QNetworkRequest(QUrl(url)) reply = self.nam.get(req) reply.finished.connect(self.handleResponseOrderDetail) def handleResponseOrderDetail(self): reply = self.sender() er = reply.error() if er == QtNetwork.QNetworkReply.NoError: bytes_string = reply.readAll() data = json.loads(str(bytes_string, 'utf-8')) # print(data) for history in data: # id = history['id'] orderID = history['orderID'] orderStatus = history['orderStatus'] cardID = history['cardID'] # sellerID = history['sellerID'] menuID = history['menuID'] menuName = history['menuName'] itemStatus = history['itemStatus'] price = formatRupiah(history['price']) qty = history['qty'] tableNumber = history['tableNumber'] rawOrderTime = history['orderTime'] orderTime = datetime.datetime.strptime( rawOrderTime, "%Y-%m-%dT%H:%M:%S.%f+07:00").strftime("%d %b %Y %H:%M:%S") rawModifiedTime = history['modifiedTime'] if rawModifiedTime is not None: modifiedTime = datetime.datetime.strptime( rawModifiedTime, "%Y-%m-%dT%H:%M:%S.%f+07:00").strftime("%d %b %Y %H:%M:%S") else: modifiedTime = "null" historyItem = [orderID, orderStatus, cardID, menuID, menuName, price, qty, itemStatus, tableNumber, orderTime, modifiedTime] self.listHistory.append(historyItem) self.populateList() else: errorMessage = "Error occured: " + str(er) + "\n" + str(reply.errorString()) QMessageBox.critical(self, "Error Order Detail", errorMessage) reply.deleteLater() def on_view_horizontalHeader_sectionClicked(self, logicalIndex): self.logicalIndex = logicalIndex self.menuValues = QMenu(self) self.signalMapper = QSignalMapper(self) self.columnComboBox.blockSignals(True) self.columnComboBox.setCurrentIndex(self.logicalIndex) self.columnComboBox.blockSignals(True) valuesUnique = [ self.model.item(row, self.logicalIndex).text() for row in range(self.model.rowCount()) ] actionAll = QAction("All", self) actionAll.triggered.connect(self.on_actionAll_triggered) self.menuValues.addAction(actionAll) self.menuValues.addSeparator() for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))): action = QAction(actionName, self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) self.menuValues.addAction(action) self.signalMapper.mapped.connect(self.on_signalMapper_mapped) headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos()) posY = headerPos.y() + self.horizontalHeader.height() posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex) self.menuValues.exec_(QPoint(posX, posY)) def on_actionAll_triggered(self): filterColumn = self.logicalIndex filterString = QRegExp( "", Qt.CaseInsensitive, QRegExp.RegExp ) self.proxy.setFilterRegExp(filterString) self.proxy.setFilterKeyColumn(filterColumn) def on_signalMapper_mapped(self, i): stringAction = self.signalMapper.mapping(i).text() filterColumn = self.logicalIndex filterString = QRegExp( stringAction, Qt.CaseSensitive, QRegExp.FixedString ) self.proxy.setFilterRegExp(filterString) self.proxy.setFilterKeyColumn(filterColumn) def on_lineEdit_textChanged(self, text): search = QRegExp( text, Qt.CaseInsensitive, QRegExp.RegExp ) self.proxy.setFilterRegExp(search) def on_comboBox_currentIndexChanged(self, index): self.proxy.setFilterKeyColumn(index)
class BasemapRenderingOptionsWidget(QFrame): values_changed = pyqtSignal() def __init__(self, datatype=None): super().__init__() self.layout = QGridLayout() self.layout.setMargin(0) self.labelProc = QLabel("Processing:") self.comboProc = QComboBox() self.layout.addWidget(self.labelProc, 0, 0) self.layout.addWidget(self.comboProc, 0, 1) self.load_ramps() self.labelRamp = QLabel("Color ramp:") self.comboRamp = QComboBox() self.layout.addWidget(self.labelRamp, 1, 0) self.layout.addWidget(self.comboRamp, 1, 1) self.setLayout(self.layout) self.comboProc.currentIndexChanged.connect(self._proc_changed) self.listWidget = QListWidget() self.comboRamp.setView(self.listWidget) self.comboRamp.setModel(self.listWidget.model()) self.comboRamp.currentIndexChanged.connect(self.values_changed.emit) self.set_datatype(datatype) self.setStyleSheet("QFrame {border: 0px;}") def set_datatype(self, datatype): self.datatype = datatype self.comboRamp.blockSignals(True) self.comboProc.clear() self.comboProc.addItem("default") procs = self.processes_for_datatype() if procs: self.comboProc.addItems(procs) self.labelProc.setVisible(self.can_use_indices()) self.comboProc.setVisible(self.can_use_indices()) self.comboRamp.blockSignals(False) self.comboRamp.setVisible(self.can_use_indices()) self.labelRamp.setVisible(self.can_use_indices()) self._proc_changed() def _proc_changed(self): if self.can_use_indices(): self.comboRamp.clear() self.comboRamp.setIconSize(QSize(100, 20)) default, ramps = self.ramps_for_current_process() if ramps: self.comboRamp.setVisible(True) self.labelRamp.setVisible(True) for name in ramps: icon = self.ramp_pixmaps[name] self.comboRamp.addItem(name) self.comboRamp.setItemData(self.comboRamp.count() - 1, icon, Qt.DecorationRole) self.comboRamp.setCurrentText(default) if len(ramps) != len(list(self.ramps["colors"].keys())): item = QListWidgetItem() self.listWidget.addItem(item) label = QLabel( "<a href='#' style='color: grey;'>Show all ramps</a>") label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) label.linkActivated.connect(self._show_all_ramps) self.listWidget.setItemWidget(item, label) else: self.comboRamp.setVisible(False) self.labelRamp.setVisible(False) self.values_changed.emit() else: self.values_changed.emit() def _show_all_ramps(self): self.comboRamp.clear() self.comboRamp.setIconSize(QSize(100, 20)) ramps = list(self.ramps["colors"].keys()) for name in ramps: icon = self.ramp_pixmaps[name] self.comboRamp.addItem(name) self.comboRamp.setItemData(self.comboRamp.count() - 1, icon, Qt.DecorationRole) self.comboRamp.showPopup() def ramps_for_current_process(self): process = self.comboProc.currentText() if process in self.ramps["indices"]: pref_colors = self.ramps["indices"][process][ "pref-colors"] or list(self.ramps["colors"].keys()) return self.ramps["indices"][process]["color"], pref_colors else: return None, [] def load_ramps(self): path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "mosaics_caps.json") with open(path) as f: self.ramps = json.load(f) self.ramp_pixmaps = {} for k, v in self.ramps["colors"].items(): base64 = v["icon"][len("data:image/png;base64,"):].encode() byte_array = QByteArray.fromBase64(base64) image = QImage.fromData(byte_array, "PNG") scaled = image.scaled(100, 20) pixmap = QPixmap.fromImage(scaled) self.ramp_pixmaps[k] = pixmap def processes_for_datatype(self): if self.datatype == "uint16": return [ "rgb", "cir", "ndvi", "mtvi2", "ndwi", "msavi2", "tgi", "vari" ] else: return [] def can_use_indices(self): return self.datatype == "uint16" def process(self): return self.comboProc.currentText() def set_process(self, proc): self.comboProc.setCurrentText(proc) def set_ramp(self, ramp): self.comboRamp.setCurrentText(ramp) def ramp(self): ramp = self.comboRamp.currentText() if self.can_use_indices() else "" return ramp
class SchemeSelector(QWidget): currentChanged = pyqtSignal() changed = pyqtSignal() def __init__(self, parent=None): super(SchemeSelector, self).__init__(parent) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.label = QLabel() self.scheme = QComboBox() self.menuButton = QPushButton(flat=True) menu = QMenu(self.menuButton) self.menuButton.setMenu(menu) layout.addWidget(self.label) layout.addWidget(self.scheme) layout.addWidget(self.menuButton) layout.addStretch(1) # action generator def act(slot, icon=None): a = QAction(self, triggered=slot) self.addAction(a) icon and a.setIcon(icons.get(icon)) return a # add action a = self.addAction_ = act(self.slotAdd, 'list-add') menu.addAction(a) # remove action a = self.removeAction = act(self.slotRemove, 'list-remove') menu.addAction(a) # rename action a = self.renameAction = act(self.slotRename, 'document-edit') menu.addAction(a) menu.addSeparator() # import action a = self.importAction = act(self.slotImport, 'document-open') menu.addAction(a) # export action a = self.exportAction = act(self.slotExport, 'document-save-as') menu.addAction(a) self.scheme.currentIndexChanged.connect(self.slotSchemeChanged) app.translateUI(self) def translateUI(self): self.label.setText(_("Scheme:")) self.menuButton.setText(_("&Menu")) self.addAction_.setText(_("&Add...")) self.removeAction.setText(_("&Remove")) self.renameAction.setText(_("Re&name...")) self.importAction.setText(_("&Import...")) self.exportAction.setText(_("&Export...")) def slotSchemeChanged(self, index): """Called when the Scheme combobox is changed by the user.""" self.disableDefault(self.scheme.itemData(index) == 'default') self.currentChanged.emit() self.changed.emit() def disableDefault(self, val): self.removeAction.setDisabled(val) self.renameAction.setDisabled(val) def schemes(self): """Returns the list with internal names of currently available schemes.""" return [self.scheme.itemData(i) for i in range(self.scheme.count())] def currentScheme(self): """Returns the internal name of the currently selected scheme""" return self.scheme.itemData(self.scheme.currentIndex()) def insertSchemeItem(self, name, scheme): for i in range(1, self.scheme.count()): n = self.scheme.itemText(i) if n.lower() > name.lower(): self.scheme.insertItem(i, name, scheme) break else: self.scheme.addItem(name, scheme) def addScheme(self, name): num, key = 1, 'user1' while key in self.schemes() or key in self._schemesToRemove: num += 1 key = 'user{0}'.format(num) self.insertSchemeItem(name, key) self.scheme.setCurrentIndex(self.scheme.findData(key)) return key def slotAdd(self): name, ok = QInputDialog.getText( self, app.caption(_("Add Scheme")), _("Please enter a name for the new scheme:")) if ok: self.addScheme(name) def slotRemove(self): index = self.scheme.currentIndex() scheme = self.scheme.itemData(index) if scheme == 'default': return # default can not be removed self._schemesToRemove.add(scheme) self.scheme.removeItem(index) def slotRename(self): index = self.scheme.currentIndex() name = self.scheme.itemText(index) scheme = self.scheme.itemData(index) newName, ok = QInputDialog.getText(self, _("Rename"), _("New name:"), text=name) if ok: self.scheme.blockSignals(True) self.scheme.removeItem(index) self.insertSchemeItem(newName, scheme) self.scheme.setCurrentIndex(self.scheme.findData(scheme)) self.scheme.blockSignals(False) self.changed.emit() def slotImport(self): filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption(_("dialog title", "Import color theme")) filename = QFileDialog.getOpenFileName(self, caption, QDir.homePath(), filetypes)[0] if filename: self.parent().import_(filename) def slotExport(self): name = self.scheme.currentText() filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption( _("dialog title", "Export {name}").format(name=name)) path = os.path.join(QDir.homePath(), name + '.xml') filename = QFileDialog.getSaveFileName(self, caption, path, filetypes)[0] if filename: if os.path.splitext(filename)[1] != '.xml': filename += '.xml' self.parent().export(name, filename) def loadSettings(self, currentKey, namesGroup): # don't mark schemes for removal anymore self._schemesToRemove = set() s = QSettings() cur = s.value(currentKey, "default", str) # load the names for the shortcut schemes s.beginGroup(namesGroup) block = self.scheme.blockSignals(True) self.scheme.clear() self.scheme.addItem(_("Default"), "default") lst = [(s.value(key, key, str), key) for key in s.childKeys()] for name, key in sorted(lst, key=lambda f: f[0].lower()): self.scheme.addItem(name, key) # find out index index = self.scheme.findData(cur) self.disableDefault(cur == 'default') self.scheme.setCurrentIndex(index) self.scheme.blockSignals(block) self.currentChanged.emit() def saveSettings(self, currentKey, namesGroup, removePrefix=None): # first save new scheme names s = QSettings() s.beginGroup(namesGroup) for i in range(self.scheme.count()): if self.scheme.itemData(i) != 'default': s.setValue(self.scheme.itemData(i), self.scheme.itemText(i)) for scheme in self._schemesToRemove: s.remove(scheme) s.endGroup() if removePrefix: for scheme in self._schemesToRemove: s.remove("{0}/{1}".format(removePrefix, scheme)) # then save current scheme = self.currentScheme() s.setValue(currentKey, scheme) # clean up self._schemesToRemove = set()
class LedgerAuthDialog(QDialog): def __init__(self, handler, data): '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods. Use last method from settings, but support new pairing and downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata[ 'keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(650) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg) self.dongle = self.handler.win.wallet.get_keystore().get_client( ).dongle self.ws = None self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): if idx < 2 and self.ws: self.ws.stop() self.ws = None self.cfg[ 'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws: self.req_validation() if self.cfg['mode'] > 0: self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.update_dlg() def add_pairing(): self.do_pairing() def return_pin(): self.pin = self.pintxt.text( ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text() if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) self.addPair = QPushButton(_("Pair")) self.addPair.setMaximumWidth(60) modelayout.addWidget(self.addPair) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.addPair.clicked.connect(add_pairing) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet( "QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = QLineEdit() self.pintxt.setEchoMode(2) self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet( "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; font-family:monospace; }" ) self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(130) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] if not constants.net.TESTNET: text = addr[:i] + '<u><b>' + addr[ i:i + 1] + '</u></b>' + addr[i + 1:] else: # pin needs to be created from mainnet address addr_mainnet = bitcoin.script_to_address( bitcoin.address_to_script(addr), net=constants.BitcoinMainnet) addr_mainnet = addr_mainnet[:i] + '<u><b>' + addr_mainnet[ i:i + 1] + '</u></b>' + addr_mainnet[i + 1:] text = str(addr) + '\n' + str(addr_mainnet) self.addrtext.setHtml(str(text)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = QLineEdit() self.cardtxt.setEchoMode(2) self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.pairbox = QWidget() pairlayout = QVBoxLayout() self.pairbox.setLayout(pairlayout) pairhelp = QTextEdit(helpTxt[5]) pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }") pairhelp.setReadOnly(True) pairlayout.addWidget(pairhelp, 1) self.pairqr = QRCodeWidget() pairlayout.addWidget(self.pairqr, 4) self.pairbox.setVisible(False) vbox.addWidget(self.pairbox) self.update_dlg() if self.cfg['mode'] > 1 and not self.ws: self.req_validation() def populate_modes(self): self.modes.blockSignals(True) self.modes.clear() self.modes.addItem( _("Summary Text PIN (requires dongle replugging)" ) if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled")) if self.txdata['confirmationType'] > 1: self.modes.addItem(_("Security Card Challenge")) if not self.cfg['pair']: self.modes.addItem(_("Mobile - Not paired")) else: self.modes.addItem( _("Mobile - {}").format(self.cfg['pair'][1])) self.modes.blockSignals(False) def update_dlg(self): self.modes.setCurrentIndex(self.cfg['mode']) self.modebox.setVisible(True) self.addPair.setText( _("Pair") if not self.cfg['pair'] else _("Re-Pair")) self.addPair.setVisible(self.txdata['confirmationType'] > 2) self.helpmsg.setText( helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self. cfg['pair'] else 4]) self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100) self.pairbox.setVisible(False) self.helpmsg.setVisible(True) self.pinbox.setVisible(self.cfg['mode'] == 0) self.cardbox.setVisible(self.cfg['mode'] == 1) self.pintxt.setFocus( True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True) self.setMaximumHeight(400) def do_pairing(self): rng = os.urandom(16) pairID = (hexlify(rng) + hexlify(hashlib.sha256(rng).digest()[0:1])).decode('utf-8') self.pairqr.setData(pairID) self.modebox.setVisible(False) self.helpmsg.setVisible(False) self.pinbox.setVisible(False) self.cardbox.setVisible(False) self.pairbox.setVisible(True) self.pairqr.setMinimumSize(300, 300) if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, pairID) self.ws.pairing_done.connect(self.pairing_done) self.ws.start() def pairing_done(self, data): if data is not None: self.cfg['pair'] = [data['pairid'], data['name'], data['platform']] self.cfg['mode'] = 2 self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.pin = 'paired' self.accept() def req_validation(self): if self.cfg['pair'] and 'secureScreenData' in self.txdata: if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata) self.ws.req_updated.connect(self.req_updated) self.ws.start() def req_updated(self, pin): if pin == 'accepted': self.helpmsg.setText(helpTxt[3]) else: self.pin = str(pin) self.accept() def getDevice2FAMode(self): apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode try: mode = self.dongle.exchange(bytearray(apdu)) return mode except BTChipException as e: debug_msg('Device getMode Failed') return 0x11 def closeEvent(self, evnt): debug_msg("CLOSE - Stop WS") if self.ws: self.ws.stop() if self.pairbox.isVisible(): evnt.ignore() self.update_dlg()
class FilterableTable(SQLTable): """a filterable Table Widget that displays content of an SQLite table; for individual widgets, subclass and overwrite the create_model method; add_color_proxy should be an (INT allele_status-column, INT lab_status-column) tuple """ def __init__(self, log, mydb = ": memory :", add_color_proxy = False, header_dic = None): super().__init__(log, mydb) self.add_color_proxy = add_color_proxy self.header_dic = header_dic self.create_model() self.fill_UI() self.create_filter_model() self.update_filterbox() def fill_UI(self): """sets up the layout """ self.log.debug("\t- Setting up the table...") self.table = QTableView() self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.header = self.table.horizontalHeader() # table header self.header.setSectionResizeMode(QHeaderView.ResizeToContents) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setAlternatingRowColors(True) # self.header.sectionClicked.connect(self.on_header_sectionClicked) mode = QAbstractItemView.SingleSelection self.table.setSelectionMode(mode) self.grid.addWidget(self.table, 2, 0, 10, 10) self.filter_lbl = QLabel("Filter:", self) self.grid.addWidget(self.filter_lbl, 1, 2) self.filter_entry = QLineEdit(self) self.grid.addWidget(self.filter_entry, 1, 3) self.filter_entry.textChanged.connect(self.on_filter_entry_textChanged) self.filter_text = "" self.filter_cb = QComboBox(self) self.grid.addWidget(self.filter_cb, 1, 4) self.filter_cb.currentIndexChanged.connect(self.on_filter_cb_IndexChanged) self.filter_btn = QPushButton("Filter!", self) self.grid.addWidget(self.filter_btn, 1, 5) self.filter_btn.clicked.connect(self.on_filter_btn_clicked) self.unfilter_btn = QPushButton("Remove Filter", self) self.grid.addWidget(self.unfilter_btn, 1, 6) self.unfilter_btn.clicked.connect(self.on_actionAll_triggered) self.log.debug("\t=> Done!") def update_filterbox(self): """fills the filter-combobox with the header values after the model has been created and set """ column_num = self.model.columnCount() if self.header_dic: columns = [self.header_dic[i] for i in self.header_dic] else: columns = [self.proxy.headerData(i, Qt.Horizontal) for i in range(column_num)] self.filter_cb.addItems(columns) def create_filter_model(self): """creates the filter-proxy-model on top of self.model """ self.log.debug("Creating filter model...") self.proxy = QSortFilterProxyModel(self) if self.add_color_proxy: (allele_status_column, lab_status_column) = self.add_color_proxy self.log.debug("adding color filter to columns {} and {}".format(allele_status_column, lab_status_column)) self.color_proxy = ColorProxyModel(self, allele_status_column, lab_status_column) self.color_proxy.setSourceModel(self.model) self.proxy.setSourceModel(self.color_proxy) else: self.proxy.setSourceModel(self.model) self.table.setSortingEnabled(True) self.table.setModel(self.proxy) def on_filter_cb_IndexChanged(self, index): """restricts RegEx filter to selected column """ self.log.debug("Combobox: colum {} selected".format(index)) self.proxy.setFilterKeyColumn(index) def on_filter_entry_textChanged(self, text): """stores content of filter_entry as self.text """ self.log.debug("filter text: '{}'".format(text)) self.filter_text = text def on_filter_btn_clicked(self): """activates RegEx filter to current content of filter_entry and filter_cb """ column = self.filter_cb.currentIndex() self.log.debug("Filtering column {} for '{}'".format(column, self.filter_text)) self.proxy.setFilterKeyColumn(column) search = QRegExp(self.filter_text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxy.setFilterRegExp(search) def on_header_sectionClicked(self, logicalIndex): """opens a dialog to choose between all unique values for this column, or revert to 'All' """ self.log.debug("Header clicked: column {}".format(logicalIndex)) self.logicalIndex = logicalIndex menuValues = QMenu(self) self.signalMapper = QSignalMapper(self) self.filter_cb.setCurrentIndex(self.logicalIndex) self.filter_cb.blockSignals(True) self.proxy.setFilterKeyColumn(self.logicalIndex) valuesUnique = [str(self.model.index(row, self.logicalIndex).data()) for row in range(self.model.rowCount()) ] actionAll = QAction("All", self) actionAll.triggered.connect(self.on_actionAll_triggered) menuValues.addAction(actionAll) menuValues.addSeparator() for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))): action = QAction(actionName, self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) menuValues.addAction(action) self.signalMapper.mapped.connect(self.on_signalMapper_mapped) headerPos = self.table.mapToGlobal(self.header.pos()) posY = headerPos.y() + self.header.height() posX = headerPos.x() + self.header.sectionViewportPosition(self.logicalIndex) menuValues.exec_(QPoint(posX, posY)) def on_actionAll_triggered(self): """reverts table to unfiltered state """ self.log.debug("Unfiltering...") filterString = QRegExp("", Qt.CaseInsensitive, QRegExp.RegExp) self.proxy.setFilterRegExp(filterString) self.filter_entry.setText("") def on_signalMapper_mapped(self, i): """filters current column to mapping text """ text = self.signalMapper.mapping(i).text() self.log.debug("Filtering column {} to '{}'".format(self.logicalIndex, text)) filterString = QRegExp(text, Qt.CaseSensitive, QRegExp.FixedString) self.proxy.setFilterRegExp(filterString)