def _clearSettings(self): """ Just clears all peacock settings. """ settings = QSettings() settings.clear() settings.sync()
def testPrefs(self): settings = QSettings() settings.setValue("execute/maxRecentWorkingDirs", 2) settings.setValue("execute/maxRecentExes", 3) settings.setValue("execute/maxRecentArgs", 4) settings.setValue("execute/mpiEnabled", True) settings.setValue("execute/mpiArgs", "foo bar") settings.setValue("execute/threadsEnabled", True) settings.setValue("execute/threadsArgs", "threads args") settings.sync() main_win, w = self.newWidget() ops = w.ExecuteOptionsPlugin self.assertEqual(ops.mpi_checkbox.isChecked(), True) self.assertEqual(ops.threads_checkbox.isChecked(), True) self.assertEqual(ops.mpi_line.text(), "foo bar") self.assertEqual(ops.threads_line.text(), "threads args") settings.setValue("execute/mpiEnabled", False) settings.setValue("execute/mpiArgs", "some args") settings.setValue("execute/threadsEnabled", False) settings.setValue("execute/threadsArgs", "other args") settings.sync() main_win, w = self.newWidget() ops = w.ExecuteOptionsPlugin self.assertEqual(ops.mpi_checkbox.isChecked(), False) self.assertEqual(ops.threads_checkbox.isChecked(), False) self.assertEqual(ops.mpi_line.text(), "some args") self.assertEqual(ops.threads_line.text(), "other args")
def SetLanguage(self): global actualLanguage settings = QSettings('Pandoc', 'PanConvert') settings.setValue('default_language', str(self.ui.comboBoxLanguageSelector.currentText())) actualLanguage = str(self.ui.comboBoxLanguageSelector.currentText()) settings.sync() settings.status()
def settings(self): settings = QSettings("Pandoc", "PanConvert") settings.setValue("path_pandoc", self.ui.Pandoc_Path.text()) settings.setValue("path_multimarkdown", self.ui.Markdown_Path.text()) settings.setValue("path_dialog", self.ui.Dialog_Path.text()) settings.setValue("fromParameter", self.ui.FromParameter.text()) settings.setValue("toParameter", self.ui.ToParameter.text()) settings.setValue("xtraParameter", self.ui.XtraParameter.text()) settings.setValue("Standard_Conversion", self.ui.StandardConversion.isChecked()) settings.setValue("Batch_Conversion", self.ui.BatchConversion.isChecked()) settings.setValue("From_Markdown", self.ui.ButtonFromMarkdown.isChecked()) settings.setValue("From_Html", self.ui.ButtonFromHtml.isChecked()) settings.setValue("From_Latex", self.ui.ButtonFromLatex.isChecked()) settings.setValue("From_Opml", self.ui.ButtonFromOpml.isChecked()) settings.setValue("To_Markdown", self.ui.ButtonToMarkdown.isChecked()) settings.setValue("To_Html", self.ui.ButtonToHtml.isChecked()) settings.setValue("To_Latex", self.ui.ButtonToLatex.isChecked()) settings.setValue("To_Opml", self.ui.ButtonToOpml.isChecked()) settings.setValue("To_Lyx", self.ui.ButtonToLyx.isChecked()) settings.sync() settings.status() PreferenceDialog.close(self)
def test_init(init_patch, config_tmpdir): configfiles.init() # Make sure qsettings land in a subdir if utils.is_linux: settings = QSettings() settings.setValue("hello", "world") settings.sync() assert (config_tmpdir / 'qsettings').exists()
def _save(self): """ Saves all the settings from the different widgets. """ settings = QSettings() for i in range(self.tabs.count()): w = self.tabs.widget(i) w.save(settings) settings.sync() self.close()
def batch_settings(self): global batch_open_path, openfiles batch_settings = QSettings('Pandoc', 'PanConvert') batch_settings.setValue('batch_convert_directory', self.ui.ParameterBatchconvertDirectory.isChecked()) batch_settings.setValue('batch_convert_files', self.ui.ParameterBatchconvertFiles.isChecked()) batch_settings.setValue('batch_convert_recursive', self.ui.ParameterBatchconvertRecursive.isChecked()) batch_settings.setValue('batch_open_path', self.ui.OpenPath.text()) batch_settings.sync() batch_settings.status() BatchDialog.close(self)
def clearAll(settings_key): """ Clear the cache files and the value in QSettings Input: settings_key[str]: The key in QSettings """ settings = QSettings() val = settings.value(settings_key, type=dict) for key, val in val.items(): FileCache.removeCacheFile(val["pickle_path"]) settings.remove(settings_key) settings.sync()
def closeEvent(self, event): settings = QSettings('Pandoc', 'PanConvert') Dialog_Size = settings.value('Dialog_Size') if Dialog_Size is True or Dialog_Size == 'true': settings.setValue("Batch_size", self.size()) settings.setValue("Batch_pos", self.pos()) settings.sync() settings.status() BatchDialog.close(self)
def show_preferences(self): """shows the preferences dialog""" dialog = QDialog() my_preferences_dialog = PreferencesDialog(dialog) dialog_accepted = dialog.exec() if dialog_accepted: print('image quality set to ' + str(my_preferences_dialog.spinBox.value())) mySettings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'Sogeti', 'validate') mySettings.setValue('report/imageQuality', my_preferences_dialog.spinBox.value()) mySettings.sync()
def resetPushButtonPressed(self, button): # create an object for PersepolisDB persepolis_db = PersepolisDB() # Reset data base persepolis_db.resetDataBase() # close connections persepolis_db.closeConnections() # Reset persepolis_setting persepolis_setting = QSettings('persepolis_download_manager', 'persepolis') persepolis_setting.clear() persepolis_setting.sync()
def settings(self): settings = QSettings('Pandoc', 'PanConvert') settings.setValue('Window_Size', self.ui.Window_Size.isChecked()) settings.setValue('Dock_Size', self.ui.Dock_Size.isChecked()) settings.setValue('Dialog_Size', self.ui.Dialog_Size.isChecked()) settings.setValue('Hide_Batch', self.ui.Hide_Batch.isChecked()) settings.setValue('Button_OldGui', self.ui.Button_OldGui.isChecked()) settings.setValue('Button_NewGui', self.ui.Button_NewGui.isChecked()) settings.setValue('Tab_StandardConverter', self.ui.Tab_StandardConverter.isChecked()) settings.setValue('Tab_ManualConverter', self.ui.Tab_ManualConverter.isChecked()) settings.setValue('path_pandoc', self.ui.Pandoc_Path.text()) settings.setValue('path_multimarkdown', self.ui.Markdown_Path.text()) settings.setValue('path_dialog', self.ui.Dialog_Path.text()) settings.setValue('BufferSaveSuffix', self.ui.BufferSaveSuffix.text()) settings.setValue('BufferSaveName', self.ui.BufferSaveName.text()) settings.setValue('fromParameter', self.ui.FromParameter.text()) settings.setValue('toParameter', self.ui.ToParameter.text()) settings.setValue('xtraParameter', self.ui.XtraParameter.text()) settings.setValue('Standard_Conversion', self.ui.StandardConversion.isChecked()) settings.setValue('Batch_Conversion', self.ui.BatchConversion.isChecked()) settings.setValue('From_Markdown', self.ui.ButtonFromMarkdown.isChecked()) settings.setValue('From_Html', self.ui.ButtonFromHtml.isChecked()) settings.setValue('From_Latex', self.ui.ButtonFromLatex.isChecked()) settings.setValue('From_Opml', self.ui.ButtonFromOpml.isChecked()) settings.setValue('To_Markdown', self.ui.ButtonToMarkdown.isChecked()) settings.setValue('To_Html', self.ui.ButtonToHtml.isChecked()) settings.setValue('To_Latex', self.ui.ButtonToLatex.isChecked()) settings.setValue('To_Opml', self.ui.ButtonToOpml.isChecked()) settings.setValue('To_Lyx', self.ui.ButtonToLyx.isChecked()) Dialog_Size = settings.value('Dialog_Size') if Dialog_Size is True or Dialog_Size == 'true': settings.setValue("Preference_size", self.size()) settings.setValue("Preference_pos", self.pos()) settings.sync() settings.status() PreferenceDialog.close(self)
def get_path_pandoc(): settings = QSettings('Pandoc', 'PanConvert') path_pandoc_tmp = settings.value('path_pandoc','') path_pandoc = str(path_pandoc_tmp) if not os.path.isfile(path_pandoc): if platform.system() == 'Darwin' or os.name == 'posix': path_pandoc = which("pandoc") settings.setValue('path_pandoc', path_pandoc) settings.sync() else: path_pandoc = where("pandoc.exe") settings.setValue('path_pandoc', path_pandoc) settings.sync()
def saveSettingsChanged(self): if self.txtAutoSave.text() in ["", "0"]: self.txtAutoSave.setText("1") if self.txtAutoSaveNoChanges.text() in ["", "0"]: self.txtAutoSaveNoChanges.setText("1") sttgs = QSettings() sttgs.setValue("autoLoad", True if self.chkAutoLoad.checkState() else False) sttgs.sync() settings.autoSave = True if self.chkAutoSave.checkState() else False settings.autoSaveNoChanges = True if self.chkAutoSaveNoChanges.checkState() else False settings.saveOnQuit = True if self.chkSaveOnQuit.checkState() else False settings.autoSaveDelay = int(self.txtAutoSave.text()) settings.autoSaveNoChangesDelay = int(self.txtAutoSaveNoChanges.text()) self.mw.saveTimer.setInterval(settings.autoSaveDelay * 60 * 1000) self.mw.saveTimerNoChanges.setInterval(settings.autoSaveNoChangesDelay * 1000) settings.defaultTextType = self.cmbDefaultTextType.currentData()
def get_path_pandoc(): settings = QSettings('Pandoc', 'PanConvert') path_pandoc = settings.value('path_pandoc','') if len(path_pandoc) == 0: if platform.system() == 'Darwin' or platform.system() == 'Linux': args = ['which', 'pandoc'] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) path_pandoc = str.rstrip(p.communicate(path_pandoc.encode('utf-8'))[0].decode('utf-8')) settings.setValue('path_pandoc', path_pandoc) settings.sync() return path_pandoc elif platform.system() == 'Windows': args = ['where', 'pandoc'] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) path_pandoc = str.rstrip(p.communicate(path_pandoc.encode('utf-8'))[0].decode('utf-8')) settings.setValue('path_pandoc', path_pandoc) settings.sync() return path_pandoc else: QtWidgets.QMessageBox.warning(None, 'Error-Message', 'Could not detect the actual operating system. Please fill in the Path' ' to Pandoc manually via Preferences.') elif len(path_pandoc) != 0: return path_pandoc else: QtWidgets.QMessageBox.warning(None, 'Error-Message', 'I tried automagically to detect pandoc. But it failed.' 'Please input the path of pandoc manually via preferences')
class Config: def __init__(self, organization, product): self.config = QSettings(organization, product) def setValue(self, option, value): self.config.setValue(option, QVariant(value)) self.config.sync() def getBoolValue(self, option): default = self._initValue(option, False) return self.config.value(option, QVariant(default)).toBool() def getNumValue(self, option): default = self._initValue(option, 0) return self.config.value(option, QVariant(default)).toInt()[0] def _initValue(self, option, value): if defaults.has_key(option): return defaults[option] return value
def gui_main(): init() global config, root_folder, settings, main_window root_folder = os.path.expanduser(config.get("root_folder")) if root_folder[-1] != '/': root_folder += '/' settings = QSettings("org", "edocuments") app = QApplication(sys.argv) main_window = MainWindow() if settings.value("geometry") is not None: main_window.restoreGeometry(settings.value("geometry")) if settings.value("state") is not None: main_window.restoreState(settings.value("state")) main_window.show() app.exec() settings.setValue("geometry", main_window.saveGeometry()) settings.setValue("state", main_window.saveState()) settings.sync()
class L5RCMSettings(object): """A QSettings wrapper for easy access to application settings""" def __init__(self): self._qsettings = QSettings() # Application settings self._app = L5RCMSettings_App(self._qsettings) # UI settings self._ui = L5RCMSettings_UI(self._qsettings) # PC export PDF generation settings self._pc_export = L5RCMSettings_PcExport(self._qsettings) # NPC export PDF generation settings self._npc_export = L5RCMSettings_NpcExport(self._qsettings) @property def app(self): return self._app @property def ui(self): return self._ui @property def pc_export(self): return self._pc_export @property def npc_export(self): return self._npc_export def sync(self): self._qsettings.sync() def load_defaults(self): self._app.load_defaults() self._ui.load_defaults() self._pc_export.load_defaults() self._npc_export.load_defaults()
def get_path_multimarkdown(): settings = QSettings('Pandoc', 'PanConvert') path_multimarkdown = settings.value('path_multimarkdown','') if getattr( sys, 'frozen', False ): if platform.system() == 'Darwin' or os.name == 'posix': path_multimarkdown = which("multimarkdown") settings.setValue('path_multimarkdown', path_multimarkdown) settings.sync() else: args = ['where', 'multimarkdown'] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) path_multimarkdown = str.rstrip(p.communicate(path_multimarkdown.encode('utf-8'))[0].decode('utf-8')) settings.setValue('path_multimarkdown', path_multimarkdown) settings.sync() return path_multimarkdown else: if platform.system() == 'Darwin' or os.name == 'posix': args = ['which', 'multimarkdown'] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) path_multimarkdown = str.rstrip(p.communicate(path_multimarkdown.encode('utf-8'))[0].decode('utf-8')) settings.setValue('path_multimarkdown', path_multimarkdown) settings.sync() return path_multimarkdown elif platform.system() == 'Windows': args = ['where', 'multimarkdown'] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) path_multimarkdown = str.rstrip(p.communicate(path_multimarkdown.encode('utf-8'))[0].decode('utf-8')) settings.setValue('path_multimarkdown', path_multimarkdown) settings.sync() return path_multimarkdown
class Addon(clickpoints.Addon): def __init__(self, *args, **kwargs): clickpoints.Addon.__init__(self, *args, **kwargs) self.frame_number = except_error(self.db.getImageCount, TypeError, print_error=False, return_v=None) # information about the path, image dimensions self.db_info, self.all_frames = get_db_info_for_analysis(self.db) print_db_info(self.db_info) self.res_dict = defaultdict( list) # dictionary that catches all results self.masks = None self.parameter_dict = copy.deepcopy( default_parameters) # loading default parameters self.read_or_set_options( ) # reading the database folder and the previous FEM_mode, or fill in default values self.outfile_path = os.path.join( self.folder, "out.txt") # path for output text file self.config_path = os.path.join(self.folder, "config.yaml") # loading some default settings if os.path.exists(self.config_path): print("found config at", self.config_path) self.parameter_dict = load_config(self.config_path, self.parameter_dict) # QT settings overwrite "default_parameters" # reading Qt settings file # on linux this file is saved at /home/user/.config/pyTFM self.settings = QSettings("pyTFM", "pyTFM") for key in self.parameter_dict.keys(): if not self.settings.value(key) is None: self.parameter_dict[key] = self.settings.value(key) # TODO: consider to save settings in each database separately """ GUI Widgets""" # set the title and layout self.setWindowTitle("pyTFM" + "-" + pyTFM.__version__) self.setWindowIcon(qta.icon("fa.compress")) self.setMinimumWidth(400) self.setMinimumHeight(200) self.layout = QtWidgets.QGridLayout(self) self.layout.setColumnMinimumWidth(0, 150) # button to start calculation self.button_start = QtWidgets.QPushButton("start") self.button_start.clicked.connect( partial(self.start_thread, run_function=self.start)) self.button_start.setToolTip(tooltips["button_start"]) self.layout.addWidget(self.button_start, 0, 0) # button to select images: self.button_select_images = QtWidgets.QPushButton("image selection") self.button_select_images.clicked.connect(self.select_images) self.button_select_images.setToolTip(tooltips["image selection"]) self.sub_layout1 = QtWidgets.QHBoxLayout() self.sub_layout1.addWidget(self.button_select_images) self.sub_layout1.addStretch() self.layout.addLayout(self.sub_layout1, 1, 0) # button to apply drift correction: self.correct_drift = QtWidgets.QPushButton("correct drift") self.correct_drift.clicked.connect( partial(self.start_thread, run_function=self.drift_correction)) self.correct_drift.setToolTip(tooltips["correct drift"]) self.sub_layout2 = QtWidgets.QHBoxLayout() self.sub_layout2.addWidget(self.correct_drift) self.sub_layout2.addStretch() self.layout.addLayout(self.sub_layout2, 2, 0) # check_boxes self.check_box_def = QtWidgets.QCheckBox("deformation") self.check_box_tra = QtWidgets.QCheckBox("traction forces") self.check_box_FEM = QtWidgets.QCheckBox("stress analysis") self.check_box_contract = QtWidgets.QCheckBox("force generation") self.check_box_def.setToolTip(tooltips["check_box_def"]) self.check_box_tra.setToolTip(tooltips["check_box_tra"]) self.check_box_FEM.setToolTip(tooltips["check_box_FEM"]) self.check_box_contract.setToolTip(tooltips["check_box_contract"]) self.layout.addWidget(self.check_box_def, 0, 1) self.layout.addWidget(self.check_box_tra, 1, 1) self.layout.addWidget(self.check_box_FEM, 2, 1) self.layout.addWidget(self.check_box_contract, 3, 1) # a gap self.layout.setRowMinimumHeight(4, 20) # # choosing single or all frames self.analysis_mode = QtWidgets.QComboBox() self.analysis_mode.addItems(["current frame", "all frames"]) self.analysis_mode.setToolTip(tooltips["apply to"]) self.layout.addWidget(self.analysis_mode, 5, 1) self.analysis_mode_descript = QtWidgets.QLabel() self.analysis_mode_descript.setText("apply to") self.layout.addWidget(self.analysis_mode_descript, 5, 0) # parameters self.parameter_labels = [ "Youngs modulus [Pa]", "Poisson's ratio", "pixel size [µm]", "PIV overlap [µm]", "PIV window size [µm]", "gel height [µm]" ] self.param_dict_keys = [ "young", "sigma", "pixelsize", "overlap", "window_size", "h" ] self.parameter_widgets, self.parameter_lables, last_line = add_parameter_from_list( self.parameter_labels, self.param_dict_keys, self.parameter_dict, self.layout, 6, self.parameters_changed) # adding to layout self.setLayout(self.layout) self.parameters_changed() # initialize parameters dict # choosing type of cell system self.colony_type = QtWidgets.QComboBox() self.colony_type.addItems(["colony", "cell layer"]) self.colony_type.setToolTip(tooltips["switch mode"]) self.colony_type.setCurrentIndex(self.parameter_dict["FEM_mode_id"]) # update parameters self.colony_type.currentTextChanged.connect(self.parameters_changed) # switch between cell layer and colony mode self.colony_type.currentTextChanged.connect( self.switch_colony_type_mode) self.parameter_widgets[ "FEM_mode"] = self.colony_type # adding to parameters dict self.parameter_lables["FEM_mode"] = "" # label self.sub_layout3 = QtWidgets.QHBoxLayout() self.sub_layout3.addWidget(self.colony_type) # button for simple segmentation of membranes and cell area self.button_seg = QtWidgets.QPushButton("segmentation") self.button_seg.setToolTip(tooltips["segmentation"]) self.button_seg.clicked.connect(self.segmentation) # update parameters self.sub_layout3.addWidget(self.button_seg) self.sub_layout3.addStretch() self.layout.addLayout(self.sub_layout3, 3, 0) def read_or_set_options(self): self.folder = get_option_wrapper(self.db, "folder", unpack_funct=None, empty_return=lambda: "") if self.folder == "": self.folder = os.getcwd() self.db._AddOption(key="folder", value=self.folder) self.db.setOption(key="folder", value=self.folder) self.parameter_dict["FEM_mode"] = get_option_wrapper( self.db, "FEM_mode", empty_return=lambda: "") if self.parameter_dict["FEM_mode"] == "": self.parameter_dict["FEM_mode"] = default_parameters["FEM_mode"] self.db._AddOption(key="FEM_mode", value=self.parameter_dict["FEM_mode"]) self.db.setOption(key="FEM_mode", value=self.parameter_dict["FEM_mode"]) self.parameter_dict["FEM_mode_id"] = 0 if self.parameter_dict[ "FEM_mode"] == "colony" else 1 def select_images(self): # for what do i need this?? self._new_window = FileSelectWindow(self) self._new_window.show() def reload_all(self): # reloading entire display ## could be optimized sys.stdout = open(os.devnull, 'w') for frame in self.db_info["cbd_frames_ref_dict"].keys(): for layer in self.db_info["layers"]: self.cp.reloadImage(frame_index=frame, layer_id=self.db.getLayer(layer).id) sys.stdout = sys.__stdout__ # reading paramteres and updating the dictionary def parameters_changed(self): self.parameter_dict = read_all_paramters(self.parameter_widgets, self.parameter_dict) # decorator functions to handle diffrent outputs and writing to text file def calculate_general_properties( self, frames): # calculation of colony area, number of cells in colony self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=general_properties, res_dict=self.res_dict, masks=self.masks, frames=frames, db_info=self.db_info) def calculate_deformation(self, frames): # calculation of deformations self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=deformation, res_dict=self.res_dict, masks=self.masks, frames=frames, db_info=self.db_info) def calculate_traction(self, frames): # calculation of traction forces self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=traction_force, res_dict=self.res_dict, masks=self.masks, frames=frames, db_info=self.db_info) def calculate_FEM_analysis( self, frames): # calculation of various stress measures self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=FEM_full_analysis, res_dict=self.res_dict, masks=self.masks, frames=frames, db_info=self.db_info) def calculate_contractile_measures( self, frames): # calculation of contractility and contractile energy self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=get_contractillity_contractile_energy, masks=self.masks, res_dict=self.res_dict, frames=frames, db_info=self.db_info) def set_frames(self): if self.mode == "current frame": # only current frame frames = self.frame if self.mode == "all frames": # all frames frames = self.all_frames return frames def drift_correction( self): # calculation of contractility and contractile energy cdb_frame = self.cp.getCurrentFrame() self.frame = self.db_info["cbd_frames_ref_dict"][cdb_frame] self.mode = self.analysis_mode.currentText( ) # only current frame or all frames frames = self.set_frames() self.db_info, self.masks, self.res_dict = apply_to_frames( self.db, self.parameter_dict, analysis_function=simple_shift_correction, masks=self.masks, res_dict=self.res_dict, frames=frames, db_info=self.db_info) print("calculation complete") def segmentation( self): # calculation of contractility and contractile energy self.cdb_frame = self.cp.getCurrentFrame() self.frame = self.db_info["cbd_frames_ref_dict"][self.cdb_frame] self._new_window_slider = SegSlider(self) self._new_window_slider.show() # switching the type of cell colony def switch_colony_type_mode(self, first=False): setup_mask_wrapper(self, self.db, self.db_info, self.parameter_dict, delete_all=False) self.cp.reloadMaskTypes( ) # reloading mask to display in clickpoints window try: self.db.setOption(key="FEM_mode", value=self.parameter_dict["FEM_mode"]) except KeyError: self.db._AddOption(key="FEM_mode", value=self.parameter_dict["FEM_mode"]) self.db.setOption(key="FEM_mode", value=self.parameter_dict["FEM_mode"]) def start(self): # store parameters in settings for key in self.param_dict_keys: self.settings.setValue(key, self.parameter_dict[key]) self.settings.sync() # workaround because plt.figure() sometimes gets overloaded when other addons are imported if "figures" in list(plt.figure.__globals__.keys()): importlib.reload(plt) # generating objects needed for the following calculation, if they are not exsisting already print_parameters(self.parameter_dict) print("output file: ", self.outfile_path) self.db_info, self.all_frames = get_db_info_for_analysis(self.db) cdb_frame = self.cp.getCurrentFrame() self.frame = self.db_info["cbd_frames_ref_dict"][cdb_frame] self.mode = self.analysis_mode.currentText( ) # only current frame or all frames self.res_dict = defaultdict(lambda: defaultdict(list)) frames = self.set_frames() print("analyzing frames = ", frames) self.masks = cells_masks(frames, self.db, self.db_info, self.parameter_dict) self.outfile_path = write_output_file(self.parameter_dict, "parameters", self.outfile_path, new_file=True) if self.check_box_def.isChecked(): self.calculate_deformation(frames) if self.check_box_tra.isChecked(): self.calculate_traction(frames) self.calculate_general_properties(frames) if self.check_box_FEM.isChecked(): self.calculate_FEM_analysis(frames) if self.check_box_contract.isChecked(): self.calculate_contractile_measures(frames) self.outfile_path = write_output_file( self.res_dict, "results", self.outfile_path, new_file=False) # writing to output file print("calculation complete") # run in a separate thread to keep clickpoints gui responsive // now using QThread and stuff def start_thread(self, run_function=None): self.thread = Worker(self, run_function=run_function) self.thread.start() # starting thread self.thread.finished.connect( self.reload_all) # connecting function on thread finish # x.join() def buttonPressedEvent(self): # show the addon window when the button in ClickPoints is pressed self.show() # disable running in other thread other wise "figsize" is somehow overloaded async def run(self): pass
class DevicesListWidget(QWidget): def __init__(self, parent, *args, **kwargs): super(DevicesListWidget, self).__init__(*args, **kwargs) self.setWindowTitle("Devices list") self.setWindowState(Qt.WindowMaximized) self.setLayout(VLayout(margin=0, spacing=0)) self.mqtt = parent.mqtt self.mdi = parent.mdi self.idx = None self.settings = QSettings() self.hidden_columns = self.settings.value("hidden_columns", [1, 2]) self.tb = Toolbar(Qt.Horizontal, 16, Qt.ToolButtonTextBesideIcon) self.tb.addAction(QIcon("GUI/icons/add.png"), "Add", self.device_add) self.layout().addWidget(self.tb) self.device_list = TableView() self.model = parent.device_model self.telemetry_model = parent.telemetry_model self.sorted_device_model = QSortFilterProxyModel() self.sorted_device_model.setSourceModel(parent.device_model) self.device_list.setModel(self.sorted_device_model) self.device_list.setupColumns(columns, self.hidden_columns) self.device_list.setSortingEnabled(True) self.device_list.setWordWrap(True) self.device_list.setItemDelegate(DeviceDelegate()) self.device_list.sortByColumn(DevMdl.TOPIC, Qt.AscendingOrder) self.device_list.setContextMenuPolicy(Qt.CustomContextMenu) self.layout().addWidget(self.device_list) self.device_list.clicked.connect(self.select_device) self.device_list.doubleClicked.connect(self.device_config) self.device_list.customContextMenuRequested.connect(self.show_list_ctx_menu) self.device_list.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.device_list.horizontalHeader().customContextMenuRequested.connect(self.show_header_ctx_menu) self.ctx_menu = QMenu() self.ctx_menu_relays = None self.create_actions() self.build_header_ctx_menu() def create_actions(self): self.ctx_menu.addAction(QIcon("GUI/icons/configure.png"), "Configure", self.device_config) self.ctx_menu.addAction(QIcon("GUI/icons/delete.png"), "Remove", self.device_delete) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/refresh.png"), "Refresh", self.ctx_menu_refresh) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/on.png"), "Power ON", lambda: self.ctx_menu_power(state="ON")) self.ctx_menu.addAction(QIcon("GUI/icons/off.png"), "Power OFF", lambda: self.ctx_menu_power(state="OFF")) self.ctx_menu_relays = QMenu("Relays") self.ctx_menu_relays.setIcon(QIcon("GUI/icons/switch.png")) relays_btn = self.ctx_menu.addMenu(self.ctx_menu_relays) self.ctx_menu_relays.setEnabled(False) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/clear.png"), "Clear retained", self.ctx_menu_clean_retained) self.ctx_menu.addSeparator() self.ctx_menu_copy = QMenu("Copy") self.ctx_menu_copy.setIcon(QIcon("GUI/icons/copy.png")) copy_btn = self.ctx_menu.addMenu(self.ctx_menu_copy) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/restart.png"), "Restart", self.ctx_menu_restart) self.ctx_menu.addAction(QIcon("GUI/icons/web.png"), "Open WebUI", self.ctx_menu_webui) self.ctx_menu_copy.addAction("IP", lambda: self.ctx_menu_copy_value(DevMdl.IP)) self.ctx_menu_copy.addAction("MAC", lambda: self.ctx_menu_copy_value(DevMdl.MAC)) self.ctx_menu_copy.addAction("BSSID", lambda: self.ctx_menu_copy_value(DevMdl.BSSID)) self.ctx_menu_copy.addSeparator() self.ctx_menu_copy.addAction("Topic", lambda: self.ctx_menu_copy_value(DevMdl.TOPIC)) self.ctx_menu_copy.addAction("FullTopic", lambda: self.ctx_menu_copy_value(DevMdl.FULL_TOPIC)) self.ctx_menu_copy.addAction("STAT topic", lambda: self.ctx_menu_copy_prefix_topic("STAT")) self.ctx_menu_copy.addAction("CMND topic", lambda: self.ctx_menu_copy_prefix_topic("CMND")) self.ctx_menu_copy.addAction("TELE topic", lambda: self.ctx_menu_copy_prefix_topic("TELE")) self.tb.addActions(self.ctx_menu.actions()) self.tb.widgetForAction(relays_btn).setPopupMode(QToolButton.InstantPopup) self.tb.widgetForAction(copy_btn).setPopupMode(QToolButton.InstantPopup) def ctx_menu_copy_value(self, column): if self.idx: row = self.idx.row() value = self.model.data(self.model.index(row, column)) QApplication.clipboard().setText(value) def ctx_menu_copy_prefix_topic(self, prefix): if self.idx: if prefix == "STAT": topic = self.model.statTopic(self.idx) elif prefix == "CMND": topic = self.model.commandTopic(self.idx) elif prefix == "TELE": topic = self.model.teleTopic(self.idx) QApplication.clipboard().setText(topic) def ctx_menu_clean_retained(self): if self.idx: relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()>1): cmnd_topic = self.model.cmndTopic(self.idx) for r in relays.keys(): self.mqtt.publish(cmnd_topic + r, retain=True) QMessageBox.information(self, "Clear retained", "Cleared reatined messages.") def ctx_menu_power(self, relay=None, state=None): if self.idx: relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) cmnd_topic = self.model.commandTopic(self.idx) if relay: self.mqtt.publish(cmnd_topic+relay, payload=state) elif relays: for r in relays.keys(): self.mqtt.publish(cmnd_topic+r, payload=state) def ctx_menu_restart(self): if self.idx: self.mqtt.publish("{}/restart".format(self.model.commandTopic(self.idx)), payload="1") def ctx_menu_refresh(self): if self.idx: for q in initial_queries: self.mqtt.publish("{}/status".format(self.model.commandTopic(self.idx)), payload=q) def ctx_menu_telemetry(self): if self.idx: self.mqtt.publish("{}/status".format(self.model.commandTopic(self.idx)), payload=8) def ctx_menu_bssid(self): if self.idx: bssid = self.model.bssid(self.idx) current = self.settings.value("BSSID/{}".format(bssid), "") alias, ok = QInputDialog.getText(self, "BSSID alias", "Alias for {}. Clear to remove.".format(bssid), text=current) if ok: self.settings.setValue("BSSID/{}".format(bssid), alias) self.model.refreshBSSID() def ctx_menu_webui(self): if self.idx: QDesktopServices.openUrl(QUrl("http://{}".format(self.model.ip(self.idx)))) def show_list_ctx_menu(self, at): self.select_device(self.device_list.indexAt(at)) self.ctx_menu.popup(self.device_list.viewport().mapToGlobal(at)) def build_header_ctx_menu(self): self.hdr_ctx_menu = QMenu() for c in columns.keys(): a = self.hdr_ctx_menu.addAction(columns[c][0]) a.setData(c) a.setCheckable(True) a.setChecked(not self.device_list.isColumnHidden(c)) a.toggled.connect(self.header_ctx_menu_toggle_col) def show_header_ctx_menu(self, at): self.hdr_ctx_menu.popup(self.device_list.horizontalHeader().viewport().mapToGlobal(at)) def header_ctx_menu_toggle_col(self, state): self.device_list.setColumnHidden(self.sender().data(), not state) hidden_columns = [int(c) for c in columns.keys() if self.device_list.isColumnHidden(c)] self.settings.setValue("hidden_columns", hidden_columns) self.settings.sync() def select_device(self, idx): self.idx = self.sorted_device_model.mapToSource(idx) self.device = self.model.data(self.model.index(idx.row(), DevMdl.TOPIC)) relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()) > 1: self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.clear() for r in relays.keys(): actR = self.ctx_menu_relays.addAction("{} ON".format(r)) actR.triggered.connect(lambda st, x=r: self.ctx_menu_power(x, "ON")) actR = self.ctx_menu_relays.addAction("{} OFF".format(r)) actR.triggered.connect(lambda st, x=r: self.ctx_menu_power(x, "OFF")) self.ctx_menu_relays.addSeparator() else: self.ctx_menu_relays.setEnabled(False) self.ctx_menu_relays.clear() def device_config(self, idx=None): dev_cfg = DevicesConfigWidget(self, self.model.topic(self.idx)) self.mdi.addSubWindow(dev_cfg) dev_cfg.setWindowState(Qt.WindowMaximized) def device_add(self): rc = self.model.rowCount() self.model.insertRow(rc) dlg = DeviceEditDialog(self.model, rc) dlg.full_topic.setText("%prefix%/%topic%/") if dlg.exec_() == QDialog.Accepted: self.model.setData(self.model.index(rc, DevMdl.FRIENDLY_NAME), self.model.data(self.model.index(rc, DevMdl.TOPIC))) topic = dlg.topic.text() tele_dev = self.telemetry_model.addDevice(TasmotaDevice, topic) self.telemetry_model.devices[topic] = tele_dev else: self.model.removeRow(rc) def device_delete(self): if self.idx: topic = self.model.topic(self.idx) if QMessageBox.question(self, "Confirm", "Do you want to remove '{}' from devices list?".format(topic)) == QMessageBox.Yes: self.model.removeRows(self.idx.row(),1) tele_idx = self.telemetry_model.devices.get(topic) if tele_idx: self.telemetry_model.removeRows(tele_idx.row(),1) def closeEvent(self, event): event.ignore()
def save_folder_settings(folder_settings): global bot_folder_settings bot_folder_settings = folder_settings settings = QSettings('rlbotgui', 'preferences') settings.setValue(BOT_FOLDER_SETTINGS_KEY, bot_folder_settings) settings.sync()
class SetupDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.settings = QSettings() self._widgets() self._properties() self._layouts() self._connections() def _widgets(self): self.accountnameLabel = QLabel() self.masterkeyLabel = QLabel() self.confirmkeyLabel = QLabel() self.confirmationLabel = QLabel() self.accountnameLineEdit = QLineEdit() self.masterkeyLineEdit = QLineEdit() self.confirmkeyLineEdit = QLineEdit() self.setPushButton = QPushButton('&Set Account') def _properties(self): self.accountnameLabel.setObjectName('accountnameLabel') self.accountnameLabel.setText('Account name:') # Temporary hidden from view, possible usage in the future self.accountnameLabel.setEnabled(False) self.accountnameLineEdit.setEnabled(False) self.accountnameLabel.setVisible(False) self.accountnameLineEdit.setVisible(False) self.masterkeyLabel.setObjectName('masterkeyLabel') self.masterkeyLabel.setText('Master key:') self.masterkeyLineEdit.setObjectName('masterkeyLineEdit') self.masterkeyLineEdit.setPlaceholderText(' something hard to guess') self.masterkeyLineEdit.setEchoMode(QLineEdit.Password) self.confirmkeyLabel.setObjectName('confirmkeyLabel') self.confirmkeyLabel.setText('Confirm master key:') self.confirmkeyLineEdit.setObjectName('confirmkeyLineEdit') self.confirmkeyLineEdit.setPlaceholderText( ' making sure to avoid typos') self.confirmkeyLineEdit.setEchoMode(QLineEdit.Password) self.setPushButton.setObjectName('setPushButton') self.setPushButton.setEnabled(False) self.setWindowTitle('Setup Canda') self.resize(426, 116) def _layouts(self): grid = QGridLayout() grid.addWidget(self.accountnameLabel, 0, 0) grid.addWidget(self.accountnameLineEdit, 0, 1) grid.addWidget(self.masterkeyLabel, 1, 0) grid.addWidget(self.masterkeyLineEdit, 1, 1) grid.addWidget(self.confirmkeyLabel, 2, 0) grid.addWidget(self.confirmkeyLineEdit, 2, 1) grid.addWidget(self.confirmationLabel, 3, 1) row_button = QHBoxLayout() row_button.addStretch() row_button.addWidget(self.setPushButton) col_layout = QVBoxLayout() col_layout.addLayout(grid) col_layout.addLayout(row_button) self.setLayout(col_layout) def _connections(self): self.accountnameLineEdit.textChanged.connect( self.on_LineEdits_textChanged) self.masterkeyLineEdit.textChanged.connect( self.on_LineEdits_textChanged) self.confirmkeyLineEdit.textChanged.connect( self.on_LineEdits_textChanged) self.setPushButton.clicked.connect(self.on_setPushButton_clicked) def on_LineEdits_textChanged(self): # if self.confirmationLabel.text(): self.confirmationLabel.clear() # Enable of disable Set button if either of the three QLineEdits has a values # with_text = [self.accountnameLineEdit.text(), self.masterkeyLineEdit.text(), self.confirmkeyLineEdit.text()] with_text = [ self.masterkeyLineEdit.text(), self.confirmkeyLineEdit.text() ] if all(with_text): self.setPushButton.setEnabled(True) else: self.setPushButton.setEnabled(False) def on_setPushButton_clicked(self): self.check_masterkey_typo() def check_masterkey_typo(self): # Check for master key typos before encrypting it if self.masterkeyLineEdit.text() == self.confirmkeyLineEdit.text(): print( 'password match, you may now proceed in encrypting the master key' ) credentials = self.encrypt_user_credentials() self._write_settings(credentials) self.accept() else: self.confirmationLabel.setText('Master key does not match.') self.masterkeyLineEdit.setFocus(True) self.confirmkeyLineEdit.clear() self.setPushButton.setEnabled(False) def encrypt_user_credentials(self): """ Encrypt user inputted credentials. Return key, salt, token """ return canda.set_masterkey3(self.masterkeyLineEdit.text()) def _write_settings(self, data): """ Save encrypted credentials to QSettings. """ self.settings.setValue('SALT', data[1]) self.settings.setValue('LOGIN_TOKEN', data[2]) self.settings.setValue('INITIAL_RUN', False) self.settings.sync() print('settings save.') def resizeEvent(self, QResizeEvent): print(f'{self.width()} x {self.height()}')
class NodeeraMain(QMainWindow, Ui_NodeeraMain): displayWelcomeMsg = pyqtSignal(str) closeWelcomeMsg = pyqtSignal() """ Create Ui_NodeeraMain and display it. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(NodeeraMain, self).__init__(parent) self.setupUi(self) self.parent = parent # object data self.pageDict = {} self.curPage = None self.helper = Helper() # get startup settings self.initSettings() # launch the welcome wagon self.welcomeDlg = HelloUserDlg(self) self.welcomeDlg.show() QApplication.processEvents() self.displayWelcomeMsg.emit("Starting NodeEra...") # setup stuff not covered by generated code icon = QIcon() icon.addPixmap(QPixmap("icons/RUN and DATA TAB/Disconnected.png"), QIcon.Normal, QIcon.Off) self.actionOpen_Connection.setIcon(icon) # main window self.setTitle("No Connection") self.resize(self.winSize) self.move(self.position) # display welcome message self.displayWelcomeMsg.emit("Welcome To NodeEra") # display announcement message from website try: url = 'https://{}'.format(appPage) response = requests.get(url) if response.status_code == 200: start = response.text.find('class="display"') + 16 end = response.text.find('</p>', start) self.displayWelcomeMsg.emit(response.text[start:end]) self.displayWelcomeMsg.emit( "For more information see www.singerlinks.com/nodeera") else: self.logMsg( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) self.displayWelcomeMsg.emit( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) except Exception as e: self.logMsg("Error accessing announcement page - {}".format( repr(e))) self.displayWelcomeMsg.emit( "Error accessing announcement page - {}".format(repr(e))) # auto open last used connection try: lastNeoConName = self.settings.value("NeoCon/LastUsed") if not lastNeoConName is None: neoDict = self.settings.value( "NeoCon/connection/{}".format(lastNeoConName)) self.openConnection(neoConName=lastNeoConName, neoConDict=neoDict) self.displayWelcomeMsg.emit(" ") self.displayWelcomeMsg.emit( "Opened most recent connection: {}".format(lastNeoConName)) self.setTitle(lastNeoConName) except: self.logMsg( "Unable to open last connection: {}".format(lastNeoConName)) self.displayWelcomeMsg.emit( "Unable to open last connection: {}".format(lastNeoConName)) # auto close the welcome dialog box # time.sleep(5) # self.closeWelcomeMsg.emit() def logMsg(self, msg): if logging: logging.info(msg) def initSettings(self, ): ''' get the system settings needed to start NodeEra. If a system setting doesn't exist then create it with default value - this happens on initial startup ''' self.settings = QSettings() try: self.expirationDate = self.helper.getText( self.settings.value("License/expirationDate")) if self.expirationDate is None: self.expirationDate = 'No expiration date set' self.settings.setValue( "License/expirationDate", self.helper.putText(self.expirationDate)) except: self.expirationDate = 'No expiration date set' self.settings.setValue("License/expirationDate", self.helper.putText(self.expirationDate)) try: self.currentVersion = self.settings.value("License/currentVersion") if self.currentVersion is None: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) elif self.currentVersion != currentVersion: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) except: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) try: self.winSize = self.settings.value("MainWindow/Size") if self.winSize is None: self.winSize = QSize(800, 500) except: self.winSize = QSize(800, 500) try: self.position = self.settings.value("MainWindow/Position") if self.position is None: self.position = QPoint(0, 0) except: self.position = QPoint(0, 0) try: self.recentList = self.settings.value("Default/RecentList") if self.recentList is None: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) except: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) try: self.defaultLoggingPath = self.settings.value( "Default/LoggingPath") if self.defaultLoggingPath is None: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) except: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) try: self.defaultProjPath = self.settings.value("Default/ProjPath") if self.defaultProjPath is None: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) except: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) # default custom formats for diagram objects # Display Format - Instance Node try: test = self.settings.value("Default/Format/InstanceNode") if test is None: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) # Display Format - Instance Relationship try: test = self.settings.value("Default/Format/InstanceRelation") if test is None: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) # Display Format - Template Node try: test = self.settings.value("Default/Format/TemplateNode") if test is None: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) # Display Format - Template Relationship try: test = self.settings.value("Default/Format/TemplateRelation") if test is None: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) # page setup try: test = self.settings.value("Default/PageSetup") if test is None: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) except: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) # default project neocon try: defaultNeoConName = self.settings.value("NeoCon/Default") if defaultNeoConName is None: self.settings.setValue("NeoCon/Default", "LOCAL") except: self.settings.setValue("NeoCon/Default", "LOCAL") # LOCAL neocon definition try: self.localNeoCon = self.settings.value("NeoCon/connection/LOCAL") if self.localNeoCon is None: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) except: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) # default lexer font size try: defaultLexerFontSize = self.settings.value("Lexer/FontSize") if defaultLexerFontSize is None: self.settings.setValue("Lexer/FontSize", "10") except: self.settings.setValue("Lexer/FontSize", "10") # validate all neocons have the prompt dictionary key which was added in 1.04 self.settings.beginGroup("NeoCon/connection") neoKeys = self.settings.childKeys() for key in neoKeys: neoDict = self.settings.value(key) promptVal = neoDict.get("prompt", None) if promptVal is None: neoDict["prompt"] = "False" self.settings.setValue(key, neoDict) self.settings.endGroup() def setTitle(self, filename): self.setWindowTitle("{} - {}".format(productName, filename)) def setMenuAccess(self, ): ''' determine what connection tab is currently selected and enable/disable menu items as needed ''' # turn on all Settings menu items for action in self.menuSettings.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) try: # get tab widget (schema or project) that is currently active - if there isn't one this will cause an exception currentTab = self.stackedPageItems.currentWidget( ).tabPage.currentWidget() # enable all the schema actions for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) for action in self.menuProject.actions(): # enable project actions if type(action) == QAction and not action.isSeparator(): if currentTab.pageType == "PROJECT": action.setEnabled(True) else: if action.text() in [ "New", "Open...", "Recent Projects" ]: action.setEnabled(True) else: action.setEnabled(False) except: # no connection tabs are open # disable all project menu actions for action in self.menuProject.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(False) for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): if action.text() in [ "Close Connection", "Generate Schema..." ]: action.setEnabled(False) else: action.setEnabled(True) ######################################################################## # NEO CONNECTION Dropdown Menu Actions ######################################################################## @pyqtSlot() def on_actionOpen_Connection_triggered(self): """ This slot provides functionality for the open connection button """ d = dlgNeoCons(parent=self) if d.exec_(): if d.selectedNeoConName: # make sure it isn't already opened if d.selectedNeoConName not in self.pageDict: self.openConnection(neoConName=d.selectedNeoConName, neoConDict=d.selectedNeoConDict) else: self.helper.displayErrMsg( "NodeEra - Open Connection", "Connection {} is already open.".format( d.selectedNeoConName)) # switch to the page they tried to open self.pageDict[d.selectedNeoConName].actionButton.trigger() def openConnection(self, neoConName=None, neoConDict=None): ''' User selects a connection to open so create the schema page and display it ''' # set the last used neocon in system settings self.settings.setValue("NeoCon/LastUsed", neoConName) # create a new toolbar button and add it to the toolbar newConAction = QAction(self) newConAction.setObjectName("newConnection") newConAction.setText("Neo4j - {}".format(neoConName)) newConAction.setData(neoConName) newConAction.setToolTip(neoConDict["URL"]) newConAction.setCheckable(True) newConAction.setChecked(True) newConAction.triggered.connect(self.connectionClicked) self.tbConnection.addAction(newConAction) # add a tabPage widget to the stacked widget newPageWidget = PageWidget(parent=self) newPageWidget.pageType = "Schema" widgetIndex = self.stackedPageItems.addWidget(newPageWidget) # save new pageItem newPageItem = PageItem(neoConName=neoConName, actionButton=newConAction, pageWidget=newPageWidget, pageWidgetIndex=widgetIndex) self.pageDict[neoConName] = newPageItem # add the schema tab cypherTab = CypherPageWidget(parent=self, pageItem=newPageItem) newPageWidget.tabPage.addTab(cypherTab, "Schema - {}".format(neoConName)) # click the new action to force selection logic newConAction.trigger() self.logMsg("Open Connection: {}".format(neoConName)) @pyqtSlot() def connectionClicked(self): ''' User clicks on a connection in the menu bar so switch to that stacked widget ''' self.logMsg("connection clicked {}".format(self.sender().text())) # uncheck all the page action buttons for pageName in self.pageDict: self.pageDict[pageName].actionButton.setChecked(False) # check the one just clicked self.sender().setChecked(True) # save the current page name self.curPage = self.sender().data() # switch the stacked page widget to the one just clicked self.stackedPageItems.setCurrentIndex( self.pageDict[self.curPage].pageWidgetIndex) # update the main window title self.setTitle(self.curPage) # adjust the menu items self.setMenuAccess() @pyqtSlot() def on_actionClose_Connection_triggered(self): """ Close the active connection and remove the page from the UI """ if self.curPage: if self.curPage in self.pageDict: # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) self.pageDict[self.curPage].pageWidget.closeSchemaTab() self.removeConnection() def removeConnection(self, ): '''if the tab page widget is responding to the close request it only needs this logic to remove the connection ''' curPage = self.pageDict.get(self.curPage, None) if not curPage is None: # remove the pageWidget from the stacked widget self.stackedPageItems.removeWidget( self.pageDict[self.curPage].pageWidget) del self.pageDict[self.curPage].pageWidget # remove the action from menu's and toolbars self.tbConnection.removeAction( self.pageDict[self.curPage].actionButton) # take the page out of the dictionary del self.pageDict[self.curPage] # if any pages left select the first one if len(self.pageDict) > 0: for pageName in self.pageDict: self.pageDict[pageName].actionButton.trigger() break else: # there are no open connections and the home page is closed self.setTitle("No Connection") @pyqtSlot() def on_actionNeo4j_Connection_Manager_triggered(self): """ Display the Connection Manager """ d = dlgNeoCons(parent=self) if d.exec_(): pass @pyqtSlot() def on_actionExit_triggered(self): """ User selected the File / Exit menu item. Tell all the open connections to close """ self.closeOpenStuff() # close the app self.close() def closeEvent(self, event): # close open connections self.closeOpenStuff() #save the window state self.settings.setValue("MainWindow/Size", self.size()) self.settings.setValue("MainWindow/Position", self.pos()) event.accept() def closeOpenStuff(self): # get a list of all the keys in the pageDict dictionary keys = list(self.pageDict.keys()) # iterate thru the list of dictionary keys. this is required as the dictionary will be changing size, i.e. you can't simply iterate thru the dictionary for key in keys: pageItem = self.pageDict[key] actionButton = pageItem.actionButton actionButton.trigger() # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) pageItem.pageWidget.closeSchemaTab() self.removeConnection() ##################################################################### # SETTINGS DROPDOWNS ##################################################################### @pyqtSlot() def on_actionSystem_Preferences_triggered(self): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ self.editSystemPreferences() def editSystemPreferences(self, ): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ if not (self.settings is None): d = SystemPreferenceBox(self, settings=self.settings) if d.exec_(): self.settings.sync() ##################################################################### # PROJECT METHODS ##################################################################### @pyqtSlot() def on_actionNewProject_triggered(self): """ Open new project """ self.loadProject(fileName=None) @pyqtSlot() def on_actionOpenProject_triggered(self): """ Open an existing project file """ dlg = QFileDialog() dlg.setFileMode(QFileDialog.ExistingFile) dlg.setAcceptMode(QFileDialog.AcceptOpen) dlg.setNameFilters(["NodeEra Project (*.mdl)", "all files (*.*)"]) dlg.setDirectory(self.settings.value("Default/ProjPath")) if dlg.exec_(): fileNames = dlg.selectedFiles() if fileNames: fileName = fileNames[0] self.loadProject(fileName=fileName) @pyqtSlot() def on_actionSaveProject_triggered(self): """ Save the open project """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProject() # if this was an unsaved project then it was really a save as so the tab name has to be updated if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionSaveProjectAs_triggered(self): """ Save Project As """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProjectAs() if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionReverse_Engineer_triggered(self): """ User selects the Reverse Engineer menu item. Display the Reverse Engineer dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().reverseEngineerGraph() else: self.helper.displayErrMsg( "Reverse Engineer", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) @pyqtSlot() def on_actionProjectProperties_triggered(self): """ User selects the project properties menu item. Display the project properties dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().editProjectProperties() else: self.helper.displayErrMsg( "Project Properties", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) def getSchemaObject(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab.schemaModel return None def getSchemaTab(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab return None def loadProject(self, fileName=None): ''' Load the project file with name fileName. If fileName is None, create a new empty project. ''' # create a temporary file name if its a new project if fileName is None: # update unnamed file counter global unNamedFileCounter unNamedFileCounter = unNamedFileCounter + 1 shortName = "{}".format( "New Project-0{}".format(unNamedFileCounter)) else: head, shortName = ntpath.split(QFileInfo(fileName).fileName()) # make sure the project isn't already loaded curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "PROJECT": if (not fileName is None) and (tab.fileName == fileName): self.helper.displayErrMsg( "Open Project", "The project file: {} is already open. It can only be open once." .format(fileName)) return # create the project widget projectTab = ProjectPageWidget(parent=self, settings=self.settings, pageItem=self.pageDict[self.curPage], fileName=fileName) curPageWidget = self.stackedPageItems.currentWidget() # add the project widget as a tab on the current page widget x = curPageWidget.tabPage.addTab( projectTab, "Project: {} - {}".format(shortName, self.pageDict[self.curPage].neoConName)) curPageWidget.tabPage.setCurrentIndex(x) @pyqtSlot() def on_actionOnline_Help_triggered(self): """ User selects the Online Help menu item. Dislay Online Help menu. """ d = OnlineHelpDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionAbout_triggered(self): """ User selects Help / About menu item. Display the about dialog box """ d = HelpAboutDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionGenerate_Schema_triggered(self): """ User requests the generate schema dialog box from main menu """ d = GenerateSchemaDlg(self) if d.exec_(): pass @pyqtSlot() def on_actionForward_Engineer_triggered(self): """ User requests to perform forward engineering from the open project from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().forwardEngineerGraph() else: self.logMsg( "User requested to forward engineer but current tab is not a Project" ) @pyqtSlot() def on_actionGenerate_Reports_triggered(self): """ User selects the Generate Project Reports menu item. Display the Generate Reports dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().generateProjectReports() @pyqtSlot() def on_actionReset_User_Password_triggered(self): """ User requests the change user password from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "CYPHER": curPageWidget.tabPage.currentWidget().resetPassword() @pyqtSlot(QAction) def on_menuRecent_Projects_triggered(self, action): """ User clicked on a recent file menu item @param action DESCRIPTION @type QAction """ self.loadProject(fileName=action.data()) @pyqtSlot() def on_menuRecent_Projects_aboutToShow(self): """ user hovering on recent projects menu item """ recentList = self.settings.value("Default/RecentList") if len(recentList) == 0: return else: # remove any existing actions self.menuRecent_Projects.clear() for projectFile in recentList: # create actions for the recent files aSubAction = self.menuRecent_Projects.addAction(projectFile) aSubAction.setData(projectFile)
class CoreSettings: def __init__(self): self.settings: QSettings = None self.app_name: str = None self.app_dir: Union[Path, Any] = None self.app_data_reader: AppDataReader = None self.app_data_writer: AppDataWriter = None self.app_data_cache: AppDataCache = None self.docs_location: Path = Path( QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)) def init(self): self.app_name = qApp.applicationName().lower() self.app_dir = Path( QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation)) self.app_dir.mkdir(exist_ok=True) settings_file = f"{self.app_name}.ini" self.settings = QSettings( self.app_dir.joinpath(settings_file).as_posix(), QSettings.IniFormat) self.settings.sync() def init_logger(self): log_file = f"{self.app_name}.log" handlers = [ logging.handlers.RotatingFileHandler( self.app_dir.joinpath(log_file), maxBytes=1_000_000, backupCount=1), logging.StreamHandler(), ] logging.basicConfig( handlers=handlers, format="%(asctime)s - %(filename)s:%(lineno)d - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.DEBUG, ) logging.captureWarnings(capture=True) def init_app_data(self): storage = self._db_from_current_user_project() self.app_data_reader = AppDataReader(storage.db) self.app_data_writer = AppDataWriter(storage.db) self.app_data_cache = AppDataCache(self.app_data_reader, self.app_data_writer) def new_app_data(self): """Used when a new project is created/opened and we just wanted to switch underlying db_table from AppDataReader and AppDataWriter without creating a new instance""" storage = self._db_from_current_user_project() self.app_data_reader.ldb = storage.db self.app_data_writer.ldb = storage.db def save_window_state(self, geometry, window_state): self.settings.setValue(GEOMETRY_KEY, geometry) self.settings.setValue(WINDOW_STATE_KEY, window_state) self.settings.sync() def save_configuration(self, app_config: AppConfiguration): self.settings.setValue(STARTUP_CHECK_KEY, app_config.update_check_on_startup) self.settings.setValue(TLS_VERIFICATION_KEY, app_config.tls_verification) self.settings.setValue(HTTP_PROXY_KEY, app_config.http_proxy) self.settings.setValue(HTTPS_PROXY_KEY, app_config.https_proxy) self.settings.setValue(REQUEST_TIMEOUT_SECS, app_config.timeout_in_secs) self.settings.setValue(PRINT_SHARE_SERVER, app_config.print_server) self.settings.sync() def load_configuration(self): app_config = AppConfiguration() app_config.update_check_on_startup = str_to_bool( self.settings.value(STARTUP_CHECK_KEY, AppConfiguration.update_check_on_startup)) app_config.tls_verification = str_to_bool( self.settings.value(TLS_VERIFICATION_KEY, AppConfiguration.tls_verification)) app_config.http_proxy = self.settings.value( HTTP_PROXY_KEY, AppConfiguration.http_proxy) app_config.https_proxy = self.settings.value( HTTPS_PROXY_KEY, AppConfiguration.https_proxy) app_config.timeout_in_secs = self.settings.value( REQUEST_TIMEOUT_SECS, AppConfiguration.timeout_in_secs) app_config.print_server = self.settings.value( PRINT_SHARE_SERVER, AppConfiguration.print_server) return app_config def geometry(self): return self.settings.value(GEOMETRY_KEY, None) def window_state(self): return self.settings.value(WINDOW_STATE_KEY, None) def save_current_project(self, user_project: UserProject): self.settings.setValue(CURRENT_PROJECT_LOCATION_KEY, user_project.location) self.settings.setValue(CURRENT_PROJECT_STATE_KEY, user_project.state) self.settings.sync() def load_current_project(self): current_project_location = self.settings.value( CURRENT_PROJECT_LOCATION_KEY, None) if not current_project_location: return self.create_new_project() return UserProject( location=current_project_location, state=self.settings.value(CURRENT_PROJECT_STATE_KEY), ) def create_new_project(self): user_project = UserProject( location=self.docs_location.joinpath( random_project_name()).as_posix(), state=SavedState.UN_SAVED, ) self.save_current_project(user_project) return user_project def _db_from_current_user_project(self): user_project: UserProject = self.load_current_project() data_location = user_project.location return Storage(data_location)
class RattrapWindow(QMainWindow, Ui_Rattrap): def __init__(self, path, app_name): super().__init__() self.setupUi(self) self.path = path self.app_name = app_name self.settings = QSettings("Asocia", self.app_name) self.ratslap_name = ratslap.RatSlap.name() self.current_mode_name = None self.conn = DBHelper(self.get_path("settings.db")) self.tray_icon = QtWidgets.QSystemTrayIcon(self) self.usb_detector = USBDetector() self.thread = QThread() # Grouping similar items together. self.radio_buttons = [getattr(self, f"mode{i}") for i in "123"] self.combo_boxes = [self.color, self.rate] self.unchangeable_items = [getattr(self, f"dpi{i}") for i in "1234"] + [self.dpi_shift] self.buttons = [self.right, self.middle, self.left] self.buttons.extend( [getattr(self, f"g{str(i)}") for i in range(4, 10)]) self.action_names = ["reset", "import", "export", "apply"] # Enable widgets, set icons for them, create actions self.setup_ui_design() # Connect signals and slots self.setup_ui_logic() skip_test_for_ratslap = False self.permission_granted = True self.mouse_connected = True try: ratslap_path = self.conn.select( "file_paths", ("path", ), program_name="ratslap").fetchone()[0] except OperationalError: ratslap_path = self.get_ratslap_path() else: try: ratslap.RatSlap(ratslap_path, skip_test_for_ratslap) except ratslap.NonValidPathError: ratslap_path = self.get_new_path(ratslap_path) except ratslap.MouseIsOfflineError: self.mouse_connected = False except ratslap.PermissionDeniedError: self.permission_granted = False finally: skip_test_for_ratslap = True self.ratslap = ratslap.RatSlap(ratslap_path, skip_test_for_ratslap) setattr(self.ratslap, 'run', self.catch_exceptions(getattr(self.ratslap, 'run'))) self.mode1.setChecked(True) self.current_mode_is_set = False self.thread.start() def get_path(self, *p): return os.path.join(self.path, *p) def catch_exceptions(self, function): def wrapper(*args, **kwargs): try: result = function(*args, **kwargs) except ratslap.NonValidPathError: self.ratslap.path = self.get_new_path(self.ratslap.path) result = function(*args, **kwargs) except ratslap.PermissionDeniedError as e: self.handle_permission_denied_error(e) result = function(*args, **kwargs) self.permission_granted = True except ratslap.UnknownRatSlapError as e: # Maybe it's because computers are fast sleep(0.1) # Let's wait a bit try: result = function(*args, **kwargs) except Exception as e: text = f"Error message was:\n{str(e)}\n{self.app_name} will now close." self.exec_message_box("An error occured", text, icon_name="Critical") self.quit() else: print( f"An error was occurred but it's gone after 0.1 seconds; " f"time heals everything :) \nIf you are curious, the error was:\n{e}" ) except Exception as e: text = f"Error message was:\n{str(e)}\n{self.app_name} will now close." self.exec_message_box("An error occurred", text, icon_name="Critical") self.quit() return result return wrapper def get_new_path(self, previous_path): text = f"The previous path to '{self.ratslap_name}' program: '{previous_path}' is unreachable. " \ f"If you want to continue using {self.app_name} please specify the path to '{self.ratslap_name}'" self.exec_message_box(f"Unable to reach '{self.ratslap_name}'", text, icon_name="Warning") return self.get_ratslap_path() def setup_ui_design(self): self.setWindowTitle(self.app_name) self.resize(303, 477) self.move(QApplication.desktop().screen().rect().center() - self.rect().center()) minimize_to_tray = self.settings.value("minimize_to_tray_when_closing", True, type=bool) self.action_minimize_to_tray.setChecked(minimize_to_tray) auto_start = self.settings.value("auto_start_on_boot", False, type=bool) auto_start_script_exists = os.path.exists( os.path.join(os.path.expanduser("~"), f".config/autostart/{self.app_name}.desktop")) self.action_autostart.setChecked(auto_start and auto_start_script_exists) for widget in self.buttons + self.radio_buttons + [self.button_apply]: widget.setEnabled(True) self.set_icons_for_widgets() actions = { "show": f"Open {self.app_name}", "hide": "Minimize to tray", "quit": f"Quit {self.app_name}" } self.add_actions_to_tray(actions) self.tray_icon.show() def setup_ui_logic(self): self.bind_functions_to_buttons() self.connect_signals_and_slots_of_thread() self.bind_functions_to_actions() self.action_autostart.toggled.connect( self.create_or_remove_autostart_file) self.action_create_desktop_shortcut.triggered.connect( self.create_desktop_shortcut) def set_icons_for_widgets(self): for action_name in self.action_names: button = getattr(self, f"button_{action_name}") image_path = self.get_path("images", f"{action_name}_icon.png") icon = QIcon() icon.addPixmap(QPixmap(image_path)) button.setIcon(icon) app_icon = QIcon(self.get_path("images", "logo.png")) self.tray_icon.setIcon(app_icon) self.setWindowIcon(app_icon) def add_actions_to_tray(self, actions): tray_menu = QtWidgets.QMenu(self, objectName="tray_menu") for action_name, desc in actions.items(): action = QtWidgets.QAction(desc, self, objectName=action_name) tray_menu.addAction(action) self.tray_icon.setContextMenu(tray_menu) def bind_functions_to_buttons(self): for radio_btn in self.radio_buttons: radio_btn.clicked.connect(self.set_current_mode) for combo_box in self.combo_boxes: combo_box.currentTextChanged.connect(self.combo_box_changed) for button in self.buttons: button.clicked.connect(self.assign_shortcut) for name in self.action_names: button = getattr(self, f"button_{name}") function = getattr(self, f"on_{name}") button.clicked.connect(function) def bind_functions_to_actions(self): for action in self.tray_icon.contextMenu().actions(): action_name = action.objectName() action.triggered.connect(getattr(self, action_name)) def on_reset(self): for i in range(3, 6): title = f"Reset F{i}?" text = f"Do you want to reset profile F{i} to its defaults?" response = self.exec_message_box( title, text, ["No", "NoToAll", "YesToAll", "Yes"], "Question", special_buttons={ "EscapeButton": 2, "DefaultButton": 4 }) if response == "YesToAll": self.ratslap.reset("all") self.conn.drop_table("profiles") self.set_current_mode() break elif response == "Yes": self.ratslap.reset(i) self.conn.delete_row("profiles", name=f"f{str(i)}") self.set_current_mode() elif response == "NoToAll": break def on_import(self): path, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Import from file", self.path, f"All files (*);;{self.app_name} files (*.rat)", f"{self.app_name} files (*.rat)") if path: with open(path) as f: for i in range(3, 6): self.ratslap.modify(i, **loads(f.readline())) self.conn.delete_row("profiles", name=f"f{str(i)}") self.set_current_mode() def on_export(self): path, _ = QtWidgets.QFileDialog.getSaveFileName( self, "Export to a file") if path: with open(f"{path}.rat", "w") as f: for i in range(3, 6): dump(self.ratslap.parse_mode(i), f) f.write("\n") def on_apply(self): for mode in ["f3", "f4", "f5"]: data = self.conn.select("profiles", "*", name=mode).fetchone() if data: cols = self.conn.get_column_names("profiles") changes = dict(zip(cols, data)) self.ratslap.modify(mode, **changes) def assign_shortcut(self): button = self.sender() command_editor = CommandEditor(button, self) command_editor.setWindowModality(Qt.ApplicationModal) command_editor.show() def get_ratslap_path(self): path_valid, first_try = False, True path = None while not path_valid: if not first_try: title = "Non valid path" text = "The path you specified is not valid. Try again" self.exec_message_box(title, text) caption = f"Select the path to the '{self.ratslap_name}' program..." path, _ = QtWidgets.QFileDialog.getOpenFileName(self, caption) if path: try: ratslap.RatSlap(path) except ratslap.NonValidPathError: first_try = False except (ratslap.MouseIsOfflineError, ratslap.PermissionDeniedError, ratslap.UnknownRatSlapError): path_valid = True else: path_valid = True else: exit() self.conn.drop_table("file_paths") self.conn.create_table("file_paths", ["program_name", "path"]) self.conn.insert_values("file_paths", **{ "program_name": "ratslap", "path": path }) return path def connect_signals_and_slots_of_thread(self): self.usb_detector.mouse_state_changed.connect(self.toggle_ui_state) self.usb_detector.moveToThread(self.thread) self.thread.started.connect(self.usb_detector.work) def toggle_ui_state(self, mouse_online): self.mouse_connected = mouse_online mouse_offline_message_box = self.findChild( QtWidgets.QMessageBox, "mouse_offline_message_box") if mouse_online: self.tray_icon.setToolTip(f"{self.app_name}\nMouse is online") if mouse_offline_message_box is not None: mouse_offline_message_box.hide() if self.isVisible(): try: self.set_current_mode() self.permission_granted = True self.current_mode_is_set = True except ratslap.PermissionDeniedError: self.permission_granted = False except ratslap.MouseIsOfflineError: pass for radio_btn in self.radio_buttons: if not radio_btn.isChecked(): radio_btn.setEnabled(mouse_online and self.permission_granted) for name in self.action_names: button = getattr(self, f"button_{name}") button.setEnabled(mouse_online and self.permission_granted) if mouse_online and self.isHidden(): self.show_tray_message("Mouse connected") elif not mouse_online: self.tray_icon.setToolTip(f"{self.app_name}\nMouse is offline") if self.isVisible(): text = f"Please plug in your Logitech G300s mouse to continue using {self.app_name}" if mouse_offline_message_box is not None: mouse_offline_message_box.show() else: self.exec_message_box( "Failed to find Logitech G300s", text, ["Ok"], "Information", objectName="mouse_offline_message_box") else: if self.current_mode_is_set: self.show_tray_message("Mouse disconnected") else: self.show_tray_message("Failed to find Logitech G300s") def handle_permission_denied_error(self, e): self.permission_granted = False error = ratslap.Error(e, self.ratslap.path) title = error.get_name() text = "You do not have write access to the mouse." informative_text = f"Would you like {self.app_name} to help you with this?" details = error.get_full_error_message() response = self.exec_message_box(title, text, icon_name="Warning", button_names=["Yes", "No"], special_buttons={ "DefaultButton": 1, "EscapeButton": 2 }, informativeText=informative_text, detailedText=details) if response == "Yes": file_name = "10-ratslap.rules" self.create_udev_rule_for_ratslap(file_name) self.prompt_user_to_move_the_file(file_name) self.exec_message_box( "Replug your mouse", "If you have done with moving the file and reloading" " the rules, plug out your mouse and replug it for " "changes to take the effect.") def prompt_user_to_move_the_file(self, file_name): title = "Move the file and reload the rules" text = f'A file named "{file_name}" created under {self.path}\n\n' \ f'Move it to the path: /etc/udev/rules.d/ \n' \ f'and then reload udevadm rules.' informative_text = "If you don't know how to do it, press Show Details button." details = f"Run the command below on terminal and then press Ok\n\n" \ f"sudo mv {self.get_path(file_name)} /etc/udev/rules.d && " \ f"sudo udevadm control --reload-rules" self.exec_message_box(title, text, informativeText=informative_text, detailedText=details) def set_current_mode(self): current_mode_index = [i.isChecked() for i in self.radio_buttons].index(True) + 3 self.current_mode_name = "f" + str(current_mode_index) # f3, f4 or f5 self.ratslap.select_mode(self.current_mode_name) current_mode = self.get_mode(self.current_mode_name) for item in self.combo_boxes + self.unchangeable_items + self.buttons: name = item.objectName() try: item.setText(current_mode[name]) except AttributeError: item.setCurrentText(current_mode[name].title()) def combo_box_changed(self, value): property_name = self.sender().objectName() self.conn.update_value("profiles", property_name, value.lower(), name=self.current_mode_name) def get_mode(self, mode): column_names = self.conn.get_column_names("defaults") self.conn.create_table("profiles", column_names) data = self.conn.select("profiles", "*", name=mode).fetchone() if data: return dict(zip(column_names, data)) else: profile = self.ratslap.parse_mode(mode) self.conn.insert_values("profiles", **profile) return self.get_mode(mode) def show_tray_message(self, message, title=None): if title is None: title = self.app_name self.tray_icon.showMessage(title, message, QIcon(self.get_path("images", "logo.png")), 1000) def exec_message_box(self, title, text, button_names=None, icon_name=None, special_buttons=None, **kwargs): msg_box = QtWidgets.QMessageBox(self, **kwargs) if not button_names: button_names = ["Ok"] for button_name in button_names: try: msg_box.addButton(getattr(QtWidgets.QMessageBox, button_name)) except AttributeError as e: print(e, file=sys.stderr) if icon_name: icon = getattr(QtWidgets.QMessageBox, icon_name) else: icon = QtWidgets.QMessageBox.Information msg_box.setIcon(icon) msg_box.setWindowTitle(title) msg_box.setText(text) if special_buttons is not None: for button_name, index in special_buttons.items(): method = getattr(msg_box, f"set{button_name}") button = getattr(msg_box, button_names[index - 1]) method(button) response = msg_box.exec() for button_name in button_names: if response == getattr(msg_box, button_name): return button_name def create_udev_rule_for_ratslap(self, file_name): with open(self.get_path(file_name), "w") as f: rule = 'DRIVER=="usb", ' \ 'ATTR{idProduct}=="c246", ' \ 'ATTR{idVendor}=="046d", ' \ 'ATTR{product}=="G300s Optical Gaming Mouse", ' \ 'MODE="0666", ' \ 'GROUP="%s"' % (os.getenv("USER")) f.write(rule) def create_or_remove_autostart_file(self): path = self.get_path(os.path.expanduser("~"), ".config/autostart/") file_name = f"{self.app_name}.desktop" if self.action_autostart.isChecked(): if os.path.exists(path): option = "--run-in-background" self.create_dot_desktop_file( file_name, path, f"Autostart {self.app_name} on startup", option) else: self.exec_message_box( "Sorry", "We do not know how to perform this operation on your system.", icon_name="Information") self.action_autostart.setChecked(False) else: if os.path.exists(self.get_path(path, file_name)): os.remove(self.get_path(path, file_name)) def create_desktop_shortcut(self): path = self.get_path(os.path.expanduser("~"), "Desktop") file_name = f"{self.app_name}.desktop" if os.path.exists(os.path.join(path, file_name)): self.exec_message_box("Info", "Desktop shortcut already exists") else: self.create_dot_desktop_file(file_name, path, f"Launch {self.app_name}") self.exec_message_box( "Info", f"A file named {file_name} created on your desktop. " f"Right click on it and follow\n" f"Properties->Permissions->Allow executing file as program") def create_dot_desktop_file(self, file_name, path, comment, run_in_background=""): with open(os.path.join(path, file_name), "w") as f: f.write( f"[Desktop Entry]\n" f"Version=1.0\n" f"Type=Application\n" f"Exec={sys.executable} {self.get_path('main.py')} {run_in_background}\n" f"Hidden=false\n" f"NoDisplay=false\n" f"X-GNOME-Autostart-enabled=true\n" f"Name[en]={self.app_name}\n" f"Name={self.app_name}\n" f"Comment[en]={comment}\n" f"Comment={comment}\n" f"Icon={self.get_path('images', 'logo.png')}\n") def save_settings(self): minimize_to_tray = self.action_minimize_to_tray.isChecked() self.settings.setValue("minimize_to_tray_when_closing", minimize_to_tray) auto_start = self.action_autostart.isChecked() self.settings.setValue("auto_start_on_boot", auto_start) self.settings.sync() def show(self): super().show() self.toggle_ui_state(self.mouse_connected) def _set_visible(self, visible): if visible: self.show() else: super().setVisible(False) def closeEvent(self, e): if not self.action_minimize_to_tray.isChecked(): self.quit() else: e.ignore() self.hide() self.show_tray_message(f"{self.app_name} was minimized to tray") def quit(self): self.save_settings() self.conn.close() QtWidgets.qApp.quit()
class Settings(QDialog, Ui_Settings): """Settings dialog""" def __init__(self, parent): QDialog.__init__(self, parent) self.parent = parent self.setupUi(self) self.core = None self.plugins = [] self.emumode = [] self.combomap = {} self.qset = QSettings("m64py", "m64py") self.qset.setDefaultFormat(QSettings.IniFormat) self.add_items() self.connect_signals() def showEvent(self, event): self.set_config() def closeEvent(self, event): self.save_config() def add_items(self): self.combomap = { M64PLUGIN_RSP: ( self.comboRSP, self.pushButtonRSP, Plugin(self.parent)), M64PLUGIN_GFX: ( self.comboVideo, self.pushButtonVideo, Plugin(self.parent)), M64PLUGIN_AUDIO: ( self.comboAudio, self.pushButtonAudio, Plugin(self.parent)), M64PLUGIN_INPUT: ( self.comboInput, self.pushButtonInput, Input(self.parent)) } self.emumode = [ QRadioButton(self.tr("Pure Interpreter")), QRadioButton(self.tr("Cached Interpreter")), QRadioButton(self.tr("Dynamic Recompiler")) ] vbox = QVBoxLayout(self.groupEmuMode) for widget in self.emumode: vbox.addWidget(widget) def show_page(self, index=0): self.tabWidget.setCurrentIndex(index) self.show() def save_config(self): self.save_paths() self.save_plugins() if self.core and self.core.get_handle(): self.save_video() self.save_core() self.core.config.save_file() self.qset.sync() def set_config(self): if self.core and self.core.get_handle(): self.set_paths() self.set_plugins() self.set_video() self.set_core() def on_vidext_changed(self, state): self.parent.vidext = state self.comboResolution.setEnabled(not self.parent.vidext) self.checkFullscreen.setEnabled(not self.parent.vidext) self.parent.worker.quit() self.parent.worker.init() def connect_signals(self): self.browseLibrary.clicked.connect(lambda: self.browse_dialog( (self.pathLibrary, self.groupLibrary, False))) self.browsePlugins.clicked.connect(lambda: self.browse_dialog( (self.pathPlugins, self.groupPlugins, True))) self.browseData.clicked.connect(lambda: self.browse_dialog( (self.pathData, self.groupData, True))) self.browseROM.clicked.connect(lambda: self.browse_dialog( (self.pathROM, self.groupROM, True))) self.checkEnableVidExt.clicked.connect(self.on_vidext_changed) for plugin_type in self.combomap: self.connect_combo_signals(self.combomap[plugin_type]) def connect_combo_signals(self, combomap): combo, button, settings = combomap if settings is not None: if combo != self.comboInput: combo.activated.connect( lambda: self.set_section(combo, button, settings)) button.clicked.connect(settings.show_dialog) def browse_dialog(self, args): widget, groupbox, directory = args dialog = QFileDialog() if directory: dialog.setFileMode(QFileDialog.Directory) path = dialog.getExistingDirectory( self, groupbox.title(), widget.text(), QFileDialog.ShowDirsOnly) else: dialog.setFileMode(QFileDialog.ExistingFile) path, _ = dialog.getOpenFileName( self, groupbox.title(), widget.text(), "%s (*%s);;All files (*)" % (groupbox.title(), DLL_FILTER)) if not path: return widget.setText(path) if widget == self.pathLibrary: self.parent.worker.quit() if not self.parent.worker.core.get_handle(): self.parent.worker.init(path) if self.parent.worker.core.get_handle(): self.core = self.parent.worker.core self.set_core() self.set_video() self.parent.window_size_triggered(self.get_size_safe()) self.parent.state_changed.emit((True, False, False, False)) elif widget == self.pathPlugins: self.parent.worker.plugins_shutdown() self.parent.worker.plugins_unload() self.parent.worker.plugins_load(path) self.parent.worker.plugins_startup() self.set_plugins() def get_int_safe(self, key, default): try: return int(self.qset.value(key, default)) except ValueError: return default def get_size_safe(self): try: size = self.qset.value("size", SIZE_1X) except TypeError: size = SIZE_1X if not type(size) == tuple: size = SIZE_1X if len(size) != 2: size = SIZE_1X if type(size[0]) != int or type(size[1]) != int: size = SIZE_1X if size[0] <= 0 or size[1] <= 0: size = SIZE_1X return size def get_section(self, combo): plugin = combo.currentText() index = combo.findText(plugin) desc = combo.itemData(index) name = os.path.splitext(plugin)[0][12:] section = "-".join([n.capitalize() for n in name.split("-")[0:2]]) return section, desc def set_section(self, combo, button, settings): if settings: if combo != self.comboInput: section, desc = self.get_section(combo) settings.set_section(section, desc) self.core.config.open_section(section) items = self.core.config.parameters[ self.core.config.section].items() if items: button.setEnabled(True) else: button.setEnabled(False) else: button.setEnabled(True) else: button.setEnabled(False) def set_paths(self): path_library = self.qset.value( "Paths/Library", find_library(CORE_NAME)) path_data = self.qset.value( "Paths/Data", self.core.config.get_path("SharedData")) path_roms = self.qset.value("Paths/ROM") try: path_plugins = self.qset.value("Paths/Plugins", os.path.realpath( os.path.dirname(self.parent.worker.plugin_files[0]))) except IndexError: path_plugins = "" try: self.pathROM.setText(path_roms) except TypeError: pass self.pathLibrary.setText(path_library) self.pathPlugins.setText(path_plugins) self.pathData.setText(path_data) def set_video(self): self.core.config.open_section("Video-General") self.set_resolution() self.checkEnableVidExt.setChecked( bool(self.get_int_safe("enable_vidext", 1))) self.checkFullscreen.setChecked( bool(self.core.config.get_parameter("Fullscreen"))) self.checkFullscreen.setEnabled(not self.parent.vidext) self.checkVsync.setChecked( bool(self.core.config.get_parameter("VerticalSync"))) self.checkVsync.setToolTip( self.core.config.get_parameter_help("VerticalSync").decode()) if sys.platform == "win32": self.checkKeepAspect.setChecked(False) self.checkKeepAspect.setEnabled(False) else: keep_aspect = bool(self.get_int_safe("keep_aspect", 1)) self.checkKeepAspect.setChecked(keep_aspect) disable_screensaver = bool(self.get_int_safe("disable_screensaver", 1)) self.checkDisableScreenSaver.setChecked(disable_screensaver) def set_core(self): self.core.config.open_section("Core") mode = self.core.config.get_parameter("R4300Emulator") self.emumode[mode].setChecked(True) self.checkOSD.setChecked( self.core.config.get_parameter("OnScreenDisplay")) self.checkOSD.setToolTip( self.core.config.get_parameter_help("OnScreenDisplay").decode()) self.checkNoCompiledJump.setChecked( self.core.config.get_parameter("NoCompiledJump")) self.checkNoCompiledJump.setToolTip( self.core.config.get_parameter_help("NoCompiledJump").decode()) self.checkDisableExtraMem.setChecked( self.core.config.get_parameter("DisableExtraMem")) self.checkDisableExtraMem.setToolTip( self.core.config.get_parameter_help("DisableExtraMem").decode()) delay_si = self.core.config.get_parameter("DelaySI") if delay_si is not None: self.checkDelaySI.setChecked(delay_si) else: self.checkDelaySI.setChecked(False) self.checkDelaySI.setEnabled(False) self.checkDelaySI.setToolTip( self.core.config.get_parameter_help("DelaySI").decode()) count_per_op = self.core.config.get_parameter("CountPerOp") if count_per_op is not None: self.comboCountPerOp.setCurrentIndex(count_per_op) else: self.comboCountPerOp.setEnabled(False) self.comboCountPerOp.setToolTip( self.core.config.get_parameter_help("CountPerOp").decode()) def set_plugins(self): plugin_map = self.core.plugin_map for plugin_type in self.combomap: combo, button, settings = self.combomap[plugin_type] combo.clear() for plugin in plugin_map[plugin_type].values(): (plugin_handle, plugin_path, plugin_name, plugin_desc, plugin_version) = plugin name = os.path.basename(plugin_path) combo.addItem(name) index = combo.findText(str(name)) combo.setItemData(index, plugin_desc) combo.setItemData(index, plugin_desc, Qt.ToolTipRole) current = self.qset.value("Plugins/%s" % ( PLUGIN_NAME[plugin_type]), PLUGIN_DEFAULT[plugin_type]) index = combo.findText(current) if index == -1: index = 0 combo.setCurrentIndex(index) self.set_section(combo, button, settings) def set_resolution(self): width = self.core.config.get_parameter("ScreenWidth") height = self.core.config.get_parameter("ScreenHeight") if (width, height) not in MODES: MODES.append((width, height)) self.comboResolution.clear() for mode in MODES: w, h = mode self.comboResolution.addItem( "%sx%s" % (w, h), (w, h)) index = self.comboResolution.findText( "%sx%s" % (width, height), Qt.MatchExactly) if index == -1: index = 0 self.comboResolution.setCurrentIndex(index) self.comboResolution.setEnabled(not self.parent.vidext) def save_paths(self): self.qset.setValue("Paths/Library", self.pathLibrary.text()) self.qset.setValue("Paths/Plugins", self.pathPlugins.text()) self.qset.setValue("Paths/Data", self.pathData.text()) self.qset.setValue("Paths/ROM", self.pathROM.text()) def save_video(self): self.core.config.open_section("Video-General") if self.parent.vidext: width, height = self.get_size_safe() else: width, height = self.comboResolution.currentText().split("x") self.core.config.set_parameter("ScreenWidth", int(width)) self.core.config.set_parameter("ScreenHeight", int(height)) self.core.config.set_parameter("Fullscreen", self.checkFullscreen.isChecked()) self.core.config.set_parameter("VerticalSync", self.checkVsync.isChecked()) self.qset.setValue("keep_aspect", int(self.checkKeepAspect.isChecked())) self.qset.setValue("disable_screensaver", int(self.checkDisableScreenSaver.isChecked())) self.qset.setValue("enable_vidext", int(self.checkEnableVidExt.isChecked())) def save_core(self): self.core.config.open_section("Core") emumode = [n for n,m in enumerate(self.emumode) if m.isChecked()][0] self.core.config.set_parameter("R4300Emulator", emumode) self.core.config.set_parameter("OnScreenDisplay", self.checkOSD.isChecked()) self.core.config.set_parameter("NoCompiledJump", self.checkNoCompiledJump.isChecked()) self.core.config.set_parameter("DisableExtraMem", self.checkDisableExtraMem.isChecked()) self.core.config.set_parameter("DelaySI", self.checkDelaySI.isChecked()) self.core.config.set_parameter("CountPerOp", self.comboCountPerOp.currentIndex()) self.core.config.set_parameter("SharedDataPath", self.pathData.text().encode()) def save_plugins(self): for plugin_type in self.combomap: combo, button, settings = self.combomap[plugin_type] self.qset.setValue("Plugins/%s" % PLUGIN_NAME[plugin_type], combo.currentText())
class TasmotaDevicesModel(QAbstractTableModel): def __init__(self, tasmota_env): super().__init__() self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat) self.tasmota_env = tasmota_env self.columns = [] for d in self.tasmota_env.devices: d.property_changed = self.notify_change d.module_changed = self.module_change def setupColumns(self, columns): self.beginResetModel() self.columns = columns self.endResetModel() def deviceAtRow(self, row): if len(self.tasmota_env.devices) > 0: return self.tasmota_env.devices[row] return None def notify_change(self, d, key): row = self.tasmota_env.devices.index(d) if key.startswith("POWER") and "Power" in self.columns: power_idx = self.columns.index("Power") idx = self.index(row, power_idx) self.dataChanged.emit(idx, idx) elif key in ("RSSI", "LWT"): fname_idx = self.columns.index("FriendlyName") idx = self.index(row, fname_idx) self.dataChanged.emit(idx, idx) elif key in self.columns: col = self.columns.index(key) idx = self.index(row, col) self.dataChanged.emit(idx, idx) def module_change(self, d): self.notify_change(d, "Module") def columnCount(self, parent=None): return len(self.columns) def rowCount(self, parent=None): return len(self.tasmota_env.devices) def flags(self, idx): return Qt.ItemIsSelectable | Qt.ItemIsEnabled def headerData(self, col, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.columns[col] def data(self, idx, role=Qt.DisplayRole): if idx.isValid(): row = idx.row() col = idx.column() col_name = self.columns[col] d = self.tasmota_env.devices[row] if role in [Qt.DisplayRole, Qt.EditRole]: val = d.p.get(col_name, "") if col_name == "FriendlyName": val = d.p.get("FriendlyName1", d.p['Topic']) elif col_name == "Module": if val == 0: return d.p['Template'].get('NAME', "Fetching template name...") else: return d.module() elif col_name == "Version" and val: if "(" in val: return val[0:val.index("(")] return val elif col_name in ("Uptime", "Downtime") and val: if val.startswith("0T"): val = val.replace('0T', '') val = val.replace('T', 'd ') elif col_name == "Core" and val: return val.replace('_', '.') elif col_name == "Time" and val: return val.replace('T', ' ') elif col_name == "Power": return d.power() elif col_name == "Color": return d.color() elif col_name == "CommandTopic": return d.cmnd_topic() elif col_name == "StatTopic": return d.stat_topic() elif col_name == "TeleTopic": return d.tele_topic() elif col_name == "FallbackTopic": return "cmnd/{}_fb/".format(d.p.get('MqttClient')) elif col_name == "BSSId": alias = self.settings.value("BSSId/{}".format(val)) if alias: return alias return val elif role == LWTRole: val = d.p.get('LWT', 'Offline') return val elif role == RestartReasonRole: val = d.p.get('RestartReason') return val elif role == RSSIRole: val = d.p.get('RSSI', 0) return val elif role == FirmwareRole: val = d.p.get('Version', "") return val elif role == Qt.TextAlignmentRole: # Left-aligned columns if col_name in ("FriendlyName", "Module", "RestartReason", "OtaUrl", "Hostname") or col_name.endswith("Topic"): return Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap # Right-aligned columns elif col_name in ("Uptime"): return Qt.AlignRight | Qt.AlignVCenter else: return Qt.AlignCenter elif role == Qt.DecorationRole and col_name == "FriendlyName": if d.p['LWT'] == "Online": rssi = int(d.p.get("RSSI", 0)) if rssi > 0 and rssi < 50: return QIcon("GUI/icons/status_low.png") elif rssi < 75: return QIcon("GUI/icons/status_medium.png") elif rssi >= 75: return QIcon("GUI/icons/status_high.png") return QIcon("GUI/icons/status_offline.png") elif role == Qt.InitialSortOrderRole: if col_name in ("Uptime", "Downtime"): val = d.p.get(col_name, "") if val: d, hms = val.split("T") h, m, s = hms.split(":") return int(s) + int(m) * 60 + int(h) * 3600 + int(d) * 86400 else: return idx.data() elif role == Qt.ToolTipRole: if col_name == "Version": val = d.p.get('Version') if val: return val[val.index("(")+1:val.index(")")] return "" elif col_name == "BSSId": return d.p.get('BSSId') elif col_name == "FriendlyName": fns = [d.p['FriendlyName1']] for i in range(2, 5): fn = d.p.get("FriendlyName{}".format(i)) if fn: fns.append(fn) return "\n".join(fns) def addDevice(self, device): self.beginInsertRows(QModelIndex(), 0, 0) device.property_changed = self.notify_change device.module_changed = self.module_change self.endInsertRows() def removeRows(self, pos, rows, parent=QModelIndex()): if pos + rows <= self.rowCount(): self.beginRemoveRows(parent, pos, pos + rows - 1) device = self.deviceAtRow(pos) self.tasmota_env.devices.pop(self.tasmota_env.devices.index(device)) topic = device.p['Topic'] self.settings.beginGroup("Devices") if topic in self.settings.childGroups(): self.settings.remove(topic) self.settings.endGroup() self.endRemoveRows() return True return False def deleteDevice(self, idx): row = idx.row() mac = self.deviceAtRow(row).p['Mac'].replace(":", "-") self.devices.remove(mac) self.devices.sync() self.removeRows(row, 1) def columnIndex(self, column): return self.columns.index(column)
class AppSettings: """ Manages application settings. To access settings through this class, make sure the settings key and default value are registered in known_settings. """ def __init__(self, settings_file=None): """ Load either the user specific settings or, if a file name is provided, specific settings from that file. """ self._settings_file = settings_file self._logger = logging.getLogger(__name__) if settings_file is None: self._settings = QSettings() else: self._logger.info('Loading settings from ' + settings_file) self._settings = QSettings(settings_file, QSettings.IniFormat) @property def settings(self): """ Provides direct access to the underlying QSettings that are used to read/store settings values. """ return self._settings def date_value(self, key): """ Reads the string value in *key* and tries to decode it into a datetime object. The string value is expected to have the format '2014-12-24 18:00:00' """ date_str = self.value(key) if date_str is None: return None try: date = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S') except ValueError: self._logger.error(date_str + ' could not be decoded. Ignoring.') return None else: return date def value(self, key): """ Returns the value that is stored for key or the default value if no value is stored. If the key is not known, the function will throw an exception. """ if key not in known_settings.keys(): raise Exception(key + ' is not a known registered setting') default = known_settings[key] if isinstance(default, collections.Container) or default is None: return self._settings.value(key, defaultValue=default) else: return self._settings.value(key, defaultValue=default, type=type(default)) def set_value(self, key, value): """ Sets the value for key If the key is not known, the function will throw an exception. """ if key not in known_settings.keys(): raise Exception(key + ' is not a known registered setting') return self._settings.setValue(key, value) def register_default_settings(self): """ Writes the default value for each setting to the settings file """ self._logger.info('Loading default settings') for key, value in known_settings.items(): self._settings.setValue(key, value) self._settings.sync()
class CoverletterCreator(QtWidgets.QMainWindow, mainWindow.Ui_MainWindow): def __init__(self, parent=None): super(CoverletterCreator, self).__init__(parent) self.setupUi(self) self.mainTitle = "Coverletter Creator" self.config = QSettings() self.settings = SettingsHandler(parent=self, settings=self.config) self.clipboard = QtWidgets.QApplication.clipboard() self.actionNew.triggered.connect(self.new_project) self.actionSave.triggered.connect(self.save_project) self.actionSave_As.triggered.connect(self.saveas_project) self.actionOpen.triggered.connect(self.open_project) self.actionExit.triggered.connect(self.close) self.actionSettings.triggered.connect(self.settings.show) # Set default values self.filename = "Examples/example_project.xml" self.file_dirty = False self.readSettings() self.load_file(self.filename) self.pb_browsePhoto.clicked.connect(self.browse_photo) self.pb_generatePdf.clicked.connect(self.generate_pdf) self.pb_generateText.clicked.connect(self.generate_text) self.connect_all_fields() self.connect_mandatory_fields() # Connect all labels to click handler for child in self.centralwidget.findChildren(QtWidgets.QLabel): child.mousePressEvent = functools.partial(self.label_clicked, source=child) for child in self.centralwidget.findChildren(QtWidgets.QCheckBox): child.mousePressEvent = functools.partial(self.checkbox_clicked, source=child) self.RECEIPIENTGENDER.mousePressEvent = functools.partial( self.combobox_clicked, source=self.RECEIPIENTGENDER) self.RECEIPIENTSALUTATION.mousePressEvent = functools.partial( self.combobox_clicked, source=self.RECEIPIENTSALUTATION) self.COMPANYNAME.editingFinished.connect( lambda: self.COMPANYSHORTNAME.setText(self.COMPANYNAME.text())) def connect_all_fields(self): for child in self.centralwidget.findChildren(QtWidgets.QLineEdit): child.textChanged.connect(self.setWindowTitleUnsaved) for child in self.centralwidget.findChildren(QtWidgets.QPlainTextEdit): child.textChanged.connect(self.setWindowTitleUnsaved) for child in self.centralwidget.findChildren(QtWidgets.QCheckBox): child.clicked.connect(self.setWindowTitleUnsaved) for child in self.centralwidget.findChildren(SpellTextEdit): child.textChanged.connect(self.setWindowTitleUnsaved) for child in self.centralwidget.findChildren(QtWidgets.QComboBox): child.currentIndexChanged.connect(self.setWindowTitleUnsaved) def connect_mandatory_fields(self): mandatory_fields_list = [ self.FIRSTNAME, self.LASTNAME, self.MOBILE, self.EMAIL, self.COMPANYNAME ] for textBox in mandatory_fields_list: textBox.textChanged[str].connect( lambda: self.pb_generatePdf.setEnabled(textBox.text() != "")) for textBox in mandatory_fields_list: textBox.textChanged[str].connect( lambda: self.pb_generateText.setEnabled(textBox.text() != "")) def label_clicked(self, event, source): var_code = source.accessibleName() self.clipboard.setText(str(var_code)) event.accept() def checkbox_clicked(self, event, source): var_code = source.accessibleName() self.clipboard.setText(str(var_code)) source.toggle() def combobox_clicked(self, event, source): var_code = source.accessibleName() self.clipboard.setText(str(var_code)) source.showPopup() def setWindowTitleUnsaved(self): self.file_dirty = True _, fname = os.path.split(self.filename) self.setWindowTitle(self.mainTitle + " - " + fname + "*") def setWindowTitleSaved(self): self.file_dirty = False _, fname = os.path.split(self.filename) self.setWindowTitle(self.mainTitle + " - " + fname) def new_project(self): filename, _ = QFileDialog.getSaveFileName(self, "New Project", "./", "XML Files (*.xml)") if filename: if ".xml" not in filename: filename = filename + '.xml' self.reset_all_fields() self.filename = filename self.setWindowTitleUnsaved() else: return def reset_all_fields(self): for child in self.centralwidget.findChildren(QtWidgets.QLineEdit): child.clear() for child in self.centralwidget.findChildren(QtWidgets.QPlainTextEdit): child.clear() for child in self.centralwidget.findChildren(QtWidgets.QCheckBox): child.setChecked(False) for child in self.centralwidget.findChildren(SpellTextEdit): child.clear() for child in self.centralwidget.findChildren(SpellTextEdit): child.clear() self.label_pic.clear() def save_project(self): try: open(self.filename, 'w') except OSError: filename, _ = QFileDialog.getSaveFileName(self, "Save Project", "./", "XML Files (*.xml)") if filename: self.filename = filename else: return self.root = self.generate_root() if ".xml" not in self.filename: self.filename = self.filename + '.xml' with open(self.filename, 'wb') as f: f.write(tostring(self.root, pretty_print=True)) self.setWindowTitleSaved() def generate_root(self): root = Element('root') personal_info = Element('personal_info') root.append(personal_info) for qW in [ self.FIRSTNAME, self.LASTNAME, self.MOBILE, self.EMAIL, self.HOMEPAGE, self.GITHUBNAME, self.LINKEDINNAME ]: child = Element(qW.objectName()) child.text = qW.text() personal_info.append(child) personal_address = Element('PERSONALADDRESS') personal_address.text = self.PERSONALADDRESS.toPlainText() personal_info.append(personal_address) company_info = Element('company_info') root.append(company_info) for qW in [ self.COMPANYNAME, self.COMPANYSHORTNAME, self.DEPARTMENT, self.LETTERTITLE, self.JOBTITLE, self.JOBREFID, self.RECEIPIENTNAME ]: child = Element(qW.objectName()) child.text = qW.text() company_info.append(child) company_address = Element('COMPANYADDRESS') company_address.text = self.COMPANYADDRESS.toPlainText() company_info.append(company_address) RECEIPIENTGENDER = Element('RECEIPIENTGENDER') RECEIPIENTGENDER.text = str(self.RECEIPIENTGENDER.currentText()) company_info.append(RECEIPIENTGENDER) RECEIPIENTSALUTATION = Element('RECEIPIENTSALUTATION') RECEIPIENTSALUTATION.text = str( self.RECEIPIENTSALUTATION.currentText()) company_info.append(RECEIPIENTSALUTATION) about_me = Element('TEXTABOUTME') about_me.text = self.TEXTABOUTME.toPlainText() root.append(about_me) WhyFirm = Element('TEXTWHYTHISFIRM') WhyFirm.text = self.TEXTWHYTHISFIRM.toPlainText() root.append(WhyFirm) whyYou = Element('TEXTWHYYOU') whyYou.text = self.TEXTWHYYOU.toPlainText() root.append(whyYou) misc = Element('misc') root.append(misc) for qW in [ self.CLOSINGSALUTATION, self.ENCLOSINGPREFIX, self.PHOTOPATH ]: child = Element(qW.objectName()) child.text = qW.text() misc.append(child) for qW in [ self.CERTIFICATESATTACHED, self.CVATTACHED, self.REFLETTERSATTACHED, self.TRANSCRIPTSATTACHED, ]: child = Element(qW.objectName()) child.text = str(qW.isChecked()) misc.append(child) return root def saveas_project(self): filename, _ = QFileDialog.getSaveFileName(self, "Save Project As", "./", "XML Files (*.xml)") if filename: if ".xml" not in filename: filename = filename + '.xml' with open(filename, 'wb') as f: f.write(tostring(self.generate_root(), pretty_print=True)) self.load_file(filename) def open_project(self): filename, _ = QFileDialog.getOpenFileName(self, "Open Project", "./", "XML Files (*.xml)") if not filename: return if ".xml" not in filename: filename = filename + '.xml' self.load_file(filename) def load_file(self, filename): try: with open(filename, 'r') as f: self.root = XML(f.read()) #.replace("\n", "")) self.reset_all_fields() for element in self.root.iter(): widget = self.findChild(QtWidgets.QLineEdit, str(element.tag)) if widget is not None and element.text is not None: widget.setText(str(element.text)) else: widget = self.findChild(QtWidgets.QPlainTextEdit, str(element.tag)) if widget is not None and element.text is not None: widget.setPlainText(str(element.text)) else: widget = self.findChild(QtWidgets.QComboBox, str(element.tag)) if widget is not None and element.text is not None: index = widget.findText(element.text, QtCore.Qt.MatchFixedString) if index >= 0: widget.setCurrentIndex(index) elif str(element.text).isdigit(): widget.setCurrentIndex(int(element.text)) else: widget.setCurrentText(str(element.text)) else: widget = self.findChild(QtWidgets.QCheckBox, str(element.tag)) if widget is not None and element.text is not None: widget.setChecked(str(element.text) == 'True') else: widget = self.findChild( SpellTextEdit, str(element.tag)) if widget is not None and element.text is not None: widget.setChecked( str(element.text) == 'True') self.filename = filename self.get_photo(self.PHOTOPATH.text()) self.setWindowTitleSaved() except FileNotFoundError: # Warning: File not found! self.filename = "untitled.xml" self.setWindowTitleUnsaved() self.file_dirty = False except XMLSyntaxError: QtWidgets.QMessageBox.critical( self, "XML Read Failed", "Cannot read xml file %s. \n\nMake sure the xml file is not blank " % filename) def browse_photo(self): fname, _ = QFileDialog.getOpenFileName(self, 'Open profile photo', './', "Image files (*.jpg *.png)") if fname: self.get_photo(fname) def get_photo(self, fname): image = QtGui.QImage(fname) if image.isNull(): QtWidgets.QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fname) return self.PHOTOPATH.setText(fname) self.label_pic.setPixmap( QtGui.QPixmap(fname).scaled(160, 160, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation)) def generate_pdf(self): pdfcreator = PdfCreator(data=self.generate_root(), parent=self) pdfcreator.read_template(template=self.settings.latex_template) pdfcreator.convert_to_dict() pdfcreator.render_template() filename = self.COMPANYSHORTNAME.text() + '_' + self.JOBREFID.text( ) + '_Coverletter' filename = "".join(i for i in filename if i not in ".\/:*?<>|").replace(r' ', '_') self.pb_generatePdf.setEnabled(False) try: pdfcreator.compile_xelatex( compiler=self.settings.get_latex_compiler(), pdfname=filename + ".pdf", outputDir=self.settings.latex_dir, open_pdf=self.settings.open_pdf, keep_tex=self.settings.keep_tex) except FileNotFoundError as e: QtWidgets.QMessageBox.critical( self, "PDF Compilation Failed: " + str(e), "Cannot complete command {}.".format( self.settings.get_latex_compiler())) self.pb_generatePdf.setEnabled(True) def generate_text(self): textcreator = TextCreator(data=self.generate_root()) try: textcreator.read_template(template=self.settings.text_template) except FileNotFoundError as e: QtWidgets.QMessageBox.critical( self, "Error: " + repr(e), "Cannot find template file {}.\n".format( self.settings.text_template)) textcreator.convert_to_dict() textcreator.render_template() filename = self.COMPANYSHORTNAME.text() + '_' + self.JOBREFID.text( ) + '_Coverletter' filename = "".join(i for i in filename if i not in ".\/: *?<>|").replace(r' ', '_') self.pb_generateText.setEnabled(False) textcreator.compile_text(textname=filename + ".txt", outputDir=self.settings.text_dir, open_text=self.settings.open_text) self.pb_generateText.setEnabled(True) def writeSettings(self): self.config.beginGroup("MainWindow") self.config.setValue("size", self.size()) self.config.setValue("pos", self.pos()) self.config.endGroup() if not self.file_dirty: self.config.beginGroup("Project") self.config.setValue("filename", str(self.filename)) self.config.endGroup() self.config.sync() def readSettings(self): self.config.beginGroup("MainWindow") self.resize(self.config.value("size", QtCore.QSize(616, 466))) self.move(self.config.value("pos", QtCore.QPoint(200, 200))) self.config.endGroup() self.config.beginGroup("Project") self.filename = str(self.config.value("filename", self.filename)) self.config.endGroup() # event : QCloseEvent def closeEvent(self, event): if self.file_dirty: choice = QtWidgets.QMessageBox.question( self, 'Project not saved', "Save Project before exit?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel) if choice == QtWidgets.QMessageBox.Yes: self.save_project() self.writeSettings() event.accept() #sys.exit() elif choice == QtWidgets.QMessageBox.Cancel: event.ignore() else: self.writeSettings() event.accept() #sys.exit() else: self.writeSettings() event.accept()
def save(self): """Saves application state to QSettings""" if system() == "Darwin": settings = QSettings(APP_NAME+".gitlab.io", APP_NAME) else: settings = QSettings(APP_NAME, APP_NAME) # Application state # Do not store the actual filename. Otherwise, after saving and closing # File -> Save would overwrite the last saved file. if self.last_file_input_path is not None: settings.setValue("last_file_input_path", self.last_file_input_path) if self.last_file_output_path is not None: settings.setValue("last_file_output_path", self.last_file_output_path) if self.last_file_import_path is not None: settings.setValue("last_file_import_path", self.last_file_import_path) if self.last_file_export_path is not None: settings.setValue("last_file_export_path", self.last_file_export_path) settings.setValue("max_file_history", self.max_file_history) settings.value("file_history", [], 'QStringList') if self.file_history: settings.setValue("file_history", self.file_history) settings.setValue("timeout", self.timeout) settings.setValue("refresh_timeout", self.refresh_timeout) settings.setValue("signature_key", self.signature_key) # GUI state for widget_name in self.widget_names: if widget_name == "main_window": widget = self.parent else: widget = getattr(self.parent, widget_name) # geometry geometry_name = widget_name + '/geometry' try: settings.setValue(geometry_name, widget.saveGeometry()) except AttributeError: pass # state widget_state_name = widget_name + '/windowState' try: settings.setValue(widget_state_name, widget.saveState()) except AttributeError: pass if isinstance(widget, QToolBar): toolbar_visibility_name = widget_name + '/visibility' settings.value(toolbar_visibility_name, [], bool) settings.setValue(toolbar_visibility_name, [a.isVisible() for a in widget.actions()]) if widget_name == "entry_line": settings.setValue("entry_line_isvisible", widget.isVisible()) settings.sync()
import os
class Preferences(QObject): prefsChanged = pyqtSignal() def __init__(self): QObject.__init__(self) self.reset() self._settings = QSettings() def _load_values(self, settings, get): pass def get_rect(self, name, default=None): r = self.get_value(name, default) if r is not None: return QRect(*r) else: return None def get_value(self, name, default=None): if self._settings.contains(name): result = adjust_after_deserialization(self._settings.value(name)) if result is not None: return result else: # If result is None, but still present in self._settings, it usually means a value # like "@Invalid". return default else: return default def load(self): self.reset() self._load_values(self._settings) def reset(self): pass def _save_values(self, settings, set_): pass def save(self): self._save_values(self._settings) self._settings.sync() def set_rect(self, name, r): if isinstance(r, QRect): rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, rectAsList) def set_value(self, name, value): self._settings.setValue(name, normalize_for_serialization(value)) def saveGeometry(self, name, widget): # We save geometry under a 7-sized int array: first item is a flag # for whether the widget is maximized, second item is a flag for whether # the widget is docked, third item is a Qt::DockWidgetArea enum value, # and the other 4 are (x, y, w, h). m = 1 if widget.isMaximized() else 0 d = 1 if isinstance(widget, QDockWidget) and not widget.isFloating() else 0 area = widget.parent.dockWidgetArea(widget) if d else 0 r = widget.geometry() rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, [m, d, area] + rectAsList) def restoreGeometry(self, name, widget): geometry = self.get_value(name) if geometry and len(geometry) == 7: m, d, area, x, y, w, h = geometry if m: widget.setWindowState(Qt.WindowMaximized) else: r = QRect(x, y, w, h) widget.setGeometry(r) if isinstance(widget, QDockWidget): # Inform of the previous dock state and the area used return bool(d), area return False, 0
class SourcePanel(ScrollAreaNoFrame): """ Display Devices and This Computer sources, as well as the timeline """ def __init__(self, rapidApp) -> None: super().__init__() assert rapidApp is not None self.rapidApp = rapidApp self.prefs = self.rapidApp.prefs self.settings = QSettings() self.settings.beginGroup("MainWindow") self.setObjectName("sourcePanelScrollArea") self.sourcePanelWidget = QWidget(parent=self) self.sourcePanelWidget.setObjectName("sourcePanelWidget") self.splitter = SourceSplitter(parent=self.sourcePanelWidget) self.splitter.setObjectName("sourcePanelSplitter") self.splitter.setOrientation(Qt.Vertical) self.setWidget(self.sourcePanelWidget) self.setWidgetResizable(True) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(self.splitter.handleWidth()) self.sourcePanelWidget.setLayout(layout) self.thisComputerBottomFrameConnection = None self.thisComputerAltBottomFrameConnection = None self.frame_width = QApplication.style().pixelMetric( QStyle.PM_DefaultFrameWidth) def sourcesIsChecked(self) -> bool: """ Determine if download sources are to be visible. :return: True if only widget to be displayed is the Timeline, else False """ return self.rapidApp.sourceButton.isChecked() or ( self.rapidApp.on_startup and self.rapidApp.sourceButtonSetting()) def temporalProximityIsChecked(self) -> bool: """ Determine if the Timeline is or is going to be visible. Works during startup. :return: True if the Timeline is or will be visible, else False """ return self.rapidApp.proximityButton.isChecked() or ( self.rapidApp.on_startup and self.rapidApp.proximityButtonSetting()) def needSplitter(self) -> bool: """ A splitter is used if the Timeline should be showed, and This Computer is toggled on and is to be shown. :return: True if splitter should be used, else False """ return (self.temporalProximityIsChecked() and self.thisComputerToggleView.on() and self.sourcesIsChecked()) def addSourceViews(self) -> None: """ Add source widgets and timeline """ self.deviceToggleView = self.rapidApp.deviceToggleView self.deviceView = self.rapidApp.deviceView self.thisComputerToggleView = self.rapidApp.thisComputerToggleView self.thisComputer = self.rapidApp.thisComputer self.temporalProximity = self.rapidApp.temporalProximity self.deviceToggleView.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.temporalProximity.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding) layout = self.sourcePanelWidget.layout() # type: QVBoxLayout layout.addWidget(self.deviceToggleView, 0) for widget in ( self.deviceView, self.thisComputer, self.thisComputerToggleView.alternateWidget, ): self.verticalScrollBarVisible.connect( widget.containerVerticalScrollBar) for widget in self.temporalProximity.flexiFrameWidgets(): self.verticalScrollBarVisible.connect( widget.containerVerticalScrollBar) self.horizontalScrollBarVisible.connect( widget.containerHorizontalScrollBar) def placeWidgets(self) -> None: """ Place This Computer and Timeline widgets in the correct container """ # Scenarios: # TL = Timeline (temporal proximity) # D = Device Toggle View # TC = This Computer Toggle View # TL only: TL in panel, D & TC hidden, splitter hidden # Sources showing only: D & TC in panel, TL hidden, splitter hidden # All showing: D in panel, and: # if TC on, TC and TL in splitter, splitter showing # if TC off, TC and TL in panel, splitter hidden layout = self.sourcePanelWidget.layout() # type: QVBoxLayout if not self.needSplitter(): if self.splitter.isVisible(): self.settings.setValue("leftPanelSplitterSizes", self.splitter.saveState()) self.settings.sync() layout.addWidget(self.thisComputerToggleView) layout.addWidget(self.temporalProximity) layout.addWidget(self.splitter) self.splitter.setVisible(False) else: layout.addWidget(self.splitter) self.splitter.addWidget(self.thisComputerToggleView) self.splitter.addWidget(self.temporalProximity) for index in range(self.splitter.count()): self.splitter.setCollapsible(index, False) self.handle = self.splitter.handle(1) self.handle.mousePress.connect(self.splitterHandleMousePress) self.handle.mouseReleased.connect(self.splitterHandleMouseRelease) self.splitter.setVisible(True) splitterSetting = self.settings.value("leftPanelSplitterSizes") if splitterSetting is not None: if not self.splitter.restoreState(splitterSetting): logging.debug( "Did not restore left splitter sizing because it is no " "longer valid") self.setThisComputerToggleViewSizePolicy() def setThisComputerToggleViewSizePolicy(self) -> None: if self.thisComputerToggleView.on(): if self.temporalProximityIsChecked(): self.thisComputerToggleView.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred) else: self.thisComputerToggleView.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.MinimumExpanding) else: if self.temporalProximityIsChecked(): self.thisComputerToggleView.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Fixed) else: self.thisComputerToggleView.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.MinimumExpanding) def setSourcesVisible(self, visible: bool) -> None: self.deviceToggleView.setVisible(visible) self.thisComputerToggleView.setVisible(visible) self.splitter.setVisible(self.needSplitter()) if visible: # scroll up to make Devices and This Computer, if necessary if self.verticalScrollBar().isVisible(): auto_scroll = self.prefs.auto_scroll if auto_scroll: self.rapidApp.temporalProximityControls.setTimelineThumbnailAutoScroll( on=False) self.verticalScrollBar().setValue( self.verticalScrollBar().minimum()) if auto_scroll: self.rapidApp.temporalProximityControls.setTimelineThumbnailAutoScroll( on=True) def setThisComputerBottomFrame(self, temporalProximityVisible: bool) -> None: """ Connect or disconnect reaction of This Computer widget to the Scroll Area horizontal scroll bar becoming visible or not. Idea is to not react when the Timeline is visible, and react when it is hidden, which is when the This Computer widget is the bottommost widget. :param temporalProximityVisible: whether the timeline is visible """ if temporalProximityVisible: if self.thisComputerBottomFrameConnection: self.horizontalScrollBarVisible.disconnect( self.thisComputerBottomFrameConnection) self.thisComputerBottomFrameConnection = None if self.thisComputerAltBottomFrameConnection: self.horizontalScrollBarVisible.disconnect( self.thisComputerAltBottomFrameConnection) self.thisComputerAltBottomFrameConnection = None # Always show the bottom edge frame, regardless of what the scroll area # scrollbar is doing self.thisComputer.containerHorizontalScrollBar(False) self.thisComputerToggleView.alternateWidget.containerHorizontalScrollBar( False) else: if self.thisComputerBottomFrameConnection is None: self.thisComputerBottomFrameConnection = ( self.horizontalScrollBarVisible.connect( self.thisComputer.containerHorizontalScrollBar)) if self.thisComputerAltBottomFrameConnection is None: self.thisComputerAltBottomFrameConnection = self.horizontalScrollBarVisible.connect( self.thisComputerToggleView.alternateWidget. containerHorizontalScrollBar) self.thisComputer.containerHorizontalScrollBar( self.horizontalScrollBar().isVisible()) self.thisComputerToggleView.alternateWidget.containerHorizontalScrollBar( self.horizontalScrollBar().isVisible()) def setTemporalProximityVisible(self, visible: bool) -> None: self.placeWidgets() self.setThisComputerBottomFrame(visible) self.temporalProximity.setVisible(visible) self.setThisComputerAltWidgetVisible(visible) def setThisComputerAltWidgetVisible( self, temporalProximityVisible: bool) -> None: if not self.thisComputerToggleView.on(): self.thisComputerToggleView.alternateWidget.setVisible( not temporalProximityVisible) def setThisComputerState(self) -> None: self.placeWidgets() self.setThisComputerAltWidgetVisible(self.temporalProximityIsChecked()) self.setThisComputerToggleViewSizePolicy() @pyqtSlot() def splitterHandleMousePress(self) -> None: y = self.handle.pos().y() if self.temporalProximity.state == TemporalProximityState.generated: self.temporalProximity.temporalProximityView.setMinimumHeight(20) else: stackedWidget = self.temporalProximity.stackedWidget if self.temporalProximity.state == TemporalProximityState.empty: self.temporalProximity.explanation.setChildPositions( fixed=True) height = max(self.splitter.height(), self.height()) self.splitter.setFixedHeight( height + stackedWidget.minimumSizeHint().height()) self.handle.moveSplitter(y) @pyqtSlot() def splitterHandleMouseRelease(self) -> None: y = self.handle.pos().y() if self.temporalProximity.state == TemporalProximityState.generated: self.temporalProximity.setProximityHeight() else: self.temporalProximity.explanation.setChildPositions(fixed=False) self.temporalProximity.stackedWidget.onCurrentChanged( self.temporalProximity.state) self.setSplitterSize() self.handle.moveSplitter(y) def setSplitterSize(self) -> None: if self.needSplitter(): bottom_frame = (0 if self.horizontalScrollBar().isVisible() else self.frame_width) if self.temporalProximity.state == TemporalProximityState.generated: self.splitter.setFixedHeight( +self.splitter.sizes()[0] # handle position + self.splitter.handleWidth() + self.frame_width + self.temporalProximity.temporalProximityView.contentHeight( ) + bottom_frame) else: stackedWidget = self.temporalProximity.stackedWidget devices_y = abs( self.deviceToggleView.mapTo(self, QPoint(0, 0)).y()) devices_height = self.splitter.mapTo(self, QPoint( 0, 0)).y() + devices_y # handle position + handle width: y = self.splitter.sizes()[0] + self.splitter.handleWidth() min_height = stackedWidget.minimumSizeHint().height() if self.height() - devices_height > y + min_height: height = self.height() - y - devices_height else: height = min_height self.splitter.setFixedHeight(y + height)
class Preferences(QObject): prefsChanged = pyqtSignal() def __init__(self): QObject.__init__(self) self.reset() self._settings = QSettings() def _load_values(self, settings, get): pass def get_rect(self, name, default=None): r = self.get_value(name, default) if r is not None: return QRect(*r) else: return None def get_value(self, name, default=None): if self._settings.contains(name): result = adjust_after_deserialization(self._settings.value(name)) if result is not None: return result else: # If result is None, but still present in self._settings, it usually means a value # like "@Invalid". return default else: return default def load(self): self.reset() self._load_values(self._settings) def reset(self): pass def _save_values(self, settings, set_): pass def save(self): self._save_values(self._settings) self._settings.sync() def set_rect(self, name, r): if isinstance(r, QRect): rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, rectAsList) def set_value(self, name, value): self._settings.setValue(name, normalize_for_serialization(value)) def saveGeometry(self, name, widget): # We save geometry under a 5-sized int array: first item is a flag for whether the widget # is maximized and the other 4 are (x, y, w, h). m = 1 if widget.isMaximized() else 0 r = widget.geometry() rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, [m] + rectAsList) def restoreGeometry(self, name, widget): l = self.get_value(name) if l and len(l) == 5: m, x, y, w, h = l if m: widget.setWindowState(Qt.WindowMaximized) else: r = QRect(x, y, w, h) widget.setGeometry(r)
class Preferences: def __init__(self): self.reset() self._settings = QSettings() def _load_values(self, settings, get): pass def get_rect(self, name, default=None): r = self.get_value(name, default) if r is not None: return QRect(*r) else: return None def get_value(self, name, default=None): if self._settings.contains(name): result = adjust_after_deserialization(self._settings.value(name)) if result is not None: return result else: # If result is None, but still present in self._settings, it usually means a value # like "@Invalid". return default else: return default def load(self): self.reset() self._load_values(self._settings) def reset(self): pass def _save_values(self, settings, set_): pass def save(self): self._save_values(self._settings) self._settings.sync() def set_rect(self, name, r): if isinstance(r, QRect): rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, rectAsList) def set_value(self, name, value): self._settings.setValue(name, normalize_for_serialization(value)) def saveGeometry(self, name, widget): # We save geometry under a 5-sized int array: first item is a flag for whether the widget # is maximized and the other 4 are (x, y, w, h). m = 1 if widget.isMaximized() else 0 r = widget.geometry() rectAsList = [r.x(), r.y(), r.width(), r.height()] self.set_value(name, [m] + rectAsList) def restoreGeometry(self, name, widget): l = self.get_value(name) if l and len(l) == 5: m, x, y, w, h = l if m: widget.setWindowState(Qt.WindowMaximized) else: r = QRect(x, y, w, h) widget.setGeometry(r)
class Settings(QDialog, Ui_Settings): """Settings dialog""" def __init__(self, parent): QDialog.__init__(self, parent) self.parent = parent self.setupUi(self) self.core = None self.plugins = [] self.emumode = [] self.combomap = {} self.qset = QSettings("m64py", "m64py") self.qset.setDefaultFormat(QSettings.IniFormat) self.add_items() self.connect_signals() def showEvent(self, event): self.set_config() def closeEvent(self, event): self.save_config() def add_items(self): self.combomap = { M64PLUGIN_RSP: (self.comboRSP, self.pushButtonRSP, Plugin(self.parent)), M64PLUGIN_GFX: (self.comboVideo, self.pushButtonVideo, Plugin(self.parent)), M64PLUGIN_AUDIO: (self.comboAudio, self.pushButtonAudio, Plugin(self.parent)), M64PLUGIN_INPUT: (self.comboInput, self.pushButtonInput, Input(self.parent)) } self.emumode = [ QRadioButton(self.tr("Pure Interpreter")), QRadioButton(self.tr("Cached Interpreter")), QRadioButton(self.tr("Dynamic Recompiler")) ] vbox = QVBoxLayout(self.groupEmuMode) for widget in self.emumode: vbox.addWidget(widget) def show_page(self, index=0): self.tabWidget.setCurrentIndex(index) self.show() def save_config(self): self.save_paths() self.save_plugins() if self.core and self.core.get_handle(): self.save_video() self.save_core() self.core.config.save_file() self.qset.sync() def set_config(self): if self.core and self.core.get_handle(): self.set_paths() self.set_plugins() self.set_video() self.set_core() def on_vidext_changed(self, state): self.parent.vidext = state self.comboResolution.setEnabled(not self.parent.vidext) self.checkFullscreen.setEnabled(not self.parent.vidext) self.parent.worker.quit() self.parent.worker.init() def connect_signals(self): self.browseLibrary.clicked.connect(lambda: self.browse_dialog( (self.pathLibrary, self.groupLibrary, False))) self.browsePlugins.clicked.connect(lambda: self.browse_dialog( (self.pathPlugins, self.groupPlugins, True))) self.browseData.clicked.connect(lambda: self.browse_dialog( (self.pathData, self.groupData, True))) self.browseROM.clicked.connect(lambda: self.browse_dialog( (self.pathROM, self.groupROM, True))) self.checkEnableVidExt.clicked.connect(self.on_vidext_changed) for plugin_type in self.combomap: self.connect_combo_signals(self.combomap[plugin_type]) def connect_combo_signals(self, combomap): combo, button, settings = combomap if settings is not None: if combo != self.comboInput: combo.activated.connect( lambda: self.set_section(combo, button, settings)) button.clicked.connect(settings.show_dialog) def browse_dialog(self, args): widget, groupbox, directory = args dialog = QFileDialog() if directory: dialog.setFileMode(QFileDialog.Directory) path = dialog.getExistingDirectory(self, groupbox.title(), widget.text(), QFileDialog.ShowDirsOnly) else: dialog.setFileMode(QFileDialog.ExistingFile) path, _ = dialog.getOpenFileName( self, groupbox.title(), widget.text(), "%s (*%s);;All files (*)" % (groupbox.title(), DLL_FILTER)) if not path: return widget.setText(path) if widget == self.pathLibrary: self.parent.worker.quit() if not self.parent.worker.core.get_handle(): self.parent.worker.init(path) if self.parent.worker.core.get_handle(): self.core = self.parent.worker.core self.set_core() self.set_video() self.parent.window_size_triggered(self.get_size_safe()) self.parent.state_changed.emit((True, False, False, False)) elif widget == self.pathPlugins: self.parent.worker.plugins_shutdown() self.parent.worker.plugins_unload() self.parent.worker.plugins_load(path) self.parent.worker.plugins_startup() self.set_plugins() def get_int_safe(self, key, default): try: return int(self.qset.value(key, default)) except ValueError: return default def get_size_safe(self): try: size = self.qset.value("size", SIZE_1X) except TypeError: size = SIZE_1X if not type(size) == tuple: size = SIZE_1X if len(size) != 2: size = SIZE_1X if type(size[0]) != int or type(size[1]) != int: size = SIZE_1X if size[0] <= 0 or size[1] <= 0: size = SIZE_1X return size def get_section(self, combo): plugin = combo.currentText() index = combo.findText(plugin) desc = combo.itemData(index) name = os.path.splitext(plugin)[0][12:] section = "-".join([n.capitalize() for n in name.split("-")[0:2]]) return section, desc def set_section(self, combo, button, settings): if settings: if combo != self.comboInput: section, desc = self.get_section(combo) settings.set_section(section, desc) self.core.config.open_section(section) items = self.core.config.parameters[ self.core.config.section].items() if items: button.setEnabled(True) else: button.setEnabled(False) else: button.setEnabled(True) else: button.setEnabled(False) def set_paths(self): path_library = self.qset.value("Paths/Library", find_library(CORE_NAME)) path_data = self.qset.value("Paths/Data", self.core.config.get_path("SharedData")) path_roms = self.qset.value("Paths/ROM") try: path_plugins = self.qset.value( "Paths/Plugins", os.path.realpath( os.path.dirname(self.parent.worker.plugin_files[0]))) except IndexError: path_plugins = "" try: self.pathROM.setText(path_roms) except TypeError: pass self.pathLibrary.setText(path_library) self.pathPlugins.setText(path_plugins) self.pathData.setText(path_data) def set_video(self): self.core.config.open_section("Video-General") self.set_resolution() self.checkEnableVidExt.setChecked( bool(self.get_int_safe("enable_vidext", 1))) self.checkFullscreen.setChecked( bool(self.core.config.get_parameter("Fullscreen"))) self.checkFullscreen.setEnabled(not self.parent.vidext) self.checkVsync.setChecked( bool(self.core.config.get_parameter("VerticalSync"))) self.checkVsync.setToolTip( self.core.config.get_parameter_help("VerticalSync").decode()) if sys.platform == "win32": self.checkKeepAspect.setChecked(False) self.checkKeepAspect.setEnabled(False) else: keep_aspect = bool(self.get_int_safe("keep_aspect", 1)) self.checkKeepAspect.setChecked(keep_aspect) disable_screensaver = bool(self.get_int_safe("disable_screensaver", 1)) self.checkDisableScreenSaver.setChecked(disable_screensaver) def set_core(self): self.core.config.open_section("Core") mode = self.core.config.get_parameter("R4300Emulator") self.emumode[mode].setChecked(True) self.checkOSD.setChecked( self.core.config.get_parameter("OnScreenDisplay")) self.checkOSD.setToolTip( self.core.config.get_parameter_help("OnScreenDisplay").decode()) self.checkNoCompiledJump.setChecked( self.core.config.get_parameter("NoCompiledJump")) self.checkNoCompiledJump.setToolTip( self.core.config.get_parameter_help("NoCompiledJump").decode()) self.checkDisableExtraMem.setChecked( self.core.config.get_parameter("DisableExtraMem")) self.checkDisableExtraMem.setToolTip( self.core.config.get_parameter_help("DisableExtraMem").decode()) delay_si = self.core.config.get_parameter("DelaySI") if delay_si is not None: self.checkDelaySI.setChecked(delay_si) else: self.checkDelaySI.setChecked(False) self.checkDelaySI.setEnabled(False) self.checkDelaySI.setToolTip( self.core.config.get_parameter_help("DelaySI").decode()) count_per_op = self.core.config.get_parameter("CountPerOp") if count_per_op is not None: self.comboCountPerOp.setCurrentIndex(count_per_op) else: self.comboCountPerOp.setEnabled(False) self.comboCountPerOp.setToolTip( self.core.config.get_parameter_help("CountPerOp").decode()) def set_plugins(self): plugin_map = self.core.plugin_map for plugin_type in self.combomap: combo, button, settings = self.combomap[plugin_type] combo.clear() for plugin in plugin_map[plugin_type].values(): (plugin_handle, plugin_path, plugin_name, plugin_desc, plugin_version) = plugin name = os.path.basename(plugin_path) combo.addItem(name) index = combo.findText(str(name)) combo.setItemData(index, plugin_desc) combo.setItemData(index, plugin_desc, Qt.ToolTipRole) current = self.qset.value( "Plugins/%s" % (PLUGIN_NAME[plugin_type]), PLUGIN_DEFAULT[plugin_type]) index = combo.findText(current) if index == -1: index = 0 combo.setCurrentIndex(index) self.set_section(combo, button, settings) def set_resolution(self): width = self.core.config.get_parameter("ScreenWidth") height = self.core.config.get_parameter("ScreenHeight") if (width, height) not in MODES: MODES.append((width, height)) self.comboResolution.clear() for mode in MODES: w, h = mode self.comboResolution.addItem("%sx%s" % (w, h), (w, h)) index = self.comboResolution.findText("%sx%s" % (width, height), Qt.MatchExactly) if index == -1: index = 0 self.comboResolution.setCurrentIndex(index) self.comboResolution.setEnabled(not self.parent.vidext) def save_paths(self): self.qset.setValue("Paths/Library", self.pathLibrary.text()) self.qset.setValue("Paths/Plugins", self.pathPlugins.text()) self.qset.setValue("Paths/Data", self.pathData.text()) self.qset.setValue("Paths/ROM", self.pathROM.text()) def save_video(self): self.core.config.open_section("Video-General") if self.parent.vidext: width, height = self.get_size_safe() else: width, height = self.comboResolution.currentText().split("x") self.core.config.set_parameter("ScreenWidth", int(width)) self.core.config.set_parameter("ScreenHeight", int(height)) self.core.config.set_parameter("Fullscreen", self.checkFullscreen.isChecked()) self.core.config.set_parameter("VerticalSync", self.checkVsync.isChecked()) self.qset.setValue("keep_aspect", int(self.checkKeepAspect.isChecked())) self.qset.setValue("disable_screensaver", int(self.checkDisableScreenSaver.isChecked())) self.qset.setValue("enable_vidext", int(self.checkEnableVidExt.isChecked())) def save_core(self): self.core.config.open_section("Core") emumode = [n for n, m in enumerate(self.emumode) if m.isChecked()][0] self.core.config.set_parameter("R4300Emulator", emumode) self.core.config.set_parameter("OnScreenDisplay", self.checkOSD.isChecked()) self.core.config.set_parameter("NoCompiledJump", self.checkNoCompiledJump.isChecked()) self.core.config.set_parameter("DisableExtraMem", self.checkDisableExtraMem.isChecked()) self.core.config.set_parameter("DelaySI", self.checkDelaySI.isChecked()) self.core.config.set_parameter("CountPerOp", self.comboCountPerOp.currentIndex()) self.core.config.set_parameter("SharedDataPath", self.pathData.text().encode()) def save_plugins(self): for plugin_type in self.combomap: combo, button, settings = self.combomap[plugin_type] self.qset.setValue("Plugins/%s" % PLUGIN_NAME[plugin_type], combo.currentText())
class SettingsDlg(QDialog): """Dialog for manipulating settings for display and PPE settings""" INTERVAL = 0x0001 AUTOTRY = 0x0002 HANDLE = 0x0004 EMAIL = 0x0008 CONTINUE = 0x0010 ACCOUNTS = 0x0020 STATE = 0x0040 SIZE = 0x0080 POS = 0x0100 SPLITTERS = 0x0200 EMPTY = 0x0400 DIGITS = 0x0800 # lambda for whether a setting is actually a string and true (happens automatically with QSettings) str_bool = lambda val: bool(val) and (not isinstance(val, str) or (isinstance(val, str) and val != 'false')) def __init__(self, parent=None): super(SettingsDlg, self).__init__(parent) self.setWindowTitle("Settings") overall_layout = QVBoxLayout() self.setLayout(overall_layout) # get relevant data to populate settings panel data = shelve.open('/etc/ppe/data') interval = autotry = 900 try: interval = data['interval'] autotry = data['autotry'] except KeyError: pass data.close() self.settings = QSettings("Scott Stewart", "qPPE") self.settings.beginGroup("Settings Dialog") # make tabwidget divided between general and view settings tab_widget = QTabWidget() overall_layout.addWidget(tab_widget) general_widget = QWidget() view_widget = QWidget() general_layout = QGridLayout() general_widget.setLayout(general_layout) view_layout = QGridLayout() view_widget.setLayout(view_layout) tab_widget.addTab(general_widget, "&General") tab_widget.addTab(view_widget, "&View") # check interval is combo/spinbox for type (sec, min) and number of sec/mins, keeping the two consistent and updated w/ a variable for secs interval_label = QLabel("&Check interval:") self.interval = TimeSpinBox(self, interval) interval_label.setBuddy(self.interval) general_layout.addWidget(interval_label, 0, 0) general_layout.addWidget(self.interval, 0, 1) # do autotry the same as check interval autotry_label = QLabel("&Autotry interval:") self.autotry = TimeSpinBox(self, autotry) autotry_label.setBuddy(self.autotry) general_layout.addWidget(autotry_label, 1, 0) general_layout.addWidget(self.autotry, 1, 1) # checkbox on whether to check email, uses setting if available,controls visibility of subsequent settings self.handle_ppe = self.constructCheckBox("Automatically &handle scheduling checks?", "handle_ppe", True) general_layout.addWidget(self.handle_ppe, 2, 0, 1, 2) # checkbox on whether to send emails if PPE is being handled by the app or should be closed on exit, usability dependent on handle_ppe self.send_emails = self.constructCheckBox("&Send email notifiations?", "send_emails", False) self.continue_running = self.constructCheckBox("Continue &running PPE after close?", "continue_running", True) # frame holding options dependent on handling handle_widgets = QFrame() handle_layout = QVBoxLayout() handle_widgets.setLayout(handle_layout) handle_widgets.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) handle_widgets.setEnabled(self.handle_ppe.checkState()) handle_layout.addWidget(self.send_emails) handle_layout.addWidget(self.continue_running) self.handle_ppe.stateChanged.connect(lambda state: handle_widgets.setEnabled(state)) general_layout.addWidget(handle_widgets, 3, 0, 2, 2) # checkbox on whether to keep account selection visible on main window self.view_accounts = self.constructCheckBox("&Show account choices?", "view_accounts", True) view_layout.addWidget(self.view_accounts, 0, 0, 1, 2) # checkbox on whether to show empty grades self.show_empty = self.constructCheckBox("Show &empty grades?", "show_empty", False) view_layout.addWidget(self.show_empty, 1, 0, 1, 2) # checkbox on whether to save application state self.save_state = self.constructCheckBox("Save &application state?", "save_state", True) view_layout.addWidget(self.save_state, 2, 0, 1, 2) # custom spinbox on the number of digits to display for percentages digit_label = QLabel("P&recision of percentagess:") self.digits = PercentSpinBox(self, int(self.settings.value('digits', 1))) digit_label.setBuddy(self.digits) view_layout.addWidget(digit_label, 3, 0) view_layout.addWidget(self.digits, 3, 1) # checkboxes on whther to save size, powition, splitters, etc, contingent on save_state self.save_size = self.constructCheckBox("Save &window size?", "save_size", True) self.save_pos = self.constructCheckBox("Save window &position?", "save_pos", True) self.save_splitters = self.constructCheckBox("Save window &geometry?", "save_splitters", False) # frame holding options dependent on handling state_widgets = QFrame() state_layout = QVBoxLayout() state_widgets.setLayout(state_layout) state_widgets.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) state_widgets.setEnabled(self.save_state.checkState()) state_layout.addWidget(self.save_size) state_layout.addWidget(self.save_pos) state_layout.addWidget(self.save_splitters) self.save_state.stateChanged.connect(lambda state: state_widgets.setEnabled(state)) view_layout.addWidget(state_widgets, 4, 0, 3, 2) view_layout.setRowStretch(5, 1) # ok/cancel buttons on custom accept buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) overall_layout.addWidget(buttons) def constructCheckBox(self, text, setting_text, default): box = QCheckBox(text) setting = self.settings.value(setting_text) if setting is not None: box.setCheckState(SettingsDlg.str_bool(setting)) else: box.setCheckState(default) self.settings.setValue(setting_text, default) box.setTristate(False) return box def accept(self): '''Updates a bitmask for changes since the beginning of the dialog and closes the dialog''' self.changes = 0 data = shelve.open('/etc/ppe/data') if not 'interval' in data or self.interval.value() != data['interval']: data['interval'] = self.interval.value() self.changes |= self.INTERVAL if not 'autotry' in data or self.autotry.value() != data['autotry']: data['autotry'] = self.autotry.value() self.changes |= self.AUTOTRY data.close() if self.handle_ppe.isChecked() != SettingsDlg.str_bool(self.settings.value('handle_ppe')): self.settings.setValue('handle_ppe', self.handle_ppe.isChecked()) self.changes |= self.HANDLE if self.send_emails.isChecked() != SettingsDlg.str_bool(self.settings.value('send_emails')): self.settings.setValue('send_emails', self.send_emails.isChecked()) self.changes |= self.EMAIL if self.continue_running.isChecked() != SettingsDlg.str_bool(self.settings.value('continue_running')): self.settings.setValue('continue_running', self.continue_running.isChecked()) self.changes |= self.CONTINUE if self.view_accounts.isChecked() != SettingsDlg.str_bool(self.settings.value('view_accounts')): self.settings.setValue('view_accounts', self.view_accounts.isChecked()) self.changes |= self.ACCOUNTS if self.save_state.isChecked() != SettingsDlg.str_bool(self.settings.value('save_state')): self.settings.setValue('save_state', self.save_state.isChecked()) self.changes |= self.STATE if self.save_size.isChecked() != SettingsDlg.str_bool(self.settings.value('save_size')): self.settings.setValue('save_size', self.save_size.isChecked()) self.changes |= self.SIZE if self.save_pos.isChecked() != SettingsDlg.str_bool(self.settings.value('save_pos')): self.settings.setValue('save_pos', self.save_pos.isChecked()) self.changes |= self.POS if self.save_splitters.isChecked() != SettingsDlg.str_bool(self.settings.value('save_splitters')): self.settings.setValue('save_splitters', self.save_splitters.isChecked()) self.changes |= self.SPLITTERS if self.show_empty.isChecked() != SettingsDlg.str_bool(self.settings.value('show_empty')): self.settings.setValue('show_empty', self.show_empty.isChecked()) self.changes |= self.EMPTY if self.digits.value() != self.settings.value('digits'): self.settings.setValue('digits', self.digits.value()) self.changes |= self.DIGITS self.settings.sync() self.settings.endGroup() QDialog.accept(self)
def initSettings(self): """Load settings file if exists, create new otherwise """ folder_name = os.path.dirname(os.path.abspath(__file__)) settings_path = os.path.join(folder_name, 'settings.ini') if not os.path.exists(settings_path): settings = QSettings(settings_path, QSettings.IniFormat) # fonts for various fields settings.setValue( 'display/fonts/meta_title', QFont('Serif', 14, QFont.Bold | QFont.Capitalize)) settings.setValue('display/fonts/meta_authors', QFont('Serif', 11)) settings.setValue('display/fonts/meta_keywords', QFont('Times', 11, QFont.StyleItalic)) settings.setValue('display/fonts/statusbar', QFont('Serif', 10)), settings.setValue('display/fonts/doc_table', QFont('Serif', 10)), settings.setValue('display/fonts/bibtex', QFont('Serif', 10)), settings.setValue('display/fonts/notes', QFont('Serif', 10)), settings.setValue('display/fonts/scratch_pad', QFont('Serif', 10)), # highlight folder containing a doc settings.setValue('display/folder/highlight_color_br', QBrush(QColor(200, 200, 255))) settings.setValue('export/bib/omit_fields', OMIT_KEYS) settings.setValue('export/bib/path_type', 'absolute') settings.setValue('export/ris/path_type', 'absolute') # storage recently opened database settings.setValue('file/recent_open', []) settings.setValue('file/recent_open_num', 2) settings.setValue('file/auto_open_last', 1) # default storage folder storage_folder = os.path.join(str(pathlib.Path.home()), 'Documents/MeiTingTrunk') settings.setValue('saving/storage_folder', storage_folder) # file copy/link settings.setValue('saving/file_move_manner', 'link') # 'copy' or 'link' # auto save settings.setValue('saving/auto_save_min', 5), # rename pdf files in storage settings.setValue('saving/rename_files', 1) settings.setValue('saving/rename_file_replace_space', 1) # min score to flag a duplication settings.setValue('duplicate_min_score', 60) settings.setValue('import/default_add_action', 'Add PDF File') # search fields settings.setValue('search/search_fields', [ 'Authors', 'Title', 'Abstract', 'Keywords', 'Tags', 'Notes', 'Publication' ]) settings.setValue('search/desend_folder', True) # view control settings.setValue('view/show_widgets', [ 'Toggle Filter List', 'Toggle Tab Pane', 'Toggle Meta Tab', 'Toggle Notes Tab', 'Toggle BibTex Tab', 'Toggle Scratch Pad Tab', 'Toggle Status bar' ]) settings.sync() else: settings = QSettings(settings_path, QSettings.IniFormat) #---------------Make sure output folder exists--------------- storage_folder = settings.value('saving/storage_folder') self.logger.info('storage_folder=%s' % storage_folder) storage_folder = os.path.expanduser(storage_folder) if not os.path.exists(storage_folder): os.makedirs(storage_folder) self.logger.info('Create folder %s' % storage_folder) return settings
# This test is playing with configuration settings and checking that works. from PyQt5.QtCore import QSettings from PyQt5.QtCore import QCoreApplication import sys app = QCoreApplication([]) app.setOrganizationName("BOGUS_NAME") app.setOrganizationDomain("bogosity.com") app.setApplicationName("BOGUS") settings = QSettings() byte_string = b'\xde\xad\xbe\xef' settings.clear() settings.setValue("bogus_byte_string",byte_string) settings.sync() return_string = settings.value("bogus_byte_string",b'\x00\x00\x00\x00') if sys.version_info >= (3,): assert return_string == byte_string, (repr(return_string), "!=", byte_string) print("OK.") # This test is using signals and will only work if PySide properly accepts # compiled functions as callables. from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QMetaObject class Communicate(QObject): speak = pyqtSignal(int) def __init__(self,name = "",parent = None): QObject.__init__(self,parent) self.setObjectName(name)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.mode = ['lineart', 'gray', 'color'] self.resolution = ['75', '100', '150', '200', '300', '600', '1200'] self.compression = ['None', 'JPEG'] self.scanFolder = os.getcwd() self.ver = sane.init() self.ui = uic.loadUi("mainwindow.ui", self) #self.ui = Ui_MainWindow() #self.ui.setupUi(self) self.dialog = QDialog() self.message = QMessageBox() self.threadpool = QThreadPool() self.settings = QSettings("bibuweb.de", "Scan2Folder") self.progressBar = None self.configWin = ConfigWindow(self) self.configWin.ui.scanButton.clicked.connect(self.configScan) self.ui.resolutions.addItems(self.resolution) self.ui.resolutions.setCurrentIndex( self.ui.resolutions.findText('300')) self.ui.btnOpenDir.clicked.connect(self.openDir) self.ui.btnStartscan.clicked.connect(self.startScanJob) self.ui.btnOcr.clicked.connect(self.ocr_startProcess) self.ui.actionCalibrate.triggered.connect(self.configureWindow) self.configWin.ui.saveButton.clicked.connect(self.saveConfig) # Change Color back after error self.ui.filename.cursorPositionChanged.connect(self.leditcolor) self.ui.scanpath.cursorPositionChanged.connect(self.leditcolor) self.ui.scanpath.textChanged.connect(self.scanPathCanged) self.is_dev = True self.dev_available = False self.dev_connected = False self.adf = False self.dev = None self.devices = [] self.scanStatus = False self.btnStyle = "" self.contrast = 1 self.brightness = 1 self.color = 1 self.sharpness = 1 self.scanPath = "" self.ocr = False self.crop = False self.cropSize = {'left': 1, 'top': 1, 'width': 1, 'height': 1} self.ocrFiles = [] self.tempocr = None if self.settings.contains("ocr"): print("Load: ", self.settings.value('ocr')) if self.settings.value('ocr') == 'true': self.ocr = True self.ui.actionEnable_OCR.setChecked(True) self.configWin.ui.OCR_Enabled.setChecked(True) self.configWin.ui.OCR_Box.setEnabled(True) else: self.ui.actionEnable_OCR.setChecked(False) self.configWin.ui.OCR_Enabled.setChecked(False) self.configWin.ui.OCR_Box.setEnabled(False) ## connect Signal here and not before loading settings ## if not, you never will get the stored value because QAction is triggered when ever the value changed self.configWin.ui.OCR_Enabled.stateChanged.connect(self.ocrConfig) if self.settings.contains('crop'): if self.settings.value('crop') == 'true': self.crop = True self.configWin.ui.checkCrop.setChecked(self.crop) if self.settings.contains('cropSize'): #print(self.settings.value('cropSize')) self.cropSize = self.settings.value('cropSize') self.configWin.ui.cropX.setValue(self.cropSize['left']) self.configWin.ui.cropY.setValue(self.cropSize['top']) self.configWin.ui.cropW.setValue(self.cropSize['width']) self.configWin.ui.cropH.setValue(self.cropSize['height']) if self.settings.contains("path"): self.ui.scanpath.setText(self.settings.value("path")) self.scanPath = self.settings.value("path") self.createCompleter() if self.settings.contains('contrast'): self.brightness = self.settings.value('brightness') self.contrast = self.settings.value('contrast') self.configWin.ui.brigthnessLcd.setValue(float(self.brightness)) self.configWin.ui.brigthnesSlider.setValue( int(float(self.brightness) * 10)) self.configWin.ui.contrastLcd.setValue(float(self.contrast)) self.configWin.ui.contrastSlider.setValue( int(float(self.contrast) * 10)) if self.settings.contains('color'): self.color = self.settings.value('color') self.configWin.ui.colorLcd.setValue(float(self.color)) self.configWin.ui.colorSlider.setValue(int(float(self.color) * 10)) if self.settings.contains("sharpness"): self.sharpness = self.settings.value('sharpness') self.configWin.ui.sharpnessLcd.setValue(float(self.sharpness)) self.configWin.ui.sharpnessSlider.setValue( int(float(self.sharpness) * 10)) def closeEvent(self, event): #if not set, process keeps running in background self.scanStatus = False def openDir(self): fileDlg = QFileDialog() self.scanFolder = fileDlg.getExistingDirectory( self, 'Scan Folder', self.scanFolder, QFileDialog.DontUseNativeDialog) self.ui.scanpath.setText(self.scanFolder) self.settings.setValue("path", self.scanFolder) self.settings.sync() def startThread(self, fn, resultFn=None, complete=None): worker = Worker( fn) # Any other args, kwargs are passed to the run function if resultFn is not None: worker.signals.result.connect(resultFn) if complete is not None: worker.signals.finished.connect(complete) #worker.signals.progress.connect(self.scannerProgress) self.threadpool.start(worker) def thread_complete(self): self.scannerProgress(100) time.sleep(1) self.dialog.close() if self.dev_available: self.show() else: #TODO: put error dlg here self.message.setText( "No scanner found\n Check your Configuration!") self.message.exec() print("Error: No Devices found") print("THREAD COMPLETE! ", self.threadpool.activeThreadCount()) @pyqtSlot(str) def scanPathCanged(self, path): self.scanPath = path self.createCompleter() def createCompleter(self): ff = glob.glob(self.scanPath + "/*.pdf") files = [] for f in ff: files.append(os.path.basename(f).split('.')[0]) completer = QCompleter(files) completer.setCaseSensitivity(Qt.CaseInsensitive) self.ui.filename.setCompleter(completer) def scannerLookup(self): #self.dialog.setModal(True) self.uidlg = Ui_Dialog() self.uidlg.setupUi(self.dialog) self.dialog.show() def checkScanMode(self): mode = "" if self.ui.btnBuW.isChecked(): mode = self.ui.btnBuW.text() elif self.ui.btnGray.isChecked(): mode = self.ui.btnGray.text() elif self.ui.btnColor.isChecked(): mode = self.ui.btnColor.text() print(mode) return mode def scannerAddToDlg(self, result): self.devices = result if len(self.devices) > 0: self.dev_available = True for i, dev in enumerate(result): self.ui.comboBox.addItem(dev[i]) else: return def scannerProgress(self, val): self.uidlg.progressBar.setValue(val) def scanners(self): self.scannerLookup() self.startThread(sane.get_devices, self.scannerAddToDlg, self.thread_complete) def scannerCheck(self): self.statusBar().showMessage("looking up for scanner ....") print(self.devices[0]) count = 0 while self.is_dev: try: #ToDo: check index from self.dev = sane.open(self.devices[0][0]) except: print("no scanner connected, waiting...", self.dev) if self.dev is not None: self.is_dev = False self.dev_connected = True print("scanner connected") time.sleep(3) ## Stop process after 3 times to avoid endless loop if no device is available ## due started as thread count += 1 if count > 2: self.is_dev = False self.statusBar().showMessage("No Scanner connected!") def commonThreadEnd(self): print("Thread ended") def scanDocThreadEnded(self): self.statusBar().showMessage("Job stopped") self.scanStatus = False self.setLedStatus() def scannerCheckThreadEnd(self): print("Lookup Thread ended") if self.dev_connected: self.startThread(self.scanDocuments, None, self.scanDocThreadEnded) def setScannerStatus(self): if self.dev_connected: self.setLedStatus() self.statusBar().showMessage("Scanner connected", 10) self.setScanButton("running") else: self.setScanButton('stopped') def setLedStatus(self): if self.scanStatus: pix = QPixmap(":/images/square_green.svg") else: pix = QPixmap(":/images/square_red.svg") self.ui.statusLed.setPixmap(pix) def scanDocuments(self): ip = self.devices[0][0].split('=')[1] print(ip) url = 'http://' + ip + XML_PATH self.dev.mode = self.checkScanMode() self.dev.resolution = int(self.ui.resolutions.currentText()) imgNr = 0 savePath = self.ui.scanpath.text() + "/" imgPrefix = self.ui.filename.text() + "_" #self.dev.contrast = 900 #self.dev.brightness = self.brightness while self.scanStatus: btnreq = urlopen(url) soup = bs4.BeautifulSoup(str(btnreq.read()), 'lxml') if soup.startscan.string == str(1): #print("Pressed") if soup.adfloaded.string == str(1): self.adf = True print("ADF Source") self.dev.source = 'ADF' imIter = self.dev.multi_scan() while self.adf: try: im = imIter.next() imgNr = imgNr + 1 img = imgPrefix + str(imgNr) + ".png" im.save(savePath + img) if self.ocr: self.ocrFiles.append(savePath + img) except: self.adf = False break else: self.adf = False imgNr = imgNr + 1 img = imgPrefix + str(imgNr) + ".png" self.dev.start() im = self.dev.snap() self.enhanceImage(im, savePath, img) if self.ocr: self.ocrFiles.append(savePath + img) time.sleep(3) def enhanceImage(self, image, path, pf): brightness = ImageEnhance.Brightness(image) image = brightness.enhance(float(self.brightness)) contrast = ImageEnhance.Contrast(image) image = contrast.enhance(float(self.contrast)) colour = ImageEnhance.Color(image) image = colour.enhance(float(self.color)) sharpness = ImageEnhance.Sharpness(image) image = sharpness.enhance(float(self.sharpness)) print("image saved ", self.ui.scanpath.text() + "/" + pf) image.save(path + pf) @pyqtSlot() def leditcolor(self): self.ui.scanpath.setStyleSheet("background-color:rgb(255, 255, 255)") self.ui.filename.setStyleSheet("background-color:rgb(255, 255, 255)") def startScanJob(self): # print("Path: ",self.scanpath.text()) # print("Mode: ",mode) # print("Resolution: ", self.resolutions.currentText()) # print("File: ", self.filename.text()) if len(self.ui.scanpath.text()) == 0: msg = QMessageBox() msg.setText("Please enter file path!") msg.exec() self.ui.scanpath.setStyleSheet( "background-color:rgb(255, 170, 127)") return if len(self.ui.filename.text()) == 0: msg = QMessageBox() msg.setText("Please enter file name prefix!") msg.exec() self.ui.filename.setStyleSheet( "background-color:rgb(255, 170, 127)") return if not self.scanStatus: self.setScanButton("starting") self.startThread(self.scannerCheck, self.setScannerStatus, self.scannerCheckThreadEnd) self.scanStatus = True self.ui.scanpath.setEnabled(False) self.ui.filename.setEnabled(False) else: self.scanStatus = False self.setScanButton("stopped") self.ui.scanpath.setEnabled(True) self.ui.filename.setEnabled(True) self.ui.filename.clear() if self.ocr and len(self.ocrFiles) > 0: self.ui.btnOcr.setEnabled(True) self.tempocr = tempfile.NamedTemporaryFile(delete=False) for f in self.ocrFiles: self.tempocr.write(str(f + "\n").encode()) self.tempocr.close() def setScanButton(self, status): if status == "starting": self.btnStyle = self.ui.btnStartscan.styleSheet() self.ui.btnStartscan.setStyleSheet("background-color: yellow") self.ui.btnStartscan.setText("Starting...") if status == "running": self.ui.btnStartscan.setStyleSheet("background-color: red") self.ui.btnStartscan.setText("Stop") self.statusBar().showMessage("Scan job is running..") if status == "stopped": self.ui.btnStartscan.setText("Sart Scan") self.ui.btnStartscan.setStyleSheet(self.btnStyle) def configureWindow(self): if self.dev is not None: self.configWin.ui.scanButton.setEnabled(True) self.configWin.ui.scanButton.setText("Start Scan") else: self.configWin.ui.scanButton.setEnabled(False) self.configWin.ui.scanButton.setText("Sart Scan Service first") self.configWin.show() def ocr_startProcess(self): self.progressDlg = QProgressDialog(self) self.progressDlg.setWindowTitle("OCR Process") self.progressDlg.setLabelText("OCR Process in Progress ...") self.progressDlg.setAutoClose(False) self.progressDlg.setAutoReset(False) self.progressDlg.setModal(True) self.startThread(self.ocr_process, None, self.ocr_stopped) def ocr_process(self): if len(self.ocrFiles) > 0: val = 0 ### add one more for pdf create process max = len(self.ocrFiles) + 1 self.progressDlg.setRange(0, max) for f in self.ocrFiles: val += 1 self.progressDlg.setValue(val) #TODO: if is checked ocrt.deskew(f) #TODO: if is checked #NOTE: this is crop and resize in one step # size and dpi are predifined to A4 300 print(self.cropSize) if self.cropSize['width'] > 1: ocrt.crop_resize(f, self.cropSize["left"], self.cropSize["top"], self.cropSize["width"], self.cropSize["height"]) #TODO: if is checked ocrt.check_orientation(f) print(self.tempocr.name) ## works in python 3.9+ #pdfname = self.ocrFiles[0].removesuffix("_1.png") pdfname, suff = self.ocrFiles[0].rsplit("_1.png") print(pdfname) ### this runs in its own process self.progressDlg.setLabelText("OCR Process finishing ...") ocrt.create_pdf(self.tempocr.name, pdfname) #### ## Workaround to get a correct finished process ## while pytesseract uses subprocces which can not be handled in this thread #### while not os.path.isfile(pdfname + ".pdf"): time.sleep(3) self.progressDlg.setValue(val + 1) os.unlink(self.tempocr.name) self.ocrFiles.clear() else: return def ocr_stopped(self): print("OCR finished") self.progressDlg.close() def configScan(self): self.dev.resolution = int(self.ui.resolutions.currentText()) self.dev.mode = self.checkScanMode() self.dev.start() im = self.dev.snap() pix = ImageQt.ImageQt(im.convert('RGBA')) #self.configWin.im = im #self.configWin.ui.view.setPixmap(self.configWin.pixmap.fromImage(pix)) self.configWin.pixmapItem.setPixmap(QPixmap.fromImage(pix)) self.configWin.ui.view.fitInView(self.configWin.pixmapItem, Qt.KeepAspectRatio) self.configWin.pixmapItem.grabMouse() self.configWin.setBufferImage() self.configWin.enhanceImage() #im = None @pyqtSlot() def saveConfig(self): self.brightness = self.configWin.ui.brigthnessLcd.value() self.contrast = self.configWin.ui.contrastLcd.value() self.color = self.configWin.ui.colorLcd.value() self.sharpness = self.configWin.ui.sharpnessLcd.value() self.crop = self.configWin.ui.checkCrop.isChecked() self.settings.setValue('brightness', self.brightness) self.settings.setValue('contrast', self.contrast) self.settings.setValue('color', self.color) self.settings.setValue('sharpness', self.sharpness) self.settings.setValue('ocr', self.ocr) self.settings.setValue('crop', self.crop) self.settings.setValue('cropSize', self.cropSize) self.settings.sync() self.configWin.close() @pyqtSlot(int) def ocrConfig(self, state): if state == Qt.Checked: self.ocr = True self.configWin.ui.OCR_Box.setEnabled(True) else: self.ocr = False self.configWin.ui.OCR_Box.setEnabled(False) def closeEvent(self, e): if self.tempocr is not None: if os.path.exists(self.tempocr.name): os.unlink(self.tempocr.name) if self.configWin.isVisible(): self.configWin.close() e.accept()
def closeEvent(self, event): reg = QSettings() reg.setValue("reset wills width", self.size().width()) reg.sync()
class LoginWindow(QWidget): tryLogin = pyqtSignal(dict, name="tryLogin") loginSuccess = pyqtSignal(name="loginSuccess") def __init__(self, state, device): super().__init__() self.settings = QSettings("Capstone", "posture-of-success") self.state = state self.device = device self.is_waiting = False self.login = QNetworkAccessManager() self.login.finished.connect(self.login_response) self.error_dialog = QErrorMessage() # Setup UI self.setWindowTitle("성공의 자세") self.setWindowIcon(QIcon('icon.png')) self.setGeometry(300, 300, 560, 460) label = QLabel("로그인") label.setProperty("class", "huge") self.id_field = QLineEdit(self.settings.value("login/id", "")) self.id_field.setPlaceholderText("Email") self.pw_field = QLineEdit(self.settings.value("login/pw", "")) self.pw_field.setPlaceholderText("Password") self.pw_field.setEchoMode(QLineEdit.Password) self.pw_field.returnPressed.connect(self.login_clicked) fields_box = QVBoxLayout() fields_box.addWidget(self.id_field) fields_box.addWidget(self.pw_field) self.login_button = QPushButton("로그인") register_button = QPushButton("회원가입") register_button.setProperty("class", "inverted") self.save_login_checkbox = QCheckBox("자동 로그인") self.device_status = QLabel() grid = QGridLayout() grid.addWidget(self.save_login_checkbox, 0, 0) grid.addWidget(self.login_button, 0, 1) grid.addWidget(self.device_status, 1, 0) grid.addWidget(register_button, 1, 1) grid.setSpacing(20) login_box = QVBoxLayout() login_box.addStretch(1) login_box.addWidget(label) login_box.addLayout(fields_box) login_box.addLayout(grid) login_box.addStretch(1) login_box.setContentsMargins(50, 50, 50, 50) login_box.setSpacing(20) frame = QWidget() frame.setLayout(login_box) frame.setProperty("class", "frame") main_layout = QVBoxLayout() main_layout.addWidget(frame) main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(main_layout) self.setProperty("class", "root") self.setContentsMargins(0, 0, 0, 0) self.setAttribute(Qt.WA_TranslucentBackground) # self.setWindowFlags(Qt.FramelessWindowHint) # Connect self.update_status() self.device.connectedChanged.connect(self.update_status) self.login_button.clicked.connect(self.login_clicked) register_button.clicked.connect(register) if self.settings.value("login/id", "") != "": self.save_login_checkbox.setChecked(True) self.login_clicked() def login_clicked(self): if not self.is_waiting: print("login clicked") self.is_waiting = True self.login_button.setEnabled(False) data = { "email": self.id_field.text(), "password": self.pw_field.text() } req = QNetworkRequest(QUrl(SERVER_BASE_ADDR + "/api/device/signin")) req.setRawHeader("Content-Type".encode('utf-8'), "application/json".encode('utf-8')) self.login.post(req, json.dumps(data).encode('utf-8')) def login_response(self, response: QNetworkReply): self.is_waiting = False self.login_button.setEnabled(True) err = response.error() if err == QNetworkReply.NoError: reply = str(response.readAll(), 'utf-8') reply_json = json.loads(reply) print(reply_json) if "success" in reply_json and reply_json["success"]: self.state.login(reply_json["email"], reply_json["score"]) self.state.sensor_values = eval(reply_json["sensor_data"]) self.loginSuccess.emit() self.close() else: self.error_dialog.showMessage('아이디나 비밀번호가 맞지 않습니다!') else: self.error_dialog.showMessage("서버 연결에 실패했습니다. 에러 코드=" + str(err)) def save_login(self, id, pw): self.settings.setValue("login/id", id) self.settings.setValue("login/pw", pw) self.settings.sync() def update_status(self): if self.device.is_connected(): self.device_status.setText("장치가 연결되었습니다.") else: self.device_status.setText("장치 연결을 기다리는 중...") def logout(self): self.state.logout() self.show() def closeEvent(self, event: QCloseEvent): if self.save_login_checkbox.isChecked(): print("saving login info") self.save_login(self.id_field.text(), self.pw_field.text()) else: print("deleting login info") self.save_login("", "") if not self.state.is_logged_in(): QCoreApplication.exit()
def save_settings(self): settings = QSettings() settings.setValue('settings/path_train', self._path_train) settings.setValue('settings/key_name', self._key_name) settings.setValue('settings/key_index', self._key_idx) settings.sync()
class TasmotaDevicesModel(QAbstractTableModel): def __init__(self, *args, **kwargs): super(TasmotaDevicesModel, self).__init__(*args, **kwargs) self.settings = QSettings() self.settings.beginGroup("Devices") self._devices = [] for d in self.settings.childGroups(): self.loadDevice(d, self.settings.value("{}/full_topic".format(d)), self.settings.value("{}/friendly_name".format(d))) self.settings.endGroup() def addDevice(self, topic, full_topic, lwt="undefined"): rc = self.rowCount() self.beginInsertRows(QModelIndex(), rc, rc) self._devices.append([lwt, topic, full_topic, topic] + ([''] * (len(columns) - 4))) self.settings.beginGroup("Devices") self.settings.setValue("{}/full_topic".format(topic), full_topic) self.settings.setValue("{}/friendly_name".format(topic), full_topic) self.settings.endGroup() self.endInsertRows() return self.index(rc, 0) def loadDevice(self, topic, full_topic, friendly_name="", lwt="undefined"): rc = self.rowCount() self.beginInsertRows(QModelIndex(), rc, rc) self._devices.append([ lwt, topic, full_topic, friendly_name if friendly_name else topic ] + ([''] * (len(columns) - 4))) self.endInsertRows() return True def findDevice(self, topic): split_topic = topic.split('/') possible_topic = split_topic[1] if possible_topic in ('tele', 'stat'): possible_topic = split_topic[0] for i, d in enumerate(self._devices): full_topic = d[DevMdl.FULL_TOPIC] + "(?P<reply>.*)" full_topic = full_topic.replace("%topic%", "(?P<topic>.*?)") full_topic = full_topic.replace("%prefix%", "(?P<prefix>.*?)") match = re.fullmatch(full_topic, topic) if match: found = match.groupdict() if found['topic'] == d[DevMdl.TOPIC]: found.update({'index': self.index(i, DevMdl.LWT)}) return found_obj(found) return found_obj({ 'index': QModelIndex(), 'topic': possible_topic, 'reply': split_topic[-1] }) def columnCount(self, parent=None): return len(columns) def rowCount(self, parent=None): return len(self._devices) def insertRows(self, pos, rows, parent=QModelIndex()): self.beginInsertRows(parent, pos, pos + rows - 1) for i in range(rows): self._devices.append(['undefined'] + ([''] * (len(columns) - 1))) self.endInsertRows() return True def removeRows(self, pos, rows, parent=QModelIndex()): if pos + rows <= self.rowCount(): self.beginRemoveRows(parent, pos, pos + rows - 1) for r in range(rows): d = self._devices[pos][DevMdl.TOPIC] self.settings.beginGroup("Devices") if d in self.settings.childGroups(): self.settings.remove(d) self.settings.endGroup() self._devices.pop(pos + r) self.endRemoveRows() return True return False def headerData(self, col, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if col <= len(columns): return columns[col][0] else: return '' def data(self, idx, role=Qt.DisplayRole): if idx.isValid(): row = idx.row() col = idx.column() if role in (Qt.DisplayRole, Qt.EditRole): val = self._devices[row][col] if val and col == DevMdl.UPTIME: if val.startswith("0T"): val = val.replace('0T', '') return val.replace('T', 'd ') elif val and col == DevMdl.MODULE: return modules.get(val, 'Unknown') elif val and col == DevMdl.FIRMWARE: return val.replace('(', ' (') elif col == DevMdl.LOADAVG: if val: return val return "n/a" if self._devices[row][ DevMdl.LWT] == 'online' else '' elif col == DevMdl.BSSID: alias = self.settings.value("BSSID/{}".format(val)) if alias: return alias return self._devices[row][col] elif role == Qt.TextAlignmentRole: if col in (DevMdl.RSSI, DevMdl.MAC, DevMdl.IP, DevMdl.SSID, DevMdl.BSSID, DevMdl.CHANNEL, DevMdl.POWER, DevMdl.LOADAVG, DevMdl.CORE, DevMdl.TELEPERIOD): return Qt.AlignCenter elif col == DevMdl.UPTIME: return Qt.AlignRight | Qt.AlignVCenter elif col == DevMdl.RESTART_REASON: return Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap elif role == Qt.BackgroundColorRole and col == DevMdl.RSSI: rssi = self._devices[row][DevMdl.RSSI] if rssi: rssi = int(rssi) if rssi < 50: return QColor("#ef4522") elif rssi > 75: return QColor("#7eca27") else: return QColor("#fcdd0f") elif role == Qt.ToolTipRole: if col == DevMdl.FIRMWARE: return self._devices[row][DevMdl.FIRMWARE] elif col == DevMdl.FRIENDLY_NAME: return "Topic: {}\nFull topic: {}".format( self._devices[row][DevMdl.TOPIC], self._devices[row][DevMdl.FULL_TOPIC]) def setData(self, idx, val, role=Qt.EditRole): row = idx.row() col = idx.column() if role == Qt.EditRole: dev = self._devices[row][DevMdl.TOPIC] old_val = self._devices[row][col] if val != old_val: self.settings.beginGroup("Devices") if col == DevMdl.FRIENDLY_NAME: self.settings.setValue("{}/friendly_name".format(dev), val) elif col == DevMdl.FULL_TOPIC: self.settings.setValue("{}/full_topic".format(dev), val) self.settings.endGroup() self._devices[row][col] = val self.dataChanged.emit(idx, idx) self.settings.sync() return True return False def flags(self, idx): return Qt.ItemIsSelectable | Qt.ItemIsEnabled def updateValue(self, idx, column, val): if idx.isValid(): row = idx.row() idx = self.index(row, column) self.setData(idx, val) def topic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.TOPIC] return None def friendly_name(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FRIENDLY_NAME] return None def commandTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "cmnd").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def statTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "stat").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def teleTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "tele").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def isDefaultTemplate(self, idx): if idx.isValid(): return self._devices[idx.row()][DevMdl.FULL_TOPIC] in [ "%prefix%/%topic%/", "%topic%/%prefix%/" ] def bssid(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.BSSID] return None def power(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.POWER] return None def refreshBSSID(self): first = self.index(0, DevMdl.BSSID) last = self.index(self.rowCount(), DevMdl.BSSID) self.dataChanged.emit(first, last)
class SettingsWindow(QtWidgets.QWidget, settingsUI.Ui_settingsForm): def __init__(self): super().__init__() self.setupUi(self) self.settings = QSettings("trik-maze-gui-gen", "preferences") if self.settings.value('locale', 'ru') == 'en': self.setEnglish() else: self.setRussian() self.colorLabel.mousePressEvent = (self.controlColor) self.telegramChannel.mousePressEvent = (self.copyLink) self.colorLine = self.settings.value('colorLineValue', '000000') self.colorLabel.setStyleSheet('QLabel {background-color: #' + str(self.colorLine) + ';}') self.lineSlider.valueChanged.connect(self.updateValueLine) self.mazeSlider.valueChanged.connect(self.updateValueMaze) self.MazeLoopsCheckBox.stateChanged.connect(self.updateValueMazeLoops) self.excersizeTime.timeChanged.connect(self.updateValueTimeLimit) self.lineSlider.setValue( self.settings.value('lineCellSizeValue', type=int)) self.mazeSlider.setValue( self.settings.value('mazeCellSizeValue', type=int)) self.MazeLoopsCheckBox.setCheckState( self.settings.value('mazeLoopsValue', type=QtCore.Qt.CheckState)) self.excersizeTime.setTime( self.settings.value('timeLimitValue', type=QtCore.QTime)) def copyLink(self, event): pyperclip.copy('https://t.me/maze_gui_gen') def updateValueMaze(self): # <html><head/><body><p align="center">2</p></body></html> value = "<html><head/><body><p align=\"center\">" + str( self.getSliderMaze()) + "</p></body></html>" self.mazeCellSizeValue.setText(value) self.settings.setValue('mazeCellSizeValue', self.mazeSlider.value()) self.settings.sync() def updateValueLine(self): value = "<html><head/><body><p align=\"center\">" + str( self.getSliderLine()) + "</p></body></html>" self.lineCellSizeValue.setText(value) self.settings.setValue('lineCellSizeValue', self.lineSlider.value()) self.settings.sync() def updateValueMazeLoops(self): self.settings.setValue('mazeLoopsValue', self.MazeLoopsCheckBox.checkState()) self.settings.sync() def updateValueTimeLimit(self): self.settings.setValue('timeLimitValue', self.excersizeTime.time()) self.settings.sync() def getSliderMaze(self) -> int: return self.mazeSlider.value() def rgb_to_hex(self, rgb): return '%02x%02x%02x' % rgb def getSliderLine(self) -> int: return self.lineSlider.value() def getMazeCheckBox(self): return self.MazeLoopsCheckBox.isChecked() def getTimelimit(self, event): v = self.excersizeTime.time().toString() v = [int(vi) for vi in v.split(':')][1:3] return v def controlColor(self, event): color = QtWidgets.QColorDialog.getColor() if color: rgb = (color.getRgb()[0:3]) hex_color = self.rgb_to_hex(rgb) self.colorLabel.setStyleSheet('QLabel {background-color: #' + str(hex_color) + ';}') self.colorLine = hex_color self.settings.setValue('colorLineValue', hex_color) self.settings.sync() def setRussian(self): self.MazeLoopsLabel.setText('Лабиринт с циклами') self.groupBox.setTitle('Настройки для генерирования полей') self.lineCellSizeLabel.setText('Размер ячейки с линией') self.mazeCellSizeLabel.setText('Размер ячейки для лабиринта') self.timelimitLabel.setText('Временное ограничение для задания') self.lineColorLabel.setText('Цвет линии') self.InfoLabel.setText( 'По вопросам и проблемам свяжитесь со мной в telegram: @robot_lev') self.telegramChannel.setText( 'Нажмите, чтобы скопировать ссылку на telegram канал: https://t.me/maze_gui_gen' ) def setEnglish(self): self.MazeLoopsLabel.setText('Maze with loops') self.groupBox.setTitle('Settings for fields generation') self.lineCellSizeLabel.setText('Line cell size') self.mazeCellSizeLabel.setText('Maze cell size') self.timelimitLabel.setText('Timelimit for excersize') self.lineColorLabel.setText('Line color') self.InfoLabel.setText( 'For any issues contact me on telegram: @robot_lev') self.telegramChannel.setText( 'Press to copy link to telegram channel: https://t.me/maze_gui_gen' )
import sys import inspect from os.path import dirname, abspath, join, exists #when in CLI use inspect to locate the source directory src_dir = join(dirname(abspath(inspect.getfile(inspect.currentframe()))), 'src') sys.path.append(src_dir) __author__ = 'saflores' from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings from gui.ui import UI import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ##### check if settings exist, otherwise initialize them ##### mySettings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'Sogeti', 'validate') if not exists(mySettings.fileName()): mySettings.setValue("report/imageQuality", 120) mySettings.sync() print("settings written to " + mySettings.fileName()) ############################################################## ui = UI(MainWindow) MainWindow.show() sys.exit(app.exec_())
'style': 'Fusion', 'color-scheme': 'Persepolis Light Blue', 'icons': 'Papirus-Light', 'font': 'Ubuntu', 'font-size': 9, 'aria2_path': '' } # this loop is checking values in persepolis_setting . if value is not # valid then value replaced by default_setting_dict value for key in default_setting_dict.keys(): setting_value = persepolis_setting.value(key, default_setting_dict[key]) persepolis_setting.setValue(key, setting_value) persepolis_setting.sync() # this section creates temporary download folder and download folder and # download sub folders if they did not existed. download_path_temp = persepolis_setting.value('download_path_temp') download_path = persepolis_setting.value('download_path') folder_list = [download_path_temp, download_path] # add subfolders to folder_list if user checked subfolders check box in setting window. if persepolis_setting.value('subfolder') == 'yes': for folder in ['Audios', 'Videos', 'Others', 'Documents', 'Compressed']: folder_list.append(os.path.join(download_path, folder)) # create folders in folder_list for folder in folder_list:
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._version = __version__ self.setWindowIcon(QIcon(":/logo.png")) self.setWindowTitle("Tasmota Device Manager {}".format(self._version)) self.unknown = [] self.env = TasmotaEnvironment() self.device = None self.topics = [] self.mqtt_queue = [] self.fulltopic_queue = [] # ensure TDM directory exists in the user directory if not os.path.isdir("{}/TDM".format(QDir.homePath())): os.mkdir("{}/TDM".format(QDir.homePath())) self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat) self.setMinimumSize(QSize(1000, 600)) # configure logging logging.basicConfig(filename="{}/TDM/tdm.log".format(QDir.homePath()), level=self.settings.value("loglevel", "INFO"), datefmt="%Y-%m-%d %H:%M:%S", format='%(asctime)s [%(levelname)s] %(message)s') logging.info("### TDM START ###") # load devices from the devices file, create TasmotaDevices and add the to the envvironment for mac in self.devices.childGroups(): self.devices.beginGroup(mac) device = TasmotaDevice(self.devices.value("topic"), self.devices.value("full_topic"), self.devices.value("friendly_name")) device.debug = self.devices.value("debug", False, bool) device.p['Mac'] = mac.replace("-", ":") device.env = self.env self.env.devices.append(device) # load device command history self.devices.beginGroup("history") for k in self.devices.childKeys(): device.history.append(self.devices.value(k)) self.devices.endGroup() self.devices.endGroup() self.device_model = TasmotaDevicesModel(self.env) self.setup_mqtt() self.setup_main_layout() self.add_devices_tab() self.build_mainmenu() # self.build_toolbars() self.setStatusBar(QStatusBar()) pbSubs = QPushButton("Show subscriptions") pbSubs.setFlat(True) pbSubs.clicked.connect(self.showSubs) self.statusBar().addPermanentWidget(pbSubs) self.queue_timer = QTimer() self.queue_timer.timeout.connect(self.mqtt_publish_queue) self.queue_timer.start(250) self.auto_timer = QTimer() self.auto_timer.timeout.connect(self.auto_telemetry) self.load_window_state() if self.settings.value("connect_on_startup", False, bool): self.actToggleConnect.trigger() self.tele_docks = {} self.consoles = [] def setup_main_layout(self): self.mdi = QMdiArea() self.mdi.setActivationOrder(QMdiArea.ActivationHistoryOrder) self.mdi.setTabsClosable(True) self.setCentralWidget(self.mdi) def setup_mqtt(self): self.mqtt = MqttClient() self.mqtt.connecting.connect(self.mqtt_connecting) self.mqtt.connected.connect(self.mqtt_connected) self.mqtt.disconnected.connect(self.mqtt_disconnected) self.mqtt.connectError.connect(self.mqtt_connectError) self.mqtt.messageSignal.connect(self.mqtt_message) def add_devices_tab(self): self.devices_list = ListWidget(self) sub = self.mdi.addSubWindow(self.devices_list) sub.setWindowState(Qt.WindowMaximized) self.devices_list.deviceSelected.connect(self.selectDevice) self.devices_list.openConsole.connect(self.openConsole) self.devices_list.openRulesEditor.connect(self.openRulesEditor) self.devices_list.openTelemetry.connect(self.openTelemetry) self.devices_list.openWebUI.connect(self.openWebUI) def load_window_state(self): wndGeometry = self.settings.value('window_geometry') if wndGeometry: self.restoreGeometry(wndGeometry) def build_mainmenu(self): mMQTT = self.menuBar().addMenu("MQTT") self.actToggleConnect = QAction(QIcon(":/disconnect.png"), "Connect") self.actToggleConnect.setCheckable(True) self.actToggleConnect.toggled.connect(self.toggle_connect) mMQTT.addAction(self.actToggleConnect) mMQTT.addAction(QIcon(), "Broker", self.setup_broker) mMQTT.addAction(QIcon(), "Autodiscovery patterns", self.patterns) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Clear obsolete retained LWTs", self.clear_LWT) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Auto telemetry period", self.auto_telemetry_period) self.actToggleAutoUpdate = QAction(QIcon(":/auto_telemetry.png"), "Auto telemetry") self.actToggleAutoUpdate.setCheckable(True) self.actToggleAutoUpdate.toggled.connect(self.toggle_autoupdate) mMQTT.addAction(self.actToggleAutoUpdate) mSettings = self.menuBar().addMenu("Settings") mSettings.addAction(QIcon(), "BSSId aliases", self.bssid) mSettings.addSeparator() mSettings.addAction(QIcon(), "Preferences", self.prefs) # mExport = self.menuBar().addMenu("Export") # mExport.addAction(QIcon(), "OpenHAB", self.openhab) def build_toolbars(self): main_toolbar = Toolbar(orientation=Qt.Horizontal, iconsize=24, label_position=Qt.ToolButtonTextBesideIcon) main_toolbar.setObjectName("main_toolbar") def initial_query(self, device, queued=False): for c in initial_commands(): cmd, payload = c cmd = device.cmnd_topic(cmd) if queued: self.mqtt_queue.append([cmd, payload]) else: self.mqtt.publish(cmd, payload, 1) def setup_broker(self): brokers_dlg = BrokerDialog() if brokers_dlg.exec_() == QDialog.Accepted and self.mqtt.state == self.mqtt.Connected: self.mqtt.disconnect() def toggle_autoupdate(self, state): if state == True: if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) self.auto_timer.setInterval(self.settings.value("autotelemetry", 5000, int)) self.auto_timer.start() else: self.auto_timer.stop() def toggle_connect(self, state): if state and self.mqtt.state == self.mqtt.Disconnected: self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) self.mqtt.connectToHost() elif not state and self.mqtt.state == self.mqtt.Connected: self.mqtt_disconnect() def auto_telemetry(self): if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) def mqtt_connect(self): self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) if self.mqtt.state == self.mqtt.Disconnected: self.mqtt.connectToHost() def mqtt_disconnect(self): self.mqtt.disconnectFromHost() def mqtt_connecting(self): self.statusBar().showMessage("Connecting to broker") def mqtt_connected(self): self.actToggleConnect.setIcon(QIcon(":/connect.png")) self.actToggleConnect.setText("Disconnect") self.statusBar().showMessage("Connected to {}:{} as {}".format(self.broker_hostname, self.broker_port, self.broker_username if self.broker_username else '[anonymous]')) self.mqtt_subscribe() def mqtt_subscribe(self): # clear old topics self.topics.clear() custom_patterns.clear() # load custom autodiscovery patterns self.settings.beginGroup("Patterns") for k in self.settings.childKeys(): custom_patterns.append(self.settings.value(k)) self.settings.endGroup() # expand fulltopic patterns to subscribable topics for pat in default_patterns: # tasmota default and SO19 self.topics += expand_fulltopic(pat) # check if custom patterns can be matched by default patterns for pat in custom_patterns: if pat.startswith("%prefix%") or pat.split('/')[1] == "%prefix%": continue # do nothing, default subcriptions will match this topic else: self.topics += expand_fulltopic(pat) for d in self.env.devices: # if device has a non-standard pattern, check if the pattern is found in the custom patterns if not d.is_default() and d.p['FullTopic'] not in custom_patterns: # if pattern is not found then add the device topics to subscription list. # if the pattern is found, it will be matched without implicit subscription self.topics += expand_fulltopic(d.p['FullTopic']) # passing a list of tuples as recommended by paho self.mqtt.subscribe([(topic, 0) for topic in self.topics]) @pyqtSlot(str, str) def mqtt_publish(self, t, p): self.mqtt.publish(t, p) def mqtt_publish_queue(self): for q in self.mqtt_queue: t, p = q self.mqtt.publish(t, p) self.mqtt_queue.pop(self.mqtt_queue.index(q)) def mqtt_disconnected(self): self.actToggleConnect.setIcon(QIcon(":/disconnect.png")) self.actToggleConnect.setText("Connect") self.statusBar().showMessage("Disconnected") def mqtt_connectError(self, rc): reason = { 1: "Incorrect protocol version", 2: "Invalid client identifier", 3: "Server unavailable", 4: "Bad username or password", 5: "Not authorized", } self.statusBar().showMessage("Connection error: {}".format(reason[rc])) self.actToggleConnect.setChecked(False) def mqtt_message(self, topic, msg): # try to find a device by matching known FullTopics against the MQTT topic of the message device = self.env.find_device(topic) if device: if topic.endswith("LWT"): if not msg: msg = "Offline" device.update_property("LWT", msg) if msg == 'Online': # known device came online, query initial state self.initial_query(device, True) else: # forward the message for processing device.parse_message(topic, msg) if device.debug: logging.debug("MQTT: %s %s", topic, msg) else: # unknown device, start autodiscovery process if topic.endswith("LWT"): self.env.lwts.append(topic) logging.info("DISCOVERY: LWT from an unknown device %s", topic) # STAGE 1 # load default and user-provided FullTopic patterns and for all the patterns, # try matching the LWT topic (it follows the device's FullTopic syntax for p in default_patterns + custom_patterns: match = re.fullmatch(p.replace("%topic%", "(?P<topic>.*?)").replace("%prefix%", "(?P<prefix>.*?)") + ".*$", topic) if match: # assume that the matched topic is the one configured in device settings possible_topic = match.groupdict().get('topic') if possible_topic not in ('tele', 'stat'): # if the assumed topic is different from tele or stat, there is a chance that it's a valid topic # query the assumed device for its FullTopic. False positives won't reply. possible_topic_cmnd = p.replace("%prefix%", "cmnd").replace("%topic%", possible_topic) + "FullTopic" logging.debug("DISCOVERY: Asking an unknown device for FullTopic at %s", possible_topic_cmnd) self.mqtt_queue.append([possible_topic_cmnd, ""]) elif topic.endswith("RESULT") or topic.endswith("FULLTOPIC"): # reply from an unknown device # STAGE 2 full_topic = loads(msg).get('FullTopic') if full_topic: # the device replies with its FullTopic # here the Topic is extracted using the returned FullTopic, identifying the device parsed = parse_topic(full_topic, topic) if parsed: # got a match, we query the device's MAC address in case it's a known device that had its topic changed logging.debug("DISCOVERY: topic %s is matched by fulltopic %s", topic, full_topic) d = self.env.find_device(topic=parsed['topic']) if d: d.update_property("FullTopic", full_topic) else: logging.info("DISCOVERY: Discovered topic=%s with fulltopic=%s", parsed['topic'], full_topic) d = TasmotaDevice(parsed['topic'], full_topic) self.env.devices.append(d) self.device_model.addDevice(d) logging.debug("DISCOVERY: Sending initial query to topic %s", parsed['topic']) self.initial_query(d, True) self.env.lwts.remove(d.tele_topic("LWT")) d.update_property("LWT", "Online") def export(self): fname, _ = QFileDialog.getSaveFileName(self, "Export device list as...", directory=QDir.homePath(), filter="CSV files (*.csv)") if fname: if not fname.endswith(".csv"): fname += ".csv" with open(fname, "w", encoding='utf8') as f: column_titles = ['mac', 'topic', 'friendly_name', 'full_topic', 'cmnd_topic', 'stat_topic', 'tele_topic', 'module', 'module_id', 'firmware', 'core'] c = csv.writer(f) c.writerow(column_titles) for r in range(self.device_model.rowCount()): d = self.device_model.index(r,0) c.writerow([ self.device_model.mac(d), self.device_model.topic(d), self.device_model.friendly_name(d), self.device_model.fullTopic(d), self.device_model.commandTopic(d), self.device_model.statTopic(d), self.device_model.teleTopic(d), # modules.get(self.device_model.module(d)), self.device_model.module(d), self.device_model.firmware(d), self.device_model.core(d) ]) def bssid(self): BSSIdDialog().exec_() def patterns(self): PatternsDialog().exec_() # def openhab(self): # OpenHABDialog(self.env).exec_() def showSubs(self): QMessageBox.information(self, "Subscriptions", "\n".join(sorted(self.topics))) def clear_LWT(self): dlg = ClearLWTDialog(self.env) if dlg.exec_() == ClearLWTDialog.Accepted: for row in range(dlg.lw.count()): itm = dlg.lw.item(row) if itm.checkState() == Qt.Checked: topic = itm.text() self.mqtt.publish(topic, retain=True) self.env.lwts.remove(topic) logging.info("MQTT: Cleared %s", topic) def prefs(self): dlg = PrefsDialog() if dlg.exec_() == QDialog.Accepted: update_devices = False devices_short_version = self.settings.value("devices_short_version", True, bool) if devices_short_version != dlg.cbDevShortVersion.isChecked(): update_devices = True self.settings.setValue("devices_short_version", dlg.cbDevShortVersion.isChecked()) update_consoles = False console_font_size = self.settings.value("console_font_size", 9) if console_font_size != dlg.sbConsFontSize.value(): update_consoles = True self.settings.setValue("console_font_size", dlg.sbConsFontSize.value()) console_word_wrap = self.settings.value("console_word_wrap", True, bool) if console_word_wrap != dlg.cbConsWW.isChecked(): update_consoles = True self.settings.setValue("console_word_wrap", dlg.cbConsWW.isChecked()) if update_consoles: for c in self.consoles: c.console.setWordWrapMode(dlg.cbConsWW.isChecked()) new_font = QFont(c.console.font()) new_font.setPointSize(dlg.sbConsFontSize.value()) c.console.setFont(new_font) self.settings.sync() def auto_telemetry_period(self): curr_val = self.settings.value("autotelemetry", 5000, int) period, ok = QInputDialog.getInt(self, "Set AutoTelemetry period", "Values under 5000ms may cause increased ESP LoadAvg", curr_val, 1000) if ok: self.settings.setValue("autotelemetry", period) self.settings.sync() @pyqtSlot(TasmotaDevice) def selectDevice(self, d): self.device = d @pyqtSlot() def openTelemetry(self): if self.device: tele_widget = TelemetryWidget(self.device) self.addDockWidget(Qt.RightDockWidgetArea, tele_widget) self.mqtt_publish(self.device.cmnd_topic('STATUS'), "8") @pyqtSlot() def openConsole(self): if self.device: console_widget = ConsoleWidget(self.device) self.mqtt.messageSignal.connect(console_widget.consoleAppend) console_widget.sendCommand.connect(self.mqtt.publish) self.addDockWidget(Qt.BottomDockWidgetArea, console_widget) console_widget.command.setFocus() self.consoles.append(console_widget) @pyqtSlot() def openRulesEditor(self): if self.device: rules = RulesWidget(self.device) self.mqtt.messageSignal.connect(rules.parseMessage) rules.sendCommand.connect(self.mqtt_publish) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.addSubWindow(rules) rules.setWindowState(Qt.WindowMaximized) rules.destroyed.connect(self.updateMDI) self.mqtt_queue.append((self.device.cmnd_topic("ruletimer"), "")) self.mqtt_queue.append((self.device.cmnd_topic("rule1"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Var"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Mem"), "")) @pyqtSlot() def openWebUI(self): if self.device and self.device.p.get('IPAddress'): url = QUrl("http://{}".format(self.device.p['IPAddress'])) try: webui = QWebEngineView() webui.load(url) frm_webui = QFrame() frm_webui.setWindowTitle("WebUI [{}]".format(self.device.p['FriendlyName1'])) frm_webui.setFrameShape(QFrame.StyledPanel) frm_webui.setLayout(VLayout(0)) frm_webui.layout().addWidget(webui) frm_webui.destroyed.connect(self.updateMDI) self.mdi.addSubWindow(frm_webui) self.mdi.setViewMode(QMdiArea.TabbedView) frm_webui.setWindowState(Qt.WindowMaximized) except NameError: QDesktopServices.openUrl(QUrl("http://{}".format(self.device.p['IPAddress']))) def updateMDI(self): if len(self.mdi.subWindowList()) == 1: self.mdi.setViewMode(QMdiArea.SubWindowView) self.devices_list.setWindowState(Qt.WindowMaximized) def closeEvent(self, e): self.settings.setValue("version", self._version) self.settings.setValue("window_geometry", self.saveGeometry()) self.settings.setValue("views_order", ";".join(self.devices_list.views.keys())) self.settings.beginGroup("Views") for view, items in self.devices_list.views.items(): self.settings.setValue(view, ";".join(items[1:])) self.settings.endGroup() self.settings.sync() for d in self.env.devices: mac = d.p.get('Mac') topic = d.p['Topic'] full_topic = d.p['FullTopic'] friendly_name = d.p['FriendlyName1'] if mac: self.devices.beginGroup(mac.replace(":", "-")) self.devices.setValue("topic", topic) self.devices.setValue("full_topic", full_topic) self.devices.setValue("friendly_name", friendly_name) for i, h in enumerate(d.history): self.devices.setValue("history/{}".format(i), h) self.devices.endGroup() self.devices.sync() e.accept()
class frmAccess(QDialog, Ui_frmAccess): databaseCreated = pyqtSignal(ConnectionQt) def __init__(self, module, settingsSection, settings=None, parent=None): QDialog.__init__(self, parent) if settings == None: self.settings = QSettings() else: self.settings = settings self.settingsSection = settingsSection self.module = module self.setModal(True) self.setupUi(self) self.setResources() self.resize( self.settings.value(self.settingsSection + "/qdialog_size", QSize(200, 60))) self.parent = parent self.languages = TranslationLanguageManager() self.languages.load_all() self.languages.selected = self.languages.find_by_id( self.settings.value(self.settingsSection + "/language", "en")) self.languages.qcombobox(self.cmbLanguages, self.languages.selected) self.con = ConnectionQt() #Pointer to connection self.setTitle(self.tr("Log in PostreSQL database")) self.cmbProfiles_update() current_profile = self.settings.value( self.settingsSection + "/current_profile", "") if current_profile == "": self.txtDB.setText( self.settings.value(self.settingsSection + "/db", "")) self.txtPort.setText( self.settings.value(self.settingsSection + "/port", "5432")) self.txtUser.setText( self.settings.value(self.settingsSection + "/user", "postgres")) self.txtServer.setText( self.settings.value(self.settingsSection + "/server", "127.0.0.1")) else: self.cmbProfiles.setCurrentText(current_profile) ## Reimplements QDialog.exec_ method to make an autologin if PGPASSWORD environment variable is detected. def exec_(self): try: self.password = environ['PGPASSWORD'] debug("Password automatically set from environment variable") self.txtPass.setText(self.password) self.cmdYN.accepted.emit() except: self.txtPass.setFocus() QDialog.exec_(self) self.settings.setValue(self.settingsSection + "/qdialog_size", self.size()) self.settings.sync() def setResources(self, pixmap=":/reusingcode/frmaccess_pixmap.png", icon=":/reusingcode/frmaccess_icon.png", database_new=":/reusingcode/database_new.png", profile_new=":/reusingcode/profile_new.png", profile_update=":/reusingcode/profile_update.png", profile_delete=":/reusingcode/button_cancel.png"): self.lblPixmap.setPixmap(QPixmap(pixmap)) self.setWindowIcon(QIcon(icon)) self.cmdDatabaseNew.setIcon(QIcon(database_new)) self.cmdProfileNew.setIcon(QIcon(profile_new)) self.cmdProfileUpdate.setIcon(QIcon(profile_update)) self.cmdProfileDelete.setIcon(QIcon(profile_delete)) def setTitle(self, text): self.setWindowTitle(text) def setLabel(self, text): self.lbl.setText(text) def setLanguagesVisible(self, boolean): if boolean == False: self.lblLanguage.hide() self.cmbLanguages.hide() def setProfilesVisible(self, boolean): if boolean == False: self.lineProfile.hide() self.lblProfile.hide() self.cmbProfiles.hide() self.cmdProfileNew.hide() self.cmdProfileUpdate.hide() self.cmdProfileDelete.hide() @pyqtSlot(int) def on_cmbLanguages_currentIndexChanged(self, stri): self.languages.selected = self.languages.find_by_id( self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/language", self.languages.selected.id) self.languages.cambiar(self.languages.selected.id, self.module) self.retranslateUi(self) @pyqtSlot(str) def on_cmbProfiles_currentTextChanged(self, stri): self.txtDB.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/db", "xulpymoney")) self.txtPort.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/port", "5432")) self.txtUser.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/user", "postgres")) self.txtServer.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/server", "127.0.0.1")) def __save_current_profile(self): if self.cmbProfiles.currentText() == "": self.settings.setValue(self.settingsSection + "/db", self.txtDB.text()) self.settings.setValue(self.settingsSection + "/port", self.txtPort.text()) self.settings.setValue(self.settingsSection + "/user", self.txtUser.text()) self.settings.setValue(self.settingsSection + "/server", self.txtServer.text()) self.settings.setValue( self.settingsSection + "/language", self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/current_profile", "") else: self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/db", self.txtDB.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/port", self.txtPort.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/user", self.txtUser.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/server", self.txtServer.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/language", self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/current_profile", self.cmbProfiles.currentText()) self.settings.sync() @pyqtSlot() def on_cmdYN_accepted(self): self.__save_current_profile() self.con.init__create(self.txtUser.text(), self.txtPass.text(), self.txtServer.text(), self.txtPort.text(), self.txtDB.text()) self.con.connect() if self.con.is_active(): self.accept() else: qmessagebox( self.tr("Error conecting to {} database in {} server").format( self.con.db, self.con.server)) @pyqtSlot() def on_cmdYN_rejected(self): self.reject() def on_cmdProfileNew_released(self): name = qinputbox_string(self.tr("Profile name")) self.cmbProfiles.addItem(name) self.settings.setValue(self.settingsSection + "/db", self.txtDB.text()) def on_cmdProfileUpdate_released(self): before = self.cmbProfiles.currentText() after = qinputbox_string(self.tr("Profile name")) self.cmbProfiles.setItemText(self.cmbProfiles.currentIndex(), after) self.settings.remove(self.settingsSection + "_profile_" + before) self.__save_current_profile() def on_cmdProfileDelete_released(self): self.settings.remove(self.settingsSection + "_profile_" + self.cmbProfiles.currentText()) self.settings.setValue(self.settingsSection + "/current_profile", "") self.cmbProfiles_update() ## @return List of string with profile names def __list_of_profiles(self): r = [] for group in self.settings.childGroups(): if group.startswith(self.settingsSection + "_profile_"): r.append(group.replace(self.settingsSection + "_profile_", "")) print(r) return r def cmbProfiles_update(self, selected=None): profiles = self.__list_of_profiles() self.cmbProfiles.blockSignals(True) self.cmbProfiles.clear() for profile in profiles: self.cmbProfiles.addItem(profile) #Force without signals to be in -1. There were problems when 0 is selected, becouse it didn't emit anything self.cmbProfiles.setCurrentIndex(-1) if selected is None: self.cmbProfiles.blockSignals(False) else: self.cmbProfiles.blockSignals(False) self.cmbProfiles.setCurrentIndex( self.cmbProfiles.findData(selected.id)) def on_cmdDatabaseNew_released(self): respuesta = QMessageBox.warning( self, self.windowTitle(), self.tr("Do you want to create {} database in {}?".format( self.txtDB.text(), self.cmbLanguages.currentText())), QMessageBox.Ok | QMessageBox.Cancel) if respuesta == QMessageBox.Ok: admin_pg = AdminPG(self.txtUser.text(), self.txtPass.text(), self.txtServer.text(), self.txtPort.text()) if admin_pg.db_exists(self.txtDB.text()) == True: qmessagebox(self.tr("Xulpymoney database already exists")) return if admin_pg.create_db(self.txtDB.text()) == False: qmessagebox(self.newdb.error) else: self.__save_current_profile() self.con = admin_pg.connect_to_database(self.txtDB.text(), connectionqt=True) if self.con.is_active(): self.databaseCreated.emit(self.con) self.accept() else: qmessagebox( self. tr("Error conecting to {} database in {} server, after creating database" ).format(self.con.db, self.con.server))
class Backup: ''' A utility to store and restore settings for use in testing. Be careful not to lose data. ''' setname = 'settings.zst' def __init__(self, backuploc=None, theDate=None): ''' Store the directory settings in self. The initial strings for the DB are in settings. If initialize is called, they are gone. Likeise, restoration will depend on reinitializing settings ''' self.settings = QSettings('zero_substance', 'structjour') self.apisettings = QSettings('zero_substance/stockapi', 'structjour') self.chartsettings = QSettings('zero_substance/chart', 'structjour') if backuploc is None: if not os.path.exists(self.settings.value('journal')): msg = f"Journal location {self.settings.value('journal')} does not exist" logging.error(msg) raise ValueError(msg) self.rootdir = os.path.normpath( os.path.join(self.settings.value('journal'), 'backup')) else: self.rootdir = backuploc d = pd.Timestamp( theDate) if theDate is not None else pd.Timestamp.now() self.bdir = os.path.join(self.rootdir, d.strftime("backup_%Y%m%d_%H.%M.%S")) self.bu_settings = os.path.join(self.bdir, self.setname) self.dbtrade = self.settings.value('tradeDb') self.dbstructjour = self.settings.value('structjourDb') self.setkeys = [] self.setvals = [] self.apisetkeys = [] self.apisetvals = [] # print(self.bu_settings) def initializeSettings(self): ''' Remove all settings except zero_substance/structjour/journal ''' for key in self.settings.allKeys(): if key != 'journal': self.settings.remove(key) self.apisettings.clear() self.chartsettings.clear() self.settings.sync() self.apisettings.sync() self.chartsettings.sync() def createDir(self): try: if not os.path.exists(self.rootdir): os.mkdir(self.rootdir) if not os.path.exists(self.bdir): os.mkdir(self.bdir) except Exception as ex: logging.error(ex) logging.error('Failed to create backup directory. ' + str(ex)) raise ValueError(ex) pass def removePickle(self): if os.path.exists(self.bu_settings): os.remove(self.bu_settings) def initializeVars(self): self.setkeys = [] self.setvals = [] self.apisetkeys = [] self.apisetvals = [] self.chartkeys = [] self.chartvals = [] def backupDatabase(self, theDir=None): ''' Helper method for backup. If either db file is not found, change the backup dir name to include 'FAILED'. That will prevent a bad restore and retain files and settings in the directory ''' self.bdir = self.bdir if theDir is None else theDir if not os.path.exists(self.bdir): raise ValueError(f'Backup directory {self.bdir} does not exist') dbtrade2 = os.path.split(self.dbtrade)[1] dbstructjour2 = os.path.split(self.dbstructjour)[1] dbtrade2 = os.path.normpath(os.path.join(self.bdir, dbtrade2)) dbstructjour2 = os.path.normpath(os.path.join(self.bdir, dbstructjour2)) try: copyfile(self.dbtrade, dbtrade2) except FileNotFoundError: logging.error( f'Trade database does not exist at {self.dbtrade} and cannot be copied' ) changebdir = self.bdir[0:-17] + "FAILED_" + self.bdir[-17:] os.rename(self.bdir, changebdir) self.bdir = changebdir dbstructjour2 = os.path.normpath( os.path.join(self.bdir, os.path.split(dbstructjour2)[1])) else: logging.info(f'Trade database has been backed up to {dbtrade2}') if dbtrade2 != dbstructjour2: logging.info(f'Trade database has been backed up to {dbtrade2}') try: copyfile(self.dbstructjour, dbstructjour2) except FileNotFoundError: logging.error( f'Structjour database does not exist at {self.dbstructjour} and cannot be copied' ) if self.bdir.find('FAILED_') == -1: changedir = self.bdir[0:-17] + "FAILED_" + self.bdir[-17:] os.rename(self.bdir, changedir) else: logging.info( f'Structjour database has been backed up to {dbstructjour2}' ) def restoreDatabase(self, theDir=None): self.bdir = self.bdir if theDir is None else theDir if not os.path.exists(self.bdir): raise ValueError(f'Backup directory {self.bdir} does not exist.') dbtrade = self.settings.value('tradeDb') dbstructjour = self.settings.value('structjourDb') dbt = os.path.join(self.bdir, os.path.split(dbtrade)[1]) dbs = os.path.join(self.bdir, os.path.split(dbstructjour)[1]) if os.path.exists(dbt): copyfile(dbt, dbtrade) logging.info(f'Db restored {dbt}') else: logging.error(f'Backup file {dbt} does not exist.') if dbs != dbt: if os.path.exists(dbs): copyfile(dbs, dbstructjour) logging.info(f'Db restored {dbs}') else: logging.error(f'Backup file {dbt} does not exist.') def storeSettings(self, replacePickle=False): self.createDir() if os.path.exists(self.bu_settings): if not replacePickle: return self.initializeVars() self.setkeys = self.settings.allKeys() for k in self.setkeys: self.setvals.append(self.settings.value(k)) self.apisetkeys = self.apisettings.allKeys() for k in self.apisetkeys: self.apisetvals.append(self.apisettings.value(k)) self.chartkeys = self.chartsettings.allKeys() for k in self.chartkeys: self.chartvals.append(self.chartsettings.value(k)) setsnkeys = [ self.setkeys, self.setvals, self.apisetkeys, self.apisetvals, self.chartkeys, self.chartvals ] with open(self.bu_settings, "wb") as f: '''Cannot pickle qsettings objects- so we pickle a list''' pickle.dump((setsnkeys), f) logging.info( f'Settings have been backed up to file {self.bu_settings}') def restoreSettings(self, theDir=None): theDir = self.mostRecent() if theDir is None else theDir bu_settings = os.path.join(theDir, self.setname) if os.path.exists(bu_settings): with open(bu_settings, "rb") as f: setsnkeys = pickle.load(f) for k, v in zip(setsnkeys[0], setsnkeys[1]): self.settings.setValue(k, v) for k2, v2 in zip(setsnkeys[2], setsnkeys[3]): self.apisettings.setValue(k2, v2) for k2, v2 in zip(setsnkeys[4], setsnkeys[5]): self.chartsettings.setValue(k2, v2) logging.info(f'Settings backed up to file {bu_settings}') else: logging.error(f'No settings backup found at {bu_settings}') def backup(self): self.storeSettings() self.backupDatabase() def restore(self, theDir=None): self.bdir = self.mostRecent() if theDir is None else theDir self.bdir = os.path.normpath(self.bdir) if not os.path.exists(self.bdir): raise ValueError(f'Backup directory {self.bdir} does not exist') self.restoreSettings(self.bdir) self.restoreDatabase(self.bdir) def mostRecent(self): thedirs = os.listdir(self.rootdir) maxdate = '' maxdir = None for thedir in thedirs: if thedir.startswith('backup_2'): d = thedir[7:].replace('.', ':') if d > maxdate: maxdir = thedir return os.path.join(self.rootdir, maxdir) if maxdir is not None else '' def _clearJournalDir(self): '''For Testing ONLY. TODO implement a seperate backup/restore for this one string''' # jdir = self.settings('journal') self.settings.remove('journal') def _restoreJournalDir(self): '''For Testing''' pass
# valid then value replaced by default_setting_dict value for key in default_setting_dict.keys(): setting_value = persepolis_setting.value(key, default_setting_dict[key]) persepolis_setting.setValue(key, setting_value) # download files is downloading in temporary folder(download_path_temp) and then they will be moved to user download folder(download_path) after completion. # Check that mount point is available of not! if not(os.path.exists(persepolis_setting.value('download_path_temp'))): persepolis_setting.setValue('download_path_temp', default_setting_dict['download_path_temp']) if not(os.path.exists(persepolis_setting.value('download_path'))): persepolis_setting.setValue('download_path', default_setting_dict['download_path']) persepolis_setting.sync() # this section creates temporary download folder and download folder and # download sub folders if they did not existed. download_path_temp = persepolis_setting.value('download_path_temp') download_path = persepolis_setting.value('download_path') folder_list = [download_path_temp, download_path] # add subfolders to folder_list if user checked subfolders check box in setting window. if persepolis_setting.value('subfolder') == 'yes': for folder in ['Audios', 'Videos', 'Others', 'Documents', 'Compressed']: folder_list.append(os.path.join(download_path, folder)) # create folders in folder_list
def save_general_settings(self): settings = QSettings() settings.setValue(SETTINGS_VIEWER_PATH, self.input_viewer_path.text()) settings.setValue(SETTINGS_TOGGLE_PREVIEW, self.chkbox_toggle_preivew.isChecked()) settings.sync()
class FilePicker(QtWidgets.QDialog, ui_filepicker.Ui_Dialog): def __init__(self, parent=None, fs_url=u"~/", file_pattern=u'All Files (*)', title=u'FS File Picker', default_filename=None, show_save_action=False, show_dirs_only=False): super(FilePicker, self).__init__(parent) frame = inspect.stack()[1][0] self.scope = who_called_me(frame) self.setupUi(self) self.settings = QSettings("fs_filepicker", self.scope) self.setWindowIcon(QIcon(icons('fs_logo.png', origin='fs'))) self.file_icon = icons('text-x-generic.png') self.dir_icon = icons('folder.png') self.selected_dir = None self.selected_file_pattern = None self.filename = None self.wparm = None self.setWindowTitle(title) stored_fs_url = self.settings.value("fs_urls", fs_url) self.fs_home_url = u"~/" self.fs_root_url = root_url() if isinstance(fs_url, list): fs_url = fs_url + [self.fs_home_url, self.fs_root_url] else: fs_url = [fs_url] + [self.fs_home_url, self.fs_root_url] self.fs_url = fs_url[0] if isinstance(stored_fs_url, list): for _fs_url in sorted(list(set(stored_fs_url + fs_url))): if fs_url_exists(_fs_url): self.ui_fs_serverlist.addItem(_fs_url) self.active_url = self.fs_url self.authentification = None self.parent_url = self.fs_url self.fs = None self.file_list_items = [] self.directory_history = [] self.last_index = 0 self.last_dir_index = 0 self.default_filename = default_filename self.file_pattern = file_pattern self.show_save_action = show_save_action self.show_dirs_only = show_dirs_only self.button_icons() self.show_action() self.configure() self.ui_FileType.currentIndexChanged.connect(self.selection_file_type) self.ui_SelectedName.textChanged.connect(self.selection_name) self.ui_Cancel.clicked.connect(self.cancel) self.action_buttons() self.ui_FileList.itemClicked.connect(self.show_name) self.ui_mkdir.clicked.connect(self.make_dir) self.ui_other_fs.clicked.connect(self.other_fs_button) self.ui_FileList.cellClicked.connect(self.onCellClicked) self.ui_FileList.cellDoubleClicked.connect(self.onCellDoubleClicked) self.ui_FileList.setSelectionBehavior(QAbstractItemView.SelectRows) self.ui_DirList.currentIndexChanged.connect(self.selection_directory) self.ui_fs_serverlist.clicked.connect(self.fs_select_other) self.ui_fs_serverlist.customContextMenuRequested.connect(self.fs_select_other_context) # ToDo check order of calls self.active_url = self.fs_url self.select_fs() def configure(self): if isinstance(self.file_pattern, list): stored_file_type = self.settings.value("selected_file_pattern", self.file_pattern[0]) try: index = self.file_pattern.index(stored_file_type) except ValueError: index = 0 for pattern in self.file_pattern: self.ui_FileType.addItem(pattern) self.ui_FileType.setCurrentIndex(index) else: self.ui_FileType.addItems([self.file_pattern]) if self.default_filename is not None: name, extension = self.default_filename.split('.') idx = 0 for pattern in self.file_pattern: if u".{}".format(extension) in pattern: self.ui_FileType.setCurrentIndex(idx) idx += 1 if self.show_dirs_only: self.ui_label_filename.hide() self.ui_label_filetype.hide() self.ui_FileType.hide() self.ui_SelectedName.hide() def button_icons(self): """ Set icon image to button """ self.ui_mkdir.setText("") self.ui_mkdir.setIcon(QIcon(icons('folder-new.png'))) self.ui_other_fs.setText("") self.ui_other_fs.setIconSize(QtCore.QSize(64, 64)) self.ui_other_fs.setIcon(QIcon(icons('fs_logo.png', origin=u'fs'))) def other_fs_button(self): fs_url, ok = QInputDialog.getText(self, 'Other FS Urls', 'Enter FS Url:') if ok: if fs_url_exists(fs_url): self.ui_fs_serverlist.setVisible(True) all_urls = [self.ui_fs_serverlist.item(idx).text() for idx in range(self.ui_fs_serverlist.count())] if fs_url not in all_urls: self.ui_fs_serverlist.addItem(fs_url) self.save_settings() else: msg = u'"%s" Url not valid' % fs_url QErrorMessage(self).showMessage(msg) logging.info(msg) def fs_select_other(self): url = self.ui_fs_serverlist.currentItem().text() # ToDo needs a check for archives self.active_url = fs.path.forcedir(url) self.select_fs() def fs_select_other_context(self): """ simple context option, removes item from list """ url = self.ui_fs_serverlist.currentItem().text() selected = QMessageBox.information(self, u"Remove FS Dir", url, QMessageBox.Ok | QMessageBox.Cancel) if selected == QMessageBox.Ok: self.ui_fs_serverlist.clear() for item in self.settings.value("fs_urls"): if item != url: self.ui_fs_serverlist.addItem(item) self.save_settings() def select_fs(self): """ loads the selected fs to the file list ui """ self.directory_history = [] self.wparm = None if self.fs: self.fs.close() try: self.fs = fs.open_fs(self.active_url) except (IOError, fs.errors.CreateFailed): logging.error(u"{} does not exist!".format(self.active_url)) exit() try: parseresult = parse(self.active_url) except fs.opener.errors.ParseError: parseresult = None if parseresult is not None and parseresult.username is not None: self.authentification = "{}:{}@".format(parseresult.username, parseresult.password) self.active_url = self.active_url.replace(self.authentification, u"") self.browse_folder() def action_buttons(self): """ Open / Save button action connect """ try: self.ui_Action.clicked.connect(self.action) except AttributeError: pass def browse_folder(self, subdir=u"."): """ list folder in drop down """ if self.show_save_action: self.ui_Action.setEnabled(True) if self.show_dirs_only: self.ui_Action.setEnabled(True) self.ui_DirList.clear() if subdir == u".": _sub_dir = self.active_url else: _sub_dir = subdir if len(self.directory_history) == 0: self.directory_history.append(_sub_dir) for item in reversed(self.directory_history): self.ui_DirList.addItem(item) self.ui_DirList.setCurrentIndex(self.last_dir_index) def selection_directory(self): """ Fills the filenames based on file_type into a FileList, also directories """ self.wparm = None if not self.show_save_action: self.ui_SelectedName.setText(u"") QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.selected_dir = self.ui_DirList.currentText() self.ui_FileList.clearContents() file_type = self.ui_FileType.currentText() self.file_list_items = [] self.dir_list_items = [] self.ui_FileList.verticalHeader().setVisible(False) self.ui_FileList.horizontalHeader().setVisible(True) self.ui_FileList.setHorizontalHeaderLabels([u'Name', u'Size', u'Modified']) self.ui_FileList.setShowGrid(False) self.ui_FileList.setSizeAdjustPolicy( QtWidgets.QAbstractScrollArea.AdjustToContents) if self.selected_dir == self.active_url: _sel_dir = u"" else: _sel_dir = self.selected_dir # on clearing ui_DirList also index changes and makes an additional call of that method if self.ui_DirList.count() > 0: try: names = self.fs.listdir(_sel_dir) # cleanup not valid names # ToDo improve make those problematic files not clickable for item in names: _item = fs.path.combine(_sel_dir, item) try: self.fs.isdir(_item) except (TypeError, fs.errors.PermissionDenied): names.remove(item) logging.error("item name %s removed from list" % item) for item in sorted(names): _item = fs.path.combine(_sel_dir, item) try: self.selected_file_pattern = get_extension_from_string(file_type) if not self.fs.isdir(_item) and match_extension(item, self.selected_file_pattern): if not self.show_dirs_only: info = self.get_info(_item) self.file_list_items.append({_item: info}) elif self.fs.isdir(_item): info = self.get_info(_item) self.dir_list_items.append({_item: info}) except (fs.errors.PermissionDenied, fs.errors.OperationFailed): logging.info(u"can't access {}".format(item)) except UnicodeDecodeError as e: logging.error(u"Error: {}".format(e)) self.ui_FileList.setRowCount(len(self.file_list_items) + len(self.dir_list_items)) index = 0 for item in self.dir_list_items: self.table_row(item, index, self.dir_icon, FOLDER_SPACES, folder=True) index = index + 1 for item in self.file_list_items: self.table_row(item, index, self.file_icon, FILES_SPACES, folder=False) index = index + 1 if self.last_index == 0 and not self.show_save_action: self.ui_FileList.clearSelection() if self.ui_FileList.currentItem() is not None: self.ui_SelectedName.setText(self.ui_FileList.currentItem().text()) self.ui_FileList.resizeRowsToContents() QtWidgets.QApplication.restoreOverrideCursor() def table_row(self, item, index, icon, spaces, folder): info = list(item.values())[0] _mod_time, _size = human_readable_info(info) if folder: _size = u"Folder" self.ui_FileList.setCellWidget(index, 0, WidgetImage(fs.path.basename(list(item)[0]), icon, item)) time.sleep(0.001) _item = " " * spaces + fs.path.basename(list(item)[0]) _ti = TableWidgetItem(_item) if _size == u"Folder": _ti.setWhatsThis("Directory") else: _ti.setWhatsThis(u"File") self.ui_FileList.setItem(index, 0, TableWidgetItem(_ti)) self.ui_FileList.setItem(index, 1, TableWidgetItem(_size)) self.ui_FileList.setItem(index, 2, TableWidgetItem(_mod_time)) def get_info(self, _item, namespaces=[u'details', u'access', u'stat']): try: info = self.fs.getinfo(_item, namespaces=namespaces) time.sleep(0.001) except (fs.errors.ResourceNotFound, UnicodeEncodeError): info = None return info @QtCore.pyqtSlot(int, int) def onCellClicked(self, row, column): """ Action for ui_FileList WidgetImageText :param row: position :param column: position """ # Any cell click is always in column 0 column = 0 self.wparm = self.ui_FileList.cellWidget(row, column) if self.wparm is not None: if "text" in self.wparm.img: self.ui_SelectedName.setText(self.wparm.text) if self.show_dirs_only and "folder" in self.wparm.img: self.ui_SelectedName.setText(self.wparm.text) @QtCore.pyqtSlot(int, int) def onCellDoubleClicked(self, row, column): """ Action for ui_FileList WidgetImageText on doubleclick :param row: position :param column: position """ column = 0 self.wparm = self.ui_FileList.cellWidget(row, column) try: if self.wparm is not None: if "folder" in self.wparm.img: self.directory_history.append(list(self.wparm.value.keys())[0]) self.browse_folder(subdir=list(self.wparm.value.keys())[0]) if not self.show_save_action: self.ui_SelectedName.setText(None) self.selection_name() if "text" in self.wparm.img: self.ui_SelectedName.setText(self.wparm.text) self.action() except AttributeError: pass def selection_file_type(self): """ Action for line edit of file type """ self.selection_directory() self.ui_FileList.clearSelection() if not self.show_save_action: self.ui_SelectedName.setText(None) if self.show_save_action: text = self.ui_SelectedName.text() new_text = text.split('.')[0] self.ui_SelectedName.setText(new_text) def selection_name(self): """ Action for filename changes, line edit text input """ if not self.show_save_action and not self.show_dirs_only: self.ui_Action.setEnabled(False) self.filename = self.ui_SelectedName.text() if self.ui_DirList.currentText() == self.active_url: dirname = u"" else: dirname = self.ui_DirList.currentText() if self.wparm is not None: _dirname = fs.path.forcedir(u'.') if self.wparm.value == u'{}{}'.format(_dirname, self.wparm.text): dirname = fs.path.dirname(u'{}{}'.format(_dirname, self.wparm.text)) else: dirname = self.selected_dir if not self.show_dirs_only: _filename = fs.path.combine(dirname, self.filename) _file_names = [list(name)[0] for name in self.file_list_items] if dirname == fs.path.forcedir(u'.'): _file_names = [fs.path.combine(dirname, list(name)[0]) for name in self.file_list_items] if _filename in _file_names: self.ui_Action.setEnabled(True) index = _file_names.index(_filename) + len(self.dir_list_items) self.ui_FileList.selectRow(index) else: if not self.show_save_action: self.ui_Action.setEnabled(False) self.ui_FileList.clearSelection() def show_name(self): """ Action for showing clicked name as filename """ try: self.filename = self.ui_SelectedName.text() except AttributeError: self.filename = u"" if self.filename != u"": self.ui_SelectedName.setText(self.filename) info = self.get_info(self.filename, namespaces=None) if info is not None and info.is_file: self.ui_Action.setEnabled(True) def cancel(self): """ Action on cancel button """ self.filename = None self.close() def show_action(self): """ Changes the Open Button into a Save Button :param show_save_action: True for showing the Save dialog """ if self.show_save_action: self.ui_SelectedName.setEnabled(True) self.ui_Action.setText("Save") if self.default_filename is not None: self.ui_SelectedName.setText(self.default_filename) if self.show_dirs_only: self.ui_SelectedName.setEnabled(True) self.ui_Action.setText("Get Directory") def make_dir(self): """ Shows the make dir dialog und creates a new directory """ new_dir_name, ok = QtWidgets.QInputDialog.getText(self, u"New Folder", u"Enter a new folder name:", QtWidgets.QLineEdit.Normal, "") if ok: if self.ui_DirList.currentText() == self.active_url: dirname = u"" else: dirname = self.ui_DirList.currentText() new_dir = fs.path.combine(dirname, new_dir_name) if not self.fs.isdir(new_dir): try: self.fs.makedir(new_dir) except fs.errors.ResourceNotFound: logging.error(u"Can't create {}".format(new_dir)) self.last_dir_index = list(reversed(self.directory_history)).index(self.selected_dir) self.browse_folder(subdir=self.selected_dir) else: ok = QtWidgets.QMessageBox.warning(self, u"New Folder", u"Can't create this Folder: {}".format(new_dir)) def action(self): """ Action on Open / Save button """ self.filename = self.ui_SelectedName.text() if self.filename == u"" or self.filename is None: return dirname = fs.path.forcedir(u'.') if self.wparm is not None: dirname = self.selected_dir if dirname.startswith(self.active_url): filename = u"{}{}".format(fs.path.forcedir(self.active_url), self.filename) else: # We can't use fs.path.join and also not fs.path.abspath because of protocol url filename = u"{}{}{}".format(fs.path.forcedir(self.active_url), fs.path.forcedir(dirname), self.filename) filename = filename.replace(fs.path.forcedir(u'.'), u'') if self.show_save_action and not self.show_dirs_only: self.save_settings() self.filename = self.ui_SelectedName.text() if self.filename == u"": return info = self.get_info(fs.path.split(filename)[1], namespaces=None) if info is not None and info.is_dir: sel = QtWidgets.QMessageBox.warning( self, u"Warning", u"You can't create a file with this name: {0}".format(self.filename), QtWidgets.QMessageBox.No) elif info is not None and info.is_file: sel = QtWidgets.QMessageBox.question( self, u"Replace Filename", u"This will replace the filename: {0}. Continue?".format(self.filename), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if sel == QtWidgets.QMessageBox.Yes: self.filename = filename self.close() else: pass else: self.filename = filename self.close() else: self.filename = filename self.close() def save_settings(self): items = [] for index in range(self.ui_fs_serverlist.count()): items.append(self.ui_fs_serverlist.item(index).text()) self.settings.setValue('fs_urls', items) selected_file_pattern = self.ui_FileType.currentText() self.settings.setValue("selected_file_pattern", selected_file_pattern) self.settings.sync()
def _commit(self): if not self.fake_db: conf = QSettings() conf.setValue("memoirs", [Message.build_file(msg) for key, msg in self._db.items()]) conf.sync()
class TrayStarter(QSystemTrayIcon): """ Class implementing a starter for the system tray. """ def __init__(self): """ Constructor """ super(TrayStarter, self).__init__( UI.PixmapCache.getIcon( Preferences.getTrayStarter("TrayStarterIcon"))) self.maxMenuFilePathLen = 75 self.rsettings = QSettings( QSettings.IniFormat, QSettings.UserScope, Globals.settingsNameOrganization, Globals.settingsNameRecent) self.recentProjects = [] self.__loadRecentProjects() self.recentMultiProjects = [] self.__loadRecentMultiProjects() self.recentFiles = [] self.__loadRecentFiles() self.activated.connect(self.__activated) self.__menu = QMenu(self.tr("Eric6 tray starter")) self.recentProjectsMenu = QMenu( self.tr('Recent Projects'), self.__menu) self.recentProjectsMenu.aboutToShow.connect( self.__showRecentProjectsMenu) self.recentProjectsMenu.triggered.connect(self.__openRecent) self.recentMultiProjectsMenu = \ QMenu(self.tr('Recent Multiprojects'), self.__menu) self.recentMultiProjectsMenu.aboutToShow.connect( self.__showRecentMultiProjectsMenu) self.recentMultiProjectsMenu.triggered.connect(self.__openRecent) self.recentFilesMenu = QMenu(self.tr('Recent Files'), self.__menu) self.recentFilesMenu.aboutToShow.connect(self.__showRecentFilesMenu) self.recentFilesMenu.triggered.connect(self.__openRecent) act = self.__menu.addAction( self.tr("Eric6 tray starter"), self.__about) font = act.font() font.setBold(True) act.setFont(font) self.__menu.addSeparator() self.__menu.addAction( self.tr("QRegExp editor"), self.__startQRegExp) self.__menu.addAction( self.tr("Python re editor"), self.__startPyRe) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("uiPreviewer.png"), self.tr("UI Previewer"), self.__startUIPreviewer) self.__menu.addAction( UI.PixmapCache.getIcon("trPreviewer.png"), self.tr("Translations Previewer"), self.__startTRPreviewer) self.__menu.addAction( UI.PixmapCache.getIcon("unittest.png"), self.tr("Unittest"), self.__startUnittest) self.__menu.addAction( UI.PixmapCache.getIcon("ericWeb.png"), self.tr("eric6 Web Browser"), self.__startHelpViewer) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("diffFiles.png"), self.tr("Compare Files"), self.__startDiff) self.__menu.addAction( UI.PixmapCache.getIcon("compareFiles.png"), self.tr("Compare Files side by side"), self.__startCompare) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("sqlBrowser.png"), self.tr("SQL Browser"), self.__startSqlBrowser) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("ericSnap.png"), self.tr("Snapshot"), self.__startSnapshot) self.__menu.addAction( UI.PixmapCache.getIcon("iconEditor.png"), self.tr("Icon Editor"), self.__startIconEditor) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("pluginInstall.png"), self.tr("Install Plugin"), self.__startPluginInstall) self.__menu.addAction( UI.PixmapCache.getIcon("pluginUninstall.png"), self.tr("Uninstall Plugin"), self.__startPluginUninstall) self.__menu.addAction( UI.PixmapCache.getIcon("pluginRepository.png"), self.tr("Plugin Repository"), self.__startPluginRepository) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("configure.png"), self.tr('Preferences'), self.__startPreferences) self.__menu.addAction( UI.PixmapCache.getIcon("erict.png"), self.tr("eric6 IDE"), self.__startEric) self.__menu.addAction( UI.PixmapCache.getIcon("editor.png"), self.tr("eric6 Mini Editor"), self.__startMiniEditor) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("configure.png"), self.tr('Configure Tray Starter'), self.__showPreferences) self.__menu.addSeparator() # recent files self.menuRecentFilesAct = self.__menu.addMenu(self.recentFilesMenu) # recent multi projects self.menuRecentMultiProjectsAct = self.__menu.addMenu( self.recentMultiProjectsMenu) # recent projects self.menuRecentProjectsAct = self.__menu.addMenu( self.recentProjectsMenu) self.__menu.addSeparator() self.__menu.addAction( UI.PixmapCache.getIcon("exit.png"), self.tr('Quit'), qApp.quit) def __loadRecentProjects(self): """ Private method to load the recently opened project filenames. """ rp = self.rsettings.value(Globals.recentNameProject) if rp is not None: for f in rp: if QFileInfo(f).exists(): self.recentProjects.append(f) def __loadRecentMultiProjects(self): """ Private method to load the recently opened multi project filenames. """ rmp = self.rsettings.value(Globals.recentNameMultiProject) if rmp is not None: for f in rmp: if QFileInfo(f).exists(): self.recentMultiProjects.append(f) def __loadRecentFiles(self): """ Private method to load the recently opened filenames. """ rf = self.rsettings.value(Globals.recentNameFiles) if rf is not None: for f in rf: if QFileInfo(f).exists(): self.recentFiles.append(f) def __activated(self, reason): """ Private slot to handle the activated signal. @param reason reason code of the signal (QSystemTrayIcon.ActivationReason) """ if reason == QSystemTrayIcon.Context or \ reason == QSystemTrayIcon.MiddleClick: self.__showContextMenu() elif reason == QSystemTrayIcon.DoubleClick: self.__startEric() def __showContextMenu(self): """ Private slot to show the context menu. """ self.menuRecentProjectsAct.setEnabled(len(self.recentProjects) > 0) self.menuRecentMultiProjectsAct.setEnabled( len(self.recentMultiProjects) > 0) self.menuRecentFilesAct.setEnabled(len(self.recentFiles) > 0) pos = QCursor.pos() x = pos.x() - self.__menu.sizeHint().width() pos.setX(x > 0 and x or 0) y = pos.y() - self.__menu.sizeHint().height() pos.setY(y > 0 and y or 0) self.__menu.popup(pos) def __startProc(self, applName, *applArgs): """ Private method to start an eric6 application. @param applName name of the eric6 application script (string) @param *applArgs variable list of application arguments """ proc = QProcess() applPath = os.path.join(getConfig("ericDir"), applName) args = [] args.append(applPath) for arg in applArgs: args.append(arg) if not os.path.isfile(applPath) or \ not proc.startDetached(sys.executable, args): E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( '<p>Could not start the process.<br>' 'Ensure that it is available as <b>{0}</b>.</p>' ).format(applPath), self.tr('OK')) def __startMiniEditor(self): """ Private slot to start the eric6 Mini Editor. """ self.__startProc("eric6_editor.py", "--config={0}".format( Utilities.getConfigDir())) def __startEric(self): """ Private slot to start the eric6 IDE. """ self.__startProc("eric6.py", "--config={0}".format( Utilities.getConfigDir())) def __startPreferences(self): """ Private slot to start the eric6 configuration dialog. """ self.__startProc("eric6_configure.py", "--config={0}".format( Utilities.getConfigDir())) def __startPluginInstall(self): """ Private slot to start the eric6 plugin installation dialog. """ self.__startProc("eric6_plugininstall.py", "--config={0}".format( Utilities.getConfigDir())) def __startPluginUninstall(self): """ Private slot to start the eric6 plugin uninstallation dialog. """ self.__startProc("eric6_pluginuninstall.py", "--config={0}".format( Utilities.getConfigDir())) def __startPluginRepository(self): """ Private slot to start the eric6 plugin repository dialog. """ self.__startProc("eric6_pluginrepository.py", "--config={0}".format( Utilities.getConfigDir())) def __startHelpViewer(self): """ Private slot to start the eric6 web browser. """ self.__startProc("eric6_webbrowser.py", "--config={0}".format( Utilities.getConfigDir())) def __startUIPreviewer(self): """ Private slot to start the eric6 UI previewer. """ self.__startProc("eric6_uipreviewer.py", "--config={0}".format( Utilities.getConfigDir())) def __startTRPreviewer(self): """ Private slot to start the eric6 translations previewer. """ self.__startProc("eric6_trpreviewer.py", "--config={0}".format( Utilities.getConfigDir())) def __startUnittest(self): """ Private slot to start the eric6 unittest dialog. """ self.__startProc("eric6_unittest.py", "--config={0}".format( Utilities.getConfigDir())) def __startDiff(self): """ Private slot to start the eric6 diff dialog. """ self.__startProc("eric6_diff.py", "--config={0}".format( Utilities.getConfigDir())) def __startCompare(self): """ Private slot to start the eric6 compare dialog. """ self.__startProc("eric6_compare.py", "--config={0}".format( Utilities.getConfigDir())) def __startSqlBrowser(self): """ Private slot to start the eric6 sql browser dialog. """ self.__startProc("eric6_sqlbrowser.py", "--config={0}".format( Utilities.getConfigDir())) def __startIconEditor(self): """ Private slot to start the eric6 icon editor dialog. """ self.__startProc("eric6_iconeditor.py", "--config={0}".format( Utilities.getConfigDir())) def __startSnapshot(self): """ Private slot to start the eric6 snapshot dialog. """ self.__startProc("eric6_snap.py", "--config={0}".format( Utilities.getConfigDir())) def __startQRegExp(self): """ Private slot to start the eric6 QRegExp editor dialog. """ self.__startProc("eric6_qregexp.py", "--config={0}".format( Utilities.getConfigDir())) def __startPyRe(self): """ Private slot to start the eric6 Python re editor dialog. """ self.__startProc("eric6_re.py", "--config={0}".format( Utilities.getConfigDir())) def __showRecentProjectsMenu(self): """ Private method to set up the recent projects menu. """ self.recentProjects = [] self.rsettings.sync() self.__loadRecentProjects() self.recentProjectsMenu.clear() idx = 1 for rp in self.recentProjects: if idx < 10: formatStr = '&{0:d}. {1}' else: formatStr = '{0:d}. {1}' act = self.recentProjectsMenu.addAction( formatStr.format( idx, Utilities.compactPath(rp, self.maxMenuFilePathLen))) act.setData(rp) idx += 1 def __showRecentMultiProjectsMenu(self): """ Private method to set up the recent multi projects menu. """ self.recentMultiProjects = [] self.rsettings.sync() self.__loadRecentMultiProjects() self.recentMultiProjectsMenu.clear() idx = 1 for rmp in self.recentMultiProjects: if idx < 10: formatStr = '&{0:d}. {1}' else: formatStr = '{0:d}. {1}' act = self.recentMultiProjectsMenu.addAction( formatStr.format( idx, Utilities.compactPath(rmp, self.maxMenuFilePathLen))) act.setData(rmp) idx += 1 def __showRecentFilesMenu(self): """ Private method to set up the recent files menu. """ self.recentFiles = [] self.rsettings.sync() self.__loadRecentFiles() self.recentFilesMenu.clear() idx = 1 for rf in self.recentFiles: if idx < 10: formatStr = '&{0:d}. {1}' else: formatStr = '{0:d}. {1}' act = self.recentFilesMenu.addAction( formatStr.format( idx, Utilities.compactPath(rf, self.maxMenuFilePathLen))) act.setData(rf) idx += 1 def __openRecent(self, act): """ Private method to open a project or file from the list of recently opened projects or files. @param act reference to the action that triggered (QAction) """ filename = act.data() if filename: self.__startProc("eric6.py", filename) def __showPreferences(self): """ Private slot to set the preferences. """ from Preferences.ConfigurationDialog import ConfigurationDialog dlg = ConfigurationDialog( None, 'Configuration', True, fromEric=True, displayMode=ConfigurationDialog.TrayStarterMode) dlg.preferencesChanged.connect(self.preferencesChanged) dlg.show() dlg.showConfigurationPageByName("trayStarterPage") dlg.exec_() QApplication.processEvents() if dlg.result() == QDialog.Accepted: dlg.setPreferences() Preferences.syncPreferences() self.preferencesChanged() def preferencesChanged(self): """ Public slot to handle a change of preferences. """ self.setIcon( UI.PixmapCache.getIcon( Preferences.getTrayStarter("TrayStarterIcon"))) def __about(self): """ Private slot to handle the About dialog. """ from Plugins.AboutPlugin.AboutDialog import AboutDialog dlg = AboutDialog() dlg.exec_()
class SubSettings: """A wrapper to QSettings. Provides an interface to all available Subconvert options.""" def __init__(self): # The following settings will cause saving config files to e.g. # ~/.config/subconvert/subconvert.ini organization = "subconvert" mainConfFile = "subconvert" programStateFile = "state" self._settings = QSettings(QSettings.IniFormat, QSettings.UserScope, organization, mainConfFile) self._programState = QSettings(QSettings.IniFormat, QSettings.UserScope, organization, programStateFile) def sync(self): self._settings.sync() self._programState.sync() def getUseDefaultDirectory(self): return self._settings.value("gui/use_default_dirs", True) def setUseDefaultDirectory(self, val): self._settings.setValue("gui/use_default_dirs", val) # # Last directory from which a file has been opened # def getLatestDirectory(self): if self.getUseDefaultDirectory(): ret = self._programState.value("gui/latest_dir", QDir.homePath()) if ret: return ret return QDir.homePath() def setLatestDirectory(self, val): self._programState.setValue("gui/latest_dir", val) # # Subtitle "property files" paths, number etc. # def getPropertyFilesPath(self): defaultDirName = "pfiles" defaultPath = os.path.join(os.path.dirname(self._programState.fileName()), defaultDirName) return self._programState.value("pfiles/path", defaultPath) def setPropertyFilesPath(self, val): self._programState.setValue("pfiles/path", val) def getMaxRememberedPropertyFiles(self): defaultMaxValue = 5 return self._programState.value("pfiles/max", defaultMaxValue) def setMaxRememberedPropertyFiles(self, val): self._programState.setValue("pfiles/max", val) def getLatestPropertyFiles(self): return self._programState.value("pfiles/latest", []) def addPropertyFile(self, val): maxPropertyFiles = self.getMaxRememberedPropertyFiles() - 1 propertyFiles = self.getLatestPropertyFiles() if val in propertyFiles: propertyFiles.remove(val) else: propertyFiles = propertyFiles[:maxPropertyFiles] propertyFiles.insert(0, val) self._programState.setValue("pfiles/latest", propertyFiles) def removePropertyFile(self, val): propertyFiles = self.getLatestPropertyFiles() try: index = propertyFiles.index(val) del propertyFiles[index] self._programState.setValue("pfiles/latest", propertyFiles) except ValueError: pass # # Generic functions for windows/widgets. Please note that passed QWidgets must have previously # set objects names via QWidget::setObjectName(str) method. The convention is to use underscores # as words separators (e.g. main_window, my_super_widget, etc.). # def setGeometry(self, widget, val): SubAssert(widget.objectName() != "", "Widget's name isn't set!") self._programState.setValue("gui/%s/geometry" % widget.objectName(), val) def getGeometry(self, widget, default=QByteArray()): SubAssert(widget.objectName() != "", "Widget's name isn't set!") return self._programState.value("gui/%s/geometry" % widget.objectName(), default) def setState(self, widget, val): SubAssert(widget.objectName() != "", "Widget's name isn't set!") self._programState.setValue("gui/%s/state" % widget.objectName(), val) def getState(self, widget, default=QByteArray()): SubAssert(widget.objectName() != "", "Widget's name isn't set!") return self._programState.value("gui/%s/state" % widget.objectName(), default) def setHidden(self, widget, val): SubAssert(widget.objectName() != "", "Widget's name isn't set!") self._programState.setValue("gui/%s/hidden" % widget.objectName(), val) def getHidden(self, widget, default=True): SubAssert(widget.objectName() != "", "Widget's name isn't set!") return str2Bool(self._programState.value("gui/%s/hidden" % widget.objectName(), default))