diff --git a/app2.py b/app2.py index 3299b8653d48f48631cb2c7d7d4bf1d3cde2a156..902f4197cc8d59ceaca0321bd648f6a4a6427314 100644 --- a/app2.py +++ b/app2.py @@ -39,10 +39,6 @@ def exception_handler(i: 'Tuple[ClassVar[BaseException], BaseException]'): reinstall_requirements_with_gui() - # dlg = ExceptionHandlerDialog(i[1]) - # - # dlg.exec_() - def on_exception(e): from check_dependency import ExceptionHandlerDialog @@ -66,7 +62,7 @@ os.environ['FORCE_QT_API'] = "1" from typing import List, Callable, Tuple, ClassVar from PySide2.QtCore import Signal, QTimer, Qt, QCoreApplication from PySide2.QtGui import QCloseEvent, QTextCursor, QResizeEvent, QMoveEvent, QFont, QIcon, QPixmap -from PySide2.QtWidgets import QApplication, QMessageBox, QSplashScreen, QStatusBar +from PySide2.QtWidgets import QApplication, QMessageBox, QSplashScreen, QStatusBar, QDialog, QVBoxLayout, QProgressBar import pmgwidgets pmgwidgets.in_unit_test = lambda: False @@ -74,10 +70,9 @@ from pmgwidgets import PMGToolBar from features.extensions.extensions_manager.manager import extensions_manager from features.main_window import base -from features.io.settings import Settings from features.io.settings import load_theme from features.interpretermanager.interpretermanager import InterpreterManagerWidget -from features.util.update import perform_update, UpdateTipClient +from features.util.update import UpdateTipClient from features.feedback import FeedbackClient from features.ui.widgets.controlpanel import PMPageExt from features.ui.pmwidgets import BaseMainWindow @@ -141,9 +136,8 @@ class MainWindow(BaseMainWindow): def __init__(self, parent=None): super().__init__(parent) - + # settings = Settings() t00 = time.time() - settings = Settings() self.main_option_form = base.OptionForm() self.project_wizard: base.ProjectWizardForm = None self.settings_changed_signal.connect(self.on_settings_changed) @@ -155,7 +149,7 @@ class MainWindow(BaseMainWindow): self.resize(1920, 1080) # 设置主窗体标题 - self.setWindowTitle('PyMiner ' + utils.version) + self.setWindowTitle('PyMiner ' + utils.get_settings_item_from_file("config.ini", "INFO/VERSION", "default")) # 设置状态栏 self.statusBar = QStatusBar() @@ -192,7 +186,8 @@ class MainWindow(BaseMainWindow): dw.setMaximumWidth(400) # 设置主题 ,组件都加载后再设置主题,否则可能有些组件不生效 - load_theme(settings['theme']) + settings = utils.get_settings_from_file("config.ini") + load_theme(settings.value("MAIN/THEME")) self.show() self.load_layout() @@ -201,7 +196,7 @@ class MainWindow(BaseMainWindow): self.on_main_window_shown() self.start_pmlocalserver() # 只要在插件加载完成之后启动就行,目前放在最后 - self.update_tip_client = UpdateTipClient() # 启动程序,检查更新,弹窗提醒 + self.update_tip_client = UpdateTipClient(True) # 启动程序,检查更新,弹窗提醒 t01 = time.time() logging.debug('Time Elapsed for loading main window contents: %f' % (t01 - t00)) @@ -220,7 +215,6 @@ class MainWindow(BaseMainWindow): a0.accept() self.delete_temporary_dock_windows() self.save_layout() # TODO:PySide2上存储布局有问题。 - Settings.get_instance().save() self.close_signal.emit() self.extensions_manager.stop() for k in self.dock_widgets.keys(): @@ -283,7 +277,12 @@ class MainWindow(BaseMainWindow): self.window_geometry_changed_signal.emit() def on_settings_changed(self): - load_theme(Settings.get_instance()['theme']) + """ + 当设置项发生改变时,加载这些设置项。 + Returns: + + """ + load_theme(utils.get_settings_from_file("config.ini").value("MAIN/THEME")) def delayed_call(self, time_ms: int, callback: Callable) -> None: """ @@ -329,7 +328,7 @@ class MainWindow(BaseMainWindow): self.widgets_ready_signal.emit() t1 = time.time() logging.info('Layout ready time elapsed:%f' % (t1 - t0)) - self.set_dock_titlebar_visible(Settings.get_instance()['dock_titlebar_visible']) + self.set_dock_titlebar_visible(utils.get_settings_item_from_file("config.ini", "MAIN/DOCK_TITLEBAR_VISIBLE")) self.bind_events() self.events_ready_signal.emit() t2 = time.time() @@ -384,7 +383,25 @@ class MainWindow(BaseMainWindow): """ 打开'检查更新'页面 """ - perform_update() + dlg = QDialog() + dlg.setLayout(QVBoxLayout()) + proc = QProgressBar() + dlg.layout().addWidget(proc) + dlg.setWindowTitle("请稍等,正在进行更新检查") + dlg.setFixedWidth(500) + proc.setRange(0, 0) # 设置进度条忙 + + def close(b): + if b: + dlg.close() + else: + dlg.close() + QMessageBox.information(self, "更新提示", "已经是最新版,无需更新!") + + self.update_tip_client = UpdateTipClient(False) + self.update_tip_client.thread.exist_update.connect(close) + + dlg.exec_() def main_install_update(self): closed = self.close() @@ -495,8 +512,9 @@ def main(test_function: Callable[[MainWindow], None] = None): splash.finish(window) if test_function is None: - # 展示快速启动窗口 - window.first_form_display() + # 展示快速启动窗口。只有在设置为True的情况下才显示起始页。 + if utils.get_settings_item_from_file("config.ini", "MAIN/SHOW_START_PAGE"): + window.first_form_display() else: test_function(window) logging.info('boot time elapsed:%f s' % (t1 - t0)) diff --git a/configuration/config.ini b/configuration/config.ini new file mode 100644 index 0000000000000000000000000000000000000000..22e5135ee9c8366c695574694911bb0d2fa4906f --- /dev/null +++ b/configuration/config.ini @@ -0,0 +1,19 @@ +[MAIN] +THEME = Fusion +LANGUAGE = zh_CN +PATH_OUTPUT = +PATH_WORKDIR = +INTERPRETER = d:/pyminer/python.exe +SHOW_START_PAGE = True +CHECK_UPDATE = True +DOCK_TITLEBAR_VISIBLE = False + +[RUN] +EXTERNAL_INTERPRETERS = "[]" +CURRENT_INTERPRETER = + +[INFO] +VERSION = v2.1.4 Beta +WEBSITE = http://www.pyminer.com +AUTHOR = PyMiner Development Team +MAIL = team@py2cn.com diff --git a/features/extensions/extensionlib/extension_lib.py b/features/extensions/extensionlib/extension_lib.py index ef22b7f646433b36d4794e7ec3a62e22d6585569..90126d73bbf9236929e8d36101e8b8fcbb14d5ec 100644 --- a/features/extensions/extensionlib/extension_lib.py +++ b/features/extensions/extensionlib/extension_lib.py @@ -1,10 +1,12 @@ import inspect import os -from typing import TYPE_CHECKING, Callable, Dict, List, Tuple, Union, Optional, Any -from PySide2.QtCore import QRect, Signal, QTranslator, QLocale -from PySide2.QtWidgets import QWidget, QApplication, QDialog -from features.workspace.data_adapter import UniversalAdapter +from typing import TYPE_CHECKING, Callable, Dict, List, Tuple, Optional, Any + +from PySide2.QtCore import QRect, Signal +from PySide2.QtWidgets import QWidget, QDialog + import utils +from features.workspace.data_adapter import UniversalAdapter if TYPE_CHECKING: from pmgwidgets import PMGToolBar @@ -18,8 +20,6 @@ def wrapper(): from utils import get_main_window from features.ui import pmwidgets from features.extensions.extensionlib import baseext - from features.ui.common.pmlocale import pmlocale - from features.io.settings import Settings from features.io.exceptions import PMExceptions from pyminer_comm.base import DataDesc @@ -260,42 +260,32 @@ def wrapper(): return utils.get_root_dir() @staticmethod - def _(text): + def get_settings_item_from_file(file: str, item: str, mode: str = "user"): """ - + 从设置文件中获取设置项 Args: - text: + file: + item: + mode: Returns: """ - return pmlocale.translate(text) + return utils.get_settings_item_from_file(file, item, mode) @staticmethod - def get_settings() -> Dict[str, str]: - """ - - Returns: - + def write_settings_item_to_file(file: str, item: str, value: Any): """ - return Settings.get_instance() - - @staticmethod - def update_settings(settings_to_modify: Dict[str, str]) -> None: - """ - 修改设置 + 将配置项写入设置文件中 Args: - settings_to_modify: + file: + item: + value: Returns: """ - settings = Settings.get_instance() - for k, v in settings_to_modify.items(): - if k in settings.keys(): - Settings.get_instance()[k] = v - else: - raise ValueError('Key {0} not in Settings!' % k) + utils.write_settings_item_to_file(file, item, value) get_main_window().settings_changed_signal.emit() @staticmethod @@ -309,7 +299,7 @@ def wrapper(): Returns: """ - Settings.get_instance()['work_dir'] = work_dir + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", work_dir) get_main_window().settings_changed_signal.emit() @staticmethod @@ -321,7 +311,20 @@ def wrapper(): """ - return Settings.get_instance()['work_dir'] + dir = utils.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") + if (not isinstance(dir, str)) or (not os.path.exists(dir)): + dir = os.path.join(os.path.expanduser("~"), "Desktop") + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", dir) + return dir + + @staticmethod + def get_theme() -> str: + """ + 获取主题 + Returns: + + """ + return utils.get_settings_item_from_file("config.ini", "MAIN/THEME") @staticmethod def run_python_file(file_path: str, interpreter_path: str): diff --git a/features/extensions/extensionlib/extensionlib.md b/features/extensions/extensionlib/extensionlib.md index 093cc62aba8a6c4523b6b978ca26c830a0b5d1c3..5e5bf1e8a0d90ba36bb5fe22acb43a4cb74fabfa 100644 --- a/features/extensions/extensionlib/extensionlib.md +++ b/features/extensions/extensionlib/extensionlib.md @@ -210,7 +210,6 @@ def get_settings() -> Dict[str, str]: Returns: """ -return Settings.get_instance() ### 设置当前工作路径 @staticmethod diff --git a/features/feedback.py b/features/feedback.py index 1fe390dc36b6122cb49df3031fc1da494a2b100a..f4a8a03522fab1d6e77b9cdc7b2f43108f26e082 100644 --- a/features/feedback.py +++ b/features/feedback.py @@ -4,41 +4,45 @@ # @Email : 2195932461@qq.com # @File : feedback.py # @Software: PyCharm -from PySide2.QtWidgets import QDialog,QTextEdit,QLabel,QVBoxLayout,QApplication,QPushButton,QMessageBox +import requests +from PySide2.QtWidgets import QDialog, QTextEdit, QLabel, QVBoxLayout, QApplication, QPushButton, QMessageBox + from features.settings import Setting -import requests,json + class FeedbackClient(QDialog): def __init__(self): super().__init__() - self.vbox=QVBoxLayout() + self.vbox = QVBoxLayout() self.setLayout(self.vbox) - self.text_edit=QTextEdit() + self.text_edit = QTextEdit() self.setWindowTitle(self.tr('Feedback')) - self.label=QLabel(self.tr('You can give feedback through issue on suggestions or problems encountered in use! (<200 words)')) - self.confirm_button=QPushButton(self.tr('confirm')) + self.label = QLabel( + self.tr('You can give feedback through issue on suggestions or problems encountered in use! (<200 words)')) + self.confirm_button = QPushButton(self.tr('confirm')) self.confirm_button.setFixedWidth(75) self.confirm_button.clicked.connect(self.post) self.vbox.addWidget(self.label) self.vbox.addWidget(self.text_edit) self.vbox.addWidget(self.confirm_button) setting = Setting() - self.version=setting.get_system_version() + self.version = setting.get_system_version() self.exec_() def post(self): - text=self.text_edit.toPlainText() - version=self.version + text = self.text_edit.toPlainText() + version = self.version url = 'http://www.pyminer.com/api/v1/feedback/' data = {'core': version, 'feedback': text} r = requests.post(url, data) - if r.status_code==201: - QMessageBox.about(self,self.tr('result'),self.tr('Submitted successfully!')) + if r.status_code == 201: + QMessageBox.about(self, self.tr('result'), self.tr('Submitted successfully!')) else: QMessageBox.warning(self, self.tr('result'), r.text, QMessageBox.Yes) if __name__ == '__main__': import sys - app=QApplication(sys.argv) - FeedbackClient() \ No newline at end of file + + app = QApplication(sys.argv) + FeedbackClient() diff --git a/features/interpretermanager/interpretermanager.py b/features/interpretermanager/interpretermanager.py index 73d1d4f66376c3d6af00c7e462b37cd96aed73a2..3861306dbfb988a15f4a90d4b6b8eaedacc7819b 100644 --- a/features/interpretermanager/interpretermanager.py +++ b/features/interpretermanager/interpretermanager.py @@ -7,13 +7,14 @@ settings['interpreters'] = """ import os import sys -from typing import Dict +from typing import Dict, List -from PySide2.QtWidgets import QTableWidget, QPushButton, QHBoxLayout, QVBoxLayout, QApplication, QWidget, QDialog, \ +from PySide2.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QWidget, QDialog, \ QListWidget, QMessageBox, QSpacerItem, QSizePolicy -from features.io.settings import Settings +import utils from pmgwidgets import PMGPanelDialog +from utils import get_settings_item_from_file class Interpreter(): @@ -35,6 +36,38 @@ def get_interpreter_version(interpreter_path: str) -> str: return version +def get_all_external_interpreters() -> List[Dict]: + """ + 获取所有的外部解释器 + Returns: + + """ + return utils.get_settings_item_from_file("config.ini", "RUN/EXTERNAL_INTERPRETERS") + + +def modify_interpreter_config(mode, info: Dict = None, index: int = -1): + """ + + Args: + info: 一个存储解释器信息的字典 + mode: "add","delete","modify" + + Returns: + + """ + + external_interpreters = utils.get_settings_item_from_file("config.ini", "RUN/EXTERNAL_INTERPRETERS") + if mode == "add": + external_interpreters.append(info) + elif mode == "modify": + external_interpreters[index] = info + elif mode == "delete": + external_interpreters.pop(index) + else: + raise NotImplementedError(mode) + utils.write_settings_item_to_file("config.ini", "RUN/EXTERNAL_INTERPRETERS", external_interpreters) + + class InterpreterManagerWidget(QWidget): def __init__(self, parent=None): super(InterpreterManagerWidget, self).__init__(parent) @@ -83,13 +116,15 @@ class InterpreterManagerWidget(QWidget): if ret == QDialog.Accepted: res = dlg.get_value() d = {'name': res['name'], 'path': res['path'], 'version': get_interpreter_version(res['path'])} - Settings.get_instance()['external_interpreters'].append(d) + modify_interpreter_config("add", d) self.show_interpreters() def edit(self): current_index = self.interpreters_list_show.currentRow() list_index = current_index - 1 # 第一个是默认解释器,所以要减去1 - current_interpreter = Settings.get_instance()['external_interpreters'][list_index] + + external_interpreters = get_all_external_interpreters() + current_interpreter = external_interpreters[list_index] views = [ ('line_ctrl', 'name', self.tr('Name'), current_interpreter['name']), @@ -103,7 +138,7 @@ class InterpreterManagerWidget(QWidget): if ret == QDialog.Accepted: res = dlg.get_value() d = {'name': res['name'], 'path': res['path'], 'version': get_interpreter_version(res['path'])} - Settings.get_instance()['external_interpreters'][list_index] = d + modify_interpreter_config("modify", d, list_index) self.show_interpreters() def manage_packages(self): @@ -126,19 +161,18 @@ class InterpreterManagerWidget(QWidget): return True def remove(self): - Settings.get_instance()['external_interpreters'].pop( - self.interpreters_list_show.currentRow() - 1) # 第一个是默认解释器,所以要减去1 + modify_interpreter_config("delete", index=self.interpreters_list_show.currentRow() - 1) self.show_interpreters() def show_interpreters(self): self.interpreters_list_show.clear() self.interpreters_list_show.addItem(self.tr('BuiltIn (3.8.5)')) - for interpreter in Settings.get_instance()['external_interpreters']: + ext_interpreters = get_all_external_interpreters() + for interpreter in ext_interpreters: name = interpreter['name'] version = interpreter['version'] text = name + ' (%s)' % version self.interpreters_list_show.addItem(text) - pass def on_list_current_item_changed(self): """ @@ -158,11 +192,10 @@ class InterpreterManagerWidget(QWidget): return else: list_index = current_index - 1 - return Settings.get_instance()['external_interpreters'][list_index] + return get_all_external_interpreters()[list_index] if __name__ == '__main__': - s = Settings() from PySide2.QtWidgets import QApplication app = QApplication([]) diff --git a/features/io/encoding.py b/features/io/encoding.py index f36c01dcb7ffed8a860017ef0a22759956ac4a88..caac3288c5d609ed51f90e89b70860d0ddd6073b 100644 --- a/features/io/encoding.py +++ b/features/io/encoding.py @@ -1,16 +1,20 @@ import os + from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QHBoxLayout, QTextBrowser, QMessageBox + from pmgwidgets import PMGPanel +import utils from utils import file_encoding_convert -from features.io.settings import Settings class EncodingConversionWidget(QDialog): def __init__(self, parent=None): super(EncodingConversionWidget, self).__init__(parent=parent) views = [ - ('file_ctrl', 'input_file', '读取文件名', '', '', Settings.get_instance()['work_dir']), - ('file_ctrl', 'output_file', '输出为文件', '', '', Settings.get_instance()['work_dir'],'save'), + ('file_ctrl', 'input_file', '读取文件名', '', '', + utils.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR")), + ('file_ctrl', 'output_file', '输出为文件', '', '', + utils.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR"), 'save'), [ ('combo_ctrl', 'input_encoding', '读取编码方式', 'UTF8', ['UTF8', 'GBK', 'ASCII']), ('combo_ctrl', 'output_encoding', '输出编码方式', 'UTF8', ['UTF8', 'GBK', 'ASCII']) @@ -32,7 +36,6 @@ class EncodingConversionWidget(QDialog): self.button_preview.clicked.connect(self.preview) self.button_convert.clicked.connect(self.convert) - # self.button_preview.clicked.connect(self.preview) def preview(self): info = self.panel.get_value() @@ -55,7 +58,6 @@ class EncodingConversionWidget(QDialog): if __name__ == '__main__': - s = Settings() from PySide2.QtWidgets import QApplication app = QApplication([]) diff --git a/features/io/settings.py b/features/io/settings.py index 30b6c6d379dd292fe97bd8794c0ff947abe00a59..6055621639a97eceef2414e2309a2c8d8a81f06e 100644 --- a/features/io/settings.py +++ b/features/io/settings.py @@ -1,4 +1,5 @@ """ +TODO:DEPRECATED。这个文件将被废弃! settings.py负责设置文件的输入输出。 Settings继承字典类,另外写了存储函数。 还有其他设置的函数 @@ -40,6 +41,7 @@ import platform from typing import Dict from PySide2.QtWidgets import QApplication + import qdarkstyle import utils @@ -49,7 +51,7 @@ logger = logging.getLogger(__name__) def get_pyminer_data_path() -> str: path = os.path.join(os.path.expanduser('~'), '.pyminer') if not os.path.exists(path): - os.mkdir(path) + os.makedirs(path) return path @@ -96,73 +98,73 @@ def load_theme(style: str): # app.setStyle("Windows") -class Settings(dict): - """ - 单例! - """ - - @classmethod - def __new__(cls, *args): - if not hasattr(cls, 'instance'): - instance = super().__new__(cls) - cls.instance = instance - return cls.instance - - def __init__(self): - super(Settings, self).__init__() - self.check_pyminer_settings_dir() - self.update(self.load()) - - def check_pyminer_settings_dir(self): - self.data_path = get_pyminer_data_path() - path = os.path.join(self.data_path, 'pyminer_config') - self.settings_path = path - if not os.path.exists(path): - os.mkdir(path) - - @staticmethod - def get_instance() -> 'Settings': - return Settings.instance - - def load(self) -> Dict[str, str]: - """ - 加载设置项。 - default_settings是默认设置项 - :return: - """ - with open(os.path.join(utils.get_root_dir(), 'configuration', 'default_settings.json'), 'r') as f: - default_settings = json.load(f) - if platform.system().lower() == 'windows': - default_settings['work_dir'] = os.path.expanduser('~') - else: - default_settings['work_dir'] = os.environ['HOME'] - if not os.path.exists(default_settings['work_dir']): - os.mkdir(default_settings['work_dir']) - - try: - with open(os.path.join(self.settings_path, 'pyminer_settings.json'), 'r') as f: - settings = json.load(f) - except BaseException: - settings = {} - - pmsettings = default_settings - pmsettings.update(settings) - if not os.path.exists(pmsettings['work_dir']): - pmsettings['work_dir'] = os.path.expanduser('~') - return pmsettings - - def save(self): - """ - 保存 - :return: - """ - import json - try: - config_file = os.path.join(self.settings_path, 'pyminer_settings.json') - with open(config_file, 'w') as f: - json.dump(self, f, indent=4) - except FileNotFoundError as e: - logging.warning(e) +# class Settings(dict): +# """ +# 单例! +# """ +# +# @classmethod +# def __new__(cls, *args): +# if not hasattr(cls, 'instance'): +# instance = super().__new__(cls) +# cls.instance = instance +# return cls.instance +# +# def __init__(self): +# super(Settings, self).__init__() +# self.check_pyminer_settings_dir() +# self.update(self.load()) +# +# def check_pyminer_settings_dir(self): +# self.data_path = get_pyminer_data_path() +# path = os.path.join(self.data_path, 'pyminer_config') +# self.settings_path = path +# if not os.path.exists(path): +# os.mkdir(path) +# +# @staticmethod +# def get_instance() -> 'Settings': +# return Settings.instance +# +# def load(self) -> Dict[str, str]: +# """ +# 加载设置项。 +# default_settings是默认设置项 +# :return: +# """ +# with open(os.path.join(utils.get_root_dir(), 'configuration', 'default_settings.json'), 'r') as f: +# default_settings = json.load(f) +# if platform.system().lower() == 'windows': +# default_settings['work_dir'] = os.path.expanduser('~') +# else: +# default_settings['work_dir'] = os.environ['HOME'] +# if not os.path.exists(default_settings['work_dir']): +# os.mkdir(default_settings['work_dir']) +# +# try: +# with open(os.path.join(self.settings_path, 'pyminer_settings.json'), 'r') as f: +# settings = json.load(f) +# except BaseException: +# settings = {} +# +# pmsettings = default_settings +# pmsettings.update(settings) +# if not os.path.exists(pmsettings['work_dir']): +# pmsettings['work_dir'] = os.path.expanduser('~') +# return pmsettings +# +# def save(self): +# """ +# 保存 +# :return: +# """ +# import json +# try: +# config_file = os.path.join(self.settings_path, 'pyminer_settings.json') +# with open(config_file, 'w') as f: +# json.dump(self, f, indent=4) +# except FileNotFoundError as e: +# logging.warning(e) if __name__ == '__main__': diff --git a/features/main_window/base.py b/features/main_window/base.py index b3555ecabc3717f56f31c84b176bb9baaafea449..32b23dbf411cecdbc88508424c52f5ae90133811 100644 --- a/features/main_window/base.py +++ b/features/main_window/base.py @@ -1,36 +1,37 @@ +import base64 +import json +import logging import os import sys import time -import subprocess import webbrowser -import base64 -import json -from typing import Tuple, List +from multiprocessing import shared_memory +from typing import List import qdarkstyle from PySide2.QtCore import QPoint, QRectF from PySide2.QtGui import QMouseEvent, QPainter, QLinearGradient, QCursor from PySide2.QtGui import QCloseEvent from PySide2.QtCore import Signal, Qt, QUrl, QPropertyAnimation -from PySide2.QtWidgets import QApplication, QListWidgetItem, QWizard, QHeaderView, QMessageBox +from PySide2.QtGui import QCloseEvent +from PySide2.QtGui import QMouseEvent from PySide2.QtWebEngineWidgets import * +from PySide2.QtWidgets import QListWidgetItem, QWizard, QMessageBox from PySide2.QtWidgets import QWidget, QDesktopWidget, QFileDialog, QApplication, QDialog -from pmgwidgets import PMGPanel - -from features.ui.ui_option import Ui_Form as Option_Ui_Form -from features.ui.ui_appstore import Ui_Form as appStore_Ui_Form +import utils +from features.extensions.extensionlib.extension_lib import extension_lib from features.ui.ui_aboutme import Ui_Form as About_Ui_Form -from features.ui.ui_project_wizard import Ui_Wizard as Project_Ui_Form +from features.ui.ui_appstore import Ui_Form as appStore_Ui_Form from features.ui.ui_first_form import Ui_Form as first_Ui_Form from features.ui.ui_login import Ui_Form as login_Ui_Form from features.ui.ui_logined import Ui_Form as logined_Ui_Form -import utils +from features.ui.ui_option import Ui_Form as Option_Ui_Form +from features.ui.ui_project_wizard import Ui_Wizard as Project_Ui_Form +from pmgwidgets import PMGPanel from utils import get_main_window, http_client -from features.io.settings import Settings -from features.extensions.extensionlib.extension_lib import extension_lib -from multiprocessing import shared_memory +logger = logging.getLogger(__name__) class OptionForm(QDialog, Option_Ui_Form): @@ -59,21 +60,12 @@ class OptionForm(QDialog, Option_Ui_Form): self.pushButton_cancel.clicked.connect(self.close) self.pushButton_ok.clicked.connect(self.close) self.pushButton_help.clicked.connect(self.get_help) - self.get_check_update() - - def get_check_update(self): - """是否启动检查更新""" - check_update = Settings.get_instance().get('check_update') - if check_update is None: - self.checkBox.setChecked(True) # 默认检查更新 - else: - self.checkBox.setChecked(check_update) def setup_ui(self): self.comboBox_9.setEnabled(False) self.comboBox_8.setEnabled(False) - self.checkBox_2.setEnabled(False) - self.checkBox_minitray.setEnabled(False) + # self.checkbox_show_startpage.setEnabled(False) + # self.checkBox_minitray.setEnabled(False) def add_settings_panel(self, text: str, settings_content: List): settings_widget = PMGPanel(views=settings_content) @@ -116,15 +108,17 @@ class OptionForm(QDialog, Option_Ui_Form): """ from features.io.settings import load_theme load_theme(style) - - Settings.get_instance()['theme'] = self.comboBox_theme.currentText() - # print(Settings.get_instance()['theme']) + utils.write_settings_item_to_file("config.ini", "MAIN/THEME", self.comboBox_theme.currentText()) get_main_window().settings_changed_signal.emit() - # get_main_window().on_settings_changed() - # self.signal_settings_changed.emit() def slot_change_workspace(self): - directory = QFileDialog.getExistingDirectory(self, "选择工作路径位置", directory=Settings.get_instance()['work_dir']) + """ + 改变工作路径时的回调 + Returns: + + """ + work_dir = utils.get_settings_item_from_file("config.ini", "MAIN/THEME", self.comboBox_theme.currentText()) + directory = QFileDialog.getExistingDirectory(self, "选择工作路径位置", directory=work_dir) if not directory == '': self.lineEdit_worksapce.setText(directory) @@ -138,22 +132,32 @@ class OptionForm(QDialog, Option_Ui_Form): 从而每次重新显示的时候都可以刷新数据。 :return: """ - settings = Settings.get_instance() - if settings.get('theme') is not None: + settings = utils.get_settings_from_file("config.ini") + logger.debug("PATH/WORKDIR", settings.value('MAIN/PATH_WORKDIR')) + if settings.value('MAIN/THEME') is not None: for i in range(self.comboBox_theme.count()): - if self.comboBox_theme.itemText(i) == settings['theme']: + if self.comboBox_theme.itemText(i) == settings.value('MAIN/THEME'): self.comboBox_theme.setCurrentIndex(i) - self.lineEdit_worksapce.setText(settings['work_dir']) + self.lineEdit_worksapce.setText(settings.value("MAIN/PATH_WORKDIR")) + self.lineEdit_output.setText(settings.value("MAIN/PATH_OUTPUT")) + + check_update = utils.get_settings_item_from_file("config.ini", "MAIN/CHECK_UPDATE") + show_start_page = utils.get_settings_item_from_file("config.ini", "MAIN/SHOW_START_PAGE") + self.check_box_check_upd_on_startup.setChecked(check_update) + self.checkbox_show_startpage.setChecked(show_start_page) def refresh_settings(self): """ 窗口关闭时,调用此方法,刷新主界面设置项。 :return: """ - settings = Settings.get_instance() - settings['work_dir'] = self.lineEdit_worksapce.text() - settings['theme'] = self.comboBox_theme.currentText() - settings['check_update'] = self.checkBox.isChecked() # 设置是否检查更新 + utils.write_settings_item_to_file("config.ini", "MAIN/THEME", self.comboBox_theme.currentText()) + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", self.lineEdit_worksapce.text()) + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_OUTPUT", self.lineEdit_output.text()) + utils.write_settings_item_to_file("config.ini", "MAIN/CHECK_UPDATE", + self.check_box_check_upd_on_startup.isChecked()) + utils.write_settings_item_to_file("config.ini", "MAIN/SHOW_START_PAGE", + self.checkbox_show_startpage.isChecked()) get_main_window().on_settings_changed() self.signal_settings_changed.emit() @@ -216,10 +220,28 @@ class AppstoreForm(QWidget, appStore_Ui_Form): class AboutForm(QWidget, About_Ui_Form): + """ + 关于 弹出框 + + """ + def __init__(self): super(AboutForm, self).__init__() self.setupUi(self) self.center() + AUTHOR = utils.get_settings_item_from_file("config.ini", "INFO/AUTHOR", "default") + MAIL = utils.get_settings_item_from_file("config.ini", "INFO/MAIL", "default") + self.textedit_about.setMarkdown("""# PyMiner +PyMiner 是一款基于Python的开源、跨平台数据分析环境。它以方便Python初学者为己任,在Python的知识理论和工作实践之间搭建桥梁,竭诚为初学者服务。 +- PyMiner开箱即用,大大减少配置解释器环境的繁琐性。不仅提供了编程运行的功能,还能够以交互式的形式进行常见的数据分析操作,减少代码编写和文档查阅的时间。 +- PyMiner通过加载各种插件实现不同的需求,开发者可以通过编写插件,将PyMiner扩展的更强大、更趁手,甚至创建一番自己的商用程序。 +- PyMiner提供面向新手的快速入门教程,教程正由开发团队编写中。 +- 我们诚挚希望与Python培训或教育机构/个人合作,让我们的产品帮助到更多学习Python的人。 + +作者:{AUTHOR} + +邮箱:{MAIL} +""".format(AUTHOR=AUTHOR, MAIL=MAIL)) self.main_about_display() @@ -246,6 +268,7 @@ class AboutForm(QWidget, About_Ui_Form): system_info = '系统信息: ' + platform.platform() + ' ' + platform.architecture()[0] cpu_info = 'CPU信息: ' + platform.processor() self.feedback.setPlainText(python_info + '\n' + system_info + '\n' + cpu_info) + self.label_version_show.setText(utils.get_settings_item_from_file("config.ini", "INFO/VERSION", "default")) class ProjectWizardForm(QWizard, Project_Ui_Form): @@ -603,6 +626,7 @@ class LoginForm(QDialog, login_Ui_Form): """ 登录窗口 """ + def __init__(self, parent=None): super(LoginForm, self).__init__(parent) self.setupUi(self) @@ -694,7 +718,7 @@ class LoginedForm(QDialog, logined_Ui_Form): shared_memo = shared_memory.SharedMemory(name="sharedMemory") # 通过name找到共享内存token buff = shared_memo.buf for i in range(0, len(buff)): - buff[i:i+1] = "\x00".encode() + buff[i:i + 1] = "\x00".encode() time.sleep(0.5) self.close() diff --git a/features/ui/pmwidgets/pmmainwindow.py b/features/ui/pmwidgets/pmmainwindow.py index 0c2060b101f690193c0ad2c502ecfe5e11d09273..f74385011a4f01ee39ee5adf2e8e15e1e2fe2b15 100644 --- a/features/ui/pmwidgets/pmmainwindow.py +++ b/features/ui/pmwidgets/pmmainwindow.py @@ -20,15 +20,15 @@ import chardet from PySide2.QtCore import Qt from PySide2.QtWidgets import QMainWindow, QToolBar, QPushButton, QWidget, QMenu, QDialog -from pmgwidgets import TopToolBarRight -from features.io.settings import Settings -from features.ui.common.pmlocale import pmlocale import utils +from features.ui.common.pmlocale import pmlocale +from pmgwidgets import TopToolBarRight if TYPE_CHECKING: from app2 import PMToolBarHome from features.ui.pmwidgets.dockwidget import PMDockWidget from pmgwidgets import ActionWithMessage +logger = logging.getLogger(__name__) class BaseMainWindow(QMainWindow): @@ -63,7 +63,7 @@ class BaseMainWindow(QMainWindow): else: for k, w in self.dock_widgets.items(): w.setTitleBarWidget(QWidget()) - Settings.get_instance()['dock_titlebar_visible'] = show + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", show) @staticmethod def get_stylesheet(style_sheet_name: str = 'standard'): @@ -82,9 +82,9 @@ class BaseMainWindow(QMainWindow): enc = chardet.detect(b) if enc.get('encoding') is not None: s = b.decode(enc['encoding']) - style_sheet = s.replace( - 'MAIN_THEME', Settings.get_instance()['main_theme']).replace( - 'MARGIN_THEME', Settings.get_instance()['margin_theme']) + style_sheet = s + else: + logger.fatal("加载样式表%s失败!" % style_sheet_name) return style_sheet def init_toolbar_tab(self): @@ -175,7 +175,13 @@ class BaseMainWindow(QMainWindow): self.refresh_toolbar_hide_button_status() def save_layout(self): - layout_path = os.path.join(Settings.get_instance().settings_path, 'layout.ini') + """ + 当关闭程序时保存布局 + Returns: + + """ + cfg_dir = utils.get_user_config_dir() + layout_path = os.path.join(cfg_dir, 'layout.ini') try: with open(layout_path, 'wb') as f: s = b'' # self.saveState() @@ -184,7 +190,7 @@ class BaseMainWindow(QMainWindow): logging.warning("file not found:" + layout_path) def load_layout(self): - p = os.path.join(Settings.get_instance().settings_path, 'layout.ini') + # p = os.path.join(Settings.get_instance().settings_path, 'layout.ini') # if os.path.exists(p): # with open(p, 'rb') as f: @@ -310,7 +316,8 @@ class BaseMainWindow(QMainWindow): parent=home_toolbar, message='lock_layout') a.setCheckable(True) - a.setChecked(not Settings.get_instance()['dock_titlebar_visible']) + dock_title_visible = utils.get_settings_item_from_file("config.ini", "MAIN/DOCK_TITLEBAR_VISIBLE") + a.setChecked(not dock_title_visible) menu.addAction(a) home_toolbar.get_control_widget('view_config').setMenu(menu) self._view_config_menu = menu diff --git a/features/ui/ui_aboutme.py b/features/ui/ui_aboutme.py index df648fe94b20549aee51d91c55513e7fc45f3336..878455db4b2d2ee3b865a69d79e17fb0b31031b1 100644 --- a/features/ui/ui_aboutme.py +++ b/features/ui/ui_aboutme.py @@ -47,6 +47,7 @@ class Ui_Form(object): font1.setFamily(u"Microsoft YaHei UI") font1.setPointSize(24) font1.setBold(True) + font1.setWeight(75) self.label.setFont(font1) self.label.setScaledContents(True) self.label.setAlignment(Qt.AlignCenter) @@ -62,12 +63,12 @@ class Ui_Form(object): self.horizontalLayout.addWidget(self.label_2) - self.label_6 = QLabel(Form) - self.label_6.setObjectName(u"label_6") - self.label_6.setFont(font2) - self.label_6.setAlignment(Qt.AlignBottom|Qt.AlignHCenter) + self.label_version_show = QLabel(Form) + self.label_version_show.setObjectName(u"label_version_show") + self.label_version_show.setFont(font2) + self.label_version_show.setAlignment(Qt.AlignBottom|Qt.AlignHCenter) - self.horizontalLayout.addWidget(self.label_6) + self.horizontalLayout.addWidget(self.label_version_show) self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) @@ -88,12 +89,12 @@ class Ui_Form(object): self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) - self.plainTextEdit = QPlainTextEdit(self.tab) - self.plainTextEdit.setObjectName(u"plainTextEdit") - self.plainTextEdit.setFont(font3) - self.plainTextEdit.setFrameShape(QFrame.NoFrame) + self.textedit_about = QTextEdit(self.tab) + self.textedit_about.setObjectName(u"textedit_about") + self.textedit_about.setFont(font3) + self.textedit_about.setFrameShape(QFrame.NoFrame) - self.verticalLayout_2.addWidget(self.plainTextEdit) + self.verticalLayout_2.addWidget(self.textedit_about) self.tabWidget.addTab(self.tab, "") self.tab_2 = QWidget() @@ -163,11 +164,7 @@ class Ui_Form(object): self.label_3.setText("") self.label.setText(QCoreApplication.translate("Form", u"PyMiner", None)) self.label_2.setText(QCoreApplication.translate("Form", u"Version:", None)) - self.label_6.setText(QCoreApplication.translate("Form", u"v2.0 Beta", None)) - self.plainTextEdit.setPlainText(QCoreApplication.translate("Form", u"PyMiner is a mathematical tool based on Python. It provides solutions to various of problems by loading different plugins. \n" -"\n" -"Author\uff1aPyMiner Development Team\n" -"E-Mail\uff1ateam@py2cn.com", None)) + self.label_version_show.setText(QCoreApplication.translate("Form", u"v2.0 Beta", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("Form", u"About", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("Form", u"System", None)) self.plainTextEdit_2.setPlainText(QCoreApplication.translate("Form", u"\u4faf\u5c55\u610f\n" diff --git a/features/ui/ui_aboutme.ui b/features/ui/ui_aboutme.ui index d5ff050725e927202a1525656abe080e4e642896..747bd4a4577215361299eb1614f077c6b0daef82 100644 --- a/features/ui/ui_aboutme.ui +++ b/features/ui/ui_aboutme.ui @@ -64,7 +64,7 @@ - :/logo/icons/logo.png + :/logo/icons/logo.png true @@ -77,6 +77,7 @@ Microsoft YaHei UI 24 + 75 true @@ -107,7 +108,7 @@ - + 黑体 @@ -168,7 +169,7 @@ 0 - + 黑体 @@ -178,12 +179,6 @@ QFrame::NoFrame - - PyMiner is a mathematical tool based on Python. It provides solutions to various of problems by loading different plugins. - -Author:PyMiner Development Team -E-Mail:team@py2cn.com - @@ -282,7 +277,7 @@ houxinluo - :/images/images/weixin.png + :/images/images/weixin.png true @@ -295,7 +290,7 @@ houxinluo - :/images/images/zhifubao.png + :/images/images/zhifubao.png true diff --git a/features/ui/ui_option.py b/features/ui/ui_option.py index 38cf32e715464be3eb4c9ffb9499a9f86c85e63f..7340b2170d05daae177441a84b475dbf2e3b1520 100644 --- a/features/ui/ui_option.py +++ b/features/ui/ui_option.py @@ -46,50 +46,24 @@ class Ui_Form(object): self.verticalLayout_8.setObjectName(u"verticalLayout_8") self.formLayout_2 = QFormLayout() self.formLayout_2.setObjectName(u"formLayout_2") - self.label = QLabel(self.tabBase) - self.label.setObjectName(u"label") - - self.formLayout_2.setWidget(1, QFormLayout.LabelRole, self.label) - - self.label_15 = QLabel(self.tabBase) - self.label_15.setObjectName(u"label_15") - - self.formLayout_2.setWidget(2, QFormLayout.LabelRole, self.label_15) - - self.checkBox = QCheckBox(self.tabBase) - self.checkBox.setObjectName(u"checkBox") - self.checkBox.setChecked(True) - - self.formLayout_2.setWidget(5, QFormLayout.LabelRole, self.checkBox) - - self.checkBox_2 = QCheckBox(self.tabBase) - self.checkBox_2.setObjectName(u"checkBox_2") - - self.formLayout_2.setWidget(7, QFormLayout.LabelRole, self.checkBox_2) - - self.label_16 = QLabel(self.tabBase) - self.label_16.setObjectName(u"label_16") - - self.formLayout_2.setWidget(3, QFormLayout.LabelRole, self.label_16) - - self.comboBox_9 = QComboBox(self.tabBase) - self.comboBox_9.addItem("") - self.comboBox_9.addItem("") - self.comboBox_9.setObjectName(u"comboBox_9") + self.label_theme = QLabel(self.tabBase) + self.label_theme.setObjectName(u"label_theme") - self.formLayout_2.setWidget(3, QFormLayout.FieldRole, self.comboBox_9) + self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.label_theme) - self.label_11 = QLabel(self.tabBase) - self.label_11.setObjectName(u"label_11") + self.comboBox_theme = QComboBox(self.tabBase) + self.comboBox_theme.addItem("") + self.comboBox_theme.addItem("") + self.comboBox_theme.addItem("") + self.comboBox_theme.addItem("") + self.comboBox_theme.setObjectName(u"comboBox_theme") - self.formLayout_2.setWidget(4, QFormLayout.LabelRole, self.label_11) + self.formLayout_2.setWidget(0, QFormLayout.FieldRole, self.comboBox_theme) - self.comboBox_8 = QComboBox(self.tabBase) - self.comboBox_8.addItem("") - self.comboBox_8.addItem("") - self.comboBox_8.setObjectName(u"comboBox_8") + self.label = QLabel(self.tabBase) + self.label.setObjectName(u"label") - self.formLayout_2.setWidget(4, QFormLayout.FieldRole, self.comboBox_8) + self.formLayout_2.setWidget(1, QFormLayout.LabelRole, self.label) self.horizontalLayout_14 = QHBoxLayout() self.horizontalLayout_14.setObjectName(u"horizontalLayout_14") @@ -106,6 +80,11 @@ class Ui_Form(object): self.formLayout_2.setLayout(1, QFormLayout.FieldRole, self.horizontalLayout_14) + self.label_15 = QLabel(self.tabBase) + self.label_15.setObjectName(u"label_15") + + self.formLayout_2.setWidget(2, QFormLayout.LabelRole, self.label_15) + self.horizontalLayout_15 = QHBoxLayout() self.horizontalLayout_15.setObjectName(u"horizontalLayout_15") self.lineEdit_output = QLineEdit(self.tabBase) @@ -121,25 +100,41 @@ class Ui_Form(object): self.formLayout_2.setLayout(2, QFormLayout.FieldRole, self.horizontalLayout_15) - self.label_theme = QLabel(self.tabBase) - self.label_theme.setObjectName(u"label_theme") + self.label_16 = QLabel(self.tabBase) + self.label_16.setObjectName(u"label_16") - self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.label_theme) + self.formLayout_2.setWidget(3, QFormLayout.LabelRole, self.label_16) - self.comboBox_theme = QComboBox(self.tabBase) - self.comboBox_theme.addItem("") - self.comboBox_theme.addItem("") - self.comboBox_theme.addItem("") - self.comboBox_theme.addItem("") - self.comboBox_theme.setObjectName(u"comboBox_theme") + self.comboBox_9 = QComboBox(self.tabBase) + self.comboBox_9.addItem("") + self.comboBox_9.addItem("") + self.comboBox_9.setObjectName(u"comboBox_9") - self.formLayout_2.setWidget(0, QFormLayout.FieldRole, self.comboBox_theme) + self.formLayout_2.setWidget(3, QFormLayout.FieldRole, self.comboBox_9) - self.checkBox_minitray = QCheckBox(self.tabBase) - self.checkBox_minitray.setObjectName(u"checkBox_minitray") - self.checkBox_minitray.setChecked(True) + self.label_11 = QLabel(self.tabBase) + self.label_11.setObjectName(u"label_11") - self.formLayout_2.setWidget(6, QFormLayout.LabelRole, self.checkBox_minitray) + self.formLayout_2.setWidget(4, QFormLayout.LabelRole, self.label_11) + + self.comboBox_8 = QComboBox(self.tabBase) + self.comboBox_8.addItem("") + self.comboBox_8.addItem("") + self.comboBox_8.setObjectName(u"comboBox_8") + + self.formLayout_2.setWidget(4, QFormLayout.FieldRole, self.comboBox_8) + + self.check_box_check_upd_on_startup = QCheckBox(self.tabBase) + self.check_box_check_upd_on_startup.setObjectName(u"check_box_check_upd_on_startup") + self.check_box_check_upd_on_startup.setChecked(True) + + self.formLayout_2.setWidget(5, QFormLayout.LabelRole, self.check_box_check_upd_on_startup) + + self.checkbox_show_startpage = QCheckBox(self.tabBase) + self.checkbox_show_startpage.setObjectName(u"checkbox_show_startpage") + self.checkbox_show_startpage.setChecked(True) + + self.formLayout_2.setWidget(6, QFormLayout.LabelRole, self.checkbox_show_startpage) self.verticalLayout_8.addLayout(self.formLayout_2) @@ -354,7 +349,7 @@ class Ui_Form(object): self.retranslateUi(Form) - self.stackedWidget.setCurrentIndex(2) + self.stackedWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0) @@ -374,10 +369,16 @@ class Ui_Form(object): ___qlistwidgetitem2.setText(QCoreApplication.translate("Form", u"\u683c\u5f0f\u5316", None)); self.listWidget.setSortingEnabled(__sortingEnabled) + self.label_theme.setText(QCoreApplication.translate("Form", u"UI Theme", None)) + self.comboBox_theme.setItemText(0, QCoreApplication.translate("Form", u"Fusion", None)) + self.comboBox_theme.setItemText(1, QCoreApplication.translate("Form", u"Qdarkstyle", None)) + self.comboBox_theme.setItemText(2, QCoreApplication.translate("Form", u"windowsvista", None)) + self.comboBox_theme.setItemText(3, QCoreApplication.translate("Form", u"Windows", None)) + self.label.setText(QCoreApplication.translate("Form", u"Work Directory", None)) + self.toolButton_workspace.setText(QCoreApplication.translate("Form", u"...", None)) self.label_15.setText(QCoreApplication.translate("Form", u"Output Directory", None)) - self.checkBox.setText(QCoreApplication.translate("Form", u"Check upd on startup", None)) - self.checkBox_2.setText(QCoreApplication.translate("Form", u"Start with system", None)) + self.toolButton_output.setText(QCoreApplication.translate("Form", u"...", None)) self.label_16.setText(QCoreApplication.translate("Form", u"UI Language", None)) self.comboBox_9.setItemText(0, QCoreApplication.translate("Form", u"\u7b80\u4f53\u4e2d\u6587", None)) self.comboBox_9.setItemText(1, QCoreApplication.translate("Form", u"English", None)) @@ -386,15 +387,8 @@ class Ui_Form(object): self.comboBox_8.setItemText(0, QCoreApplication.translate("Form", u"utf-8", None)) self.comboBox_8.setItemText(1, QCoreApplication.translate("Form", u"gb2312", None)) - self.toolButton_workspace.setText(QCoreApplication.translate("Form", u"...", None)) - self.toolButton_output.setText(QCoreApplication.translate("Form", u"...", None)) - self.label_theme.setText(QCoreApplication.translate("Form", u"UI Theme", None)) - self.comboBox_theme.setItemText(0, QCoreApplication.translate("Form", u"Fusion", None)) - self.comboBox_theme.setItemText(1, QCoreApplication.translate("Form", u"Qdarkstyle", None)) - self.comboBox_theme.setItemText(2, QCoreApplication.translate("Form", u"windowsvista", None)) - self.comboBox_theme.setItemText(3, QCoreApplication.translate("Form", u"Windows", None)) - - self.checkBox_minitray.setText(QCoreApplication.translate("Form", u"Minimize to tray", None)) + self.check_box_check_upd_on_startup.setText(QCoreApplication.translate("Form", u"Check upd on startup", None)) + self.checkbox_show_startpage.setText(QCoreApplication.translate("Form", u"\u663e\u793a\u8d77\u59cb\u9875\u9762", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabBase), QCoreApplication.translate("Form", u"Basic", None)) self.checkBox_3.setText(QCoreApplication.translate("Form", u"Interlaced coloring", None)) self.label_10.setText(QCoreApplication.translate("Form", u"Table Header Background:", None)) @@ -441,7 +435,7 @@ class Ui_Form(object): self.label_8.setText(QCoreApplication.translate("Form", u"\u8d27\u5e01\u7b26\u53f7\u4f4d\u4e8e:", None)) self.pushButton_help.setText(QCoreApplication.translate("Form", u"Help", None)) - self.pushButton_ok.setText(QCoreApplication.translate("Form", u"Ok", None)) + self.pushButton_ok.setText(QCoreApplication.translate("Form", u"OK", None)) self.pushButton_cancel.setText(QCoreApplication.translate("Form", u"Cancel", None)) # retranslateUi diff --git a/features/ui/ui_option.ui b/features/ui/ui_option.ui index addaebe83857f069a47dc9bf2f462b207033d59b..f9394124cfd07d880f577bf506eea314a30dcdd7 100644 --- a/features/ui/ui_option.ui +++ b/features/ui/ui_option.ui @@ -44,7 +44,7 @@ - 2 + 0 @@ -75,79 +75,44 @@ - - - - Work Directory - - - - - - - Output Directory - - - - - - - Check upd on startup - - - true - - - - - - - Start with system - - - - - + + - UI Language + UI Theme - - + + - 简体中文 + Fusion - English + Qdarkstyle - - - - - - Encoding - - - - - - utf-8 + windowsvista - gb2312 + Windows + + + + Work Directory + + + @@ -162,6 +127,13 @@ + + + + Output Directory + + + @@ -176,41 +148,62 @@ - - + + - UI Theme + UI Language - - + + - Fusion + 简体中文 - Qdarkstyle + English + + + + + + Encoding + + + + + - windowsvista + utf-8 - Windows + gb2312 + + + + Check upd on startup + + + true + + + - + - Minimize to tray + 显示起始页面 true @@ -569,7 +562,7 @@ p, li { white-space: pre-wrap; } - Ok + OK diff --git a/features/util/update.py b/features/util/update.py index 9c30689db40fbd0349e8d12a9f73bc19e2f34e58..22aad5c8cbadec6ec8b28cecb05ad3826fc09737 100644 --- a/features/util/update.py +++ b/features/util/update.py @@ -11,7 +11,7 @@ from PySide2.QtCore import Qt, QThread, Signal from PySide2.QtWidgets import QProgressBar, QVBoxLayout, QLabel, QApplication, QDesktopWidget, QDialog, QHBoxLayout, \ QPushButton, QTableWidgetItem, QHeaderView, QTableView -from features.io.settings import Settings +import utils from features.util.check_update_ui import Ui_Dialog from features.util.make_update import should_be_recorded @@ -184,7 +184,6 @@ class UpdateClientThread(BaseUpdateThread): return if response.status_code == 200: local_path = self.root.joinpath(item) - # local_path = Path('D:/temp/temp').joinpath(item) if not local_path.parent.is_dir(): os.makedirs(local_path.parent) with open(local_path, 'wb') as f: @@ -208,23 +207,23 @@ class UpdateClientThread(BaseUpdateThread): class UpdateTipClient(Ui_Dialog): - def __init__(self): + def __init__(self, startup: bool): self.dialog = QDialog() self.setupUi(self.dialog) self.url = MD5_JSON_URL self.update_files_list = [] self.delete_files_list = [] self.root = Path(__file__).parent.parent.parent.parent - self.is_start_thread() + self.is_start_thread(startup) - def is_start_thread(self): + def is_start_thread(self, startup: bool): """ 根据根目录下是否存在.git目录,判断是否处于开发状态,开发状态不检查更新 根据设置界面检查更新checkbox是否为True,决定是否启动检查更新 """ if self.root.joinpath('.git').is_dir(): return - if Settings.get_instance().get('check_update'): + if not (startup and (not utils.get_settings_item_from_file("config.ini", "MAIN/CHECK_UPDATE"))): self.thread = UpdateTipThread(url=self.url) self.thread.update_files_list.connect(self.set_update_files_list) self.thread.delete_files_list.connect(self.set_delete_files_list) @@ -283,7 +282,6 @@ class UpdateTipClient(Ui_Dialog): def set_exist_update(self, flag): """ 存在更新则启动界面 - TODO:暂时不要启动这样的更新界面了。要更新的话,手动更新.因为现在设置项还没有做好。 """ if flag: self.init_gui() diff --git a/load_modules.py b/load_modules.py index 01602d0e14176d3e30092023007576f17f827bf9..14542a2d06d4e094da886818aa39eee2c224d0bb 100644 --- a/load_modules.py +++ b/load_modules.py @@ -1,8 +1,11 @@ +import logging +import os + from PySide2.QtCore import QTranslator, QSettings from PySide2.QtGui import QFontDatabase from PySide2.QtWidgets import QApplication -from utils import * +from utils import get_root_dir, get_config_file_dir logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -18,14 +21,12 @@ def load_translator(app: QApplication): app.translator = QTranslator() app.translator2 = QTranslator() - settings = QSettings(os.path.join(get_root_dir(), "config.ini"), QSettings.IniFormat) + settings = QSettings(get_config_file_dir("config.ini"), QSettings.IniFormat) language = settings.value("MAIN/LANGUAGE") logger.debug("获取设置项MAIN/LANGUAGE: %s" % language) - print(get_root_dir()) path_lang = os.path.join(get_root_dir(), 'languages', '{}'.format(language), '{}.qm'.format(language)) logger.debug("翻译文件的路径:%s" % path_lang) - if os.path.isfile(path_lang): app.translator.load(path_lang) app.installTranslator(app.translator) diff --git a/packages/applications_toolbar/applications_toolbar.py b/packages/applications_toolbar/applications_toolbar.py index 76cafa0aad226fa003914dd31c4d41263a29b53b..2ef583f5a1f5d343b7d67361c53d31ad9393410d 100644 --- a/packages/applications_toolbar/applications_toolbar.py +++ b/packages/applications_toolbar/applications_toolbar.py @@ -269,7 +269,7 @@ class PMApplicationsToolBar(PMGToolBar): Returns: """ - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() app_paths = APPManager.get_instance().get_app_paths() path_index = 1 if len(app_paths) > 1 else 0 dlg = PMGPanelDialog(parent=self, views=[]) @@ -299,7 +299,7 @@ class PMApplicationsToolBar(PMGToolBar): Returns: """ - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() dev_path = QFileDialog.getExistingDirectory(self, self.tr('Select Packup App Developing Folder'), directory=work_dir) diff --git a/packages/applications_toolbar/dev_tools.py b/packages/applications_toolbar/dev_tools.py index 7702388164f242efff1f82b5948ae1965c755ca7..c4f82c85e6557c491f9ce0fd4d7002865d72f4db 100644 --- a/packages/applications_toolbar/dev_tools.py +++ b/packages/applications_toolbar/dev_tools.py @@ -53,7 +53,7 @@ class DevelopTools(QObject): def open_designer(self): import subprocess - self.workdir = self.extension_lib.Program.get_settings()['work_dir'] + self.workdir = self.extension_lib.Program.get_work_dir() if self.check_installed(self.designer_path): subprocess.Popen(self.designer_path, cwd=self.workdir) @@ -136,7 +136,7 @@ class DevelopTools(QObject): """ ret = self.show_covered_message('.ui', '.py') if ret == QMessageBox.Ok: - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() ui_files = self.list_files(work_dir, '.ui') for ui_file_path in ui_files: try: @@ -156,7 +156,7 @@ class DevelopTools(QObject): Returns: """ - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() pro_path = '' for name in os.listdir(work_dir): if name.endswith('.pro'): @@ -211,7 +211,7 @@ class DevelopTools(QObject): Returns: """ - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() ret = self.show_covered_message(self.tr('.py files in working directory with tr() or translate() functions'), '.ts') if ret == QMessageBox.Ok: @@ -257,7 +257,7 @@ class DevelopTools(QObject): """ ret = self.show_covered_message('.qrc', '*_rc.py') if ret == QMessageBox.Ok: - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + work_dir = self.extension_lib.Program.get_work_dir() qrcs = self.list_files(work_dir, '.qrc') for qrc_path in qrcs: try: diff --git a/packages/code_editor/codeeditor/tabwidget.py b/packages/code_editor/codeeditor/tabwidget.py index ea32224e714c9d6b1af286c9d475431e2f7ebc95..fa0bbcc75b2371666da449048c9bfd1041b17c74 100644 --- a/packages/code_editor/codeeditor/tabwidget.py +++ b/packages/code_editor/codeeditor/tabwidget.py @@ -21,26 +21,26 @@ translated into English by those websites. __version__ = '0.1' +import sys +import cgitb import logging import os -import sys import re import time from contextlib import redirect_stdout from io import StringIO from queue import Queue -from typing import TYPE_CHECKING, List -import pmgwidgets -from PySide2.QtCore import QDir, QLocale, QObject, Signal, QThread, QTemporaryFile, QTimer +from typing import List +from typing import TYPE_CHECKING, Dict, Union, Tuple, Optional, Any + +from PySide2.QtCore import QDir, QObject, Signal, QThread, QTemporaryFile, QTimer from PySide2.QtGui import QCloseEvent from PySide2.QtWidgets import QTabWidget, QFileDialog, QMessageBox, QApplication, QSizePolicy, QWidget, QComboBox - -from packages.code_editor.codeeditor.qtpyeditor import PythonHighlighter - # TODO to remove (use extensionlib) from flake8.main.application import Application -from typing import TYPE_CHECKING, Dict, Union, Tuple, Optional, Any +import pmgwidgets +from packages.code_editor.codeeditor.qtpyeditor import PythonHighlighter from pmgwidgets import PMDockObject, in_unit_test, PMGFileSystemWatchdog, UndoManager if TYPE_CHECKING or in_unit_test(): @@ -742,7 +742,8 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): # 打开文件 self.extension_lib.UI.get_toolbar_widget('code_editor_toolbar', 'button_open_script').clicked.connect( self.slot_open_script) - interpreters = self.extension_lib.Program.get_settings()['external_interpreters'] + interpreters = self.extension_lib.Program.get_settings_item_from_file("config.ini", + "RUN/EXTERNAL_INTERPRETERS") interpreter_names = [self.tr('Builtin (3.8.5)')] + [d['name'] for d in interpreters] combo_box: QComboBox = self.extension_lib.UI.get_toolbar_widget('code_editor_toolbar', 'combobox_interpreter') @@ -821,7 +822,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): Returns: """ - interpreters = self.extension_lib.Program.get_settings()['external_interpreters'] + interpreters = self.extension_lib.Program.get_settings_item_from_file("config.ini", 'RUN/EXTERNAL_INTERPRETERS') combo.clear() combo.addItem(self.tr('Builtin (%s)' % sys.version.split()[0])) for interpreter in interpreters: @@ -838,9 +839,8 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): elif interpreter_index == 0: self._current_executable = sys.executable else: - print(self.extension_lib.Program.get_settings()['external_interpreters'], interpreter_index) self._current_executable = \ - self.extension_lib.Program.get_settings()['external_interpreters'][interpreter_index - 1]['path'] + self.extension_lib.Program.get_settings_item_from_file("config.ini", 'RUN/EXTERNAL_INTERPRETERS')[interpreter_index - 1]['path'] def on_tab_switched(self, index: int) -> None: for i in range(self.count()): @@ -955,9 +955,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): if __name__ == '__main__': - import sys - import cgitb - import logging + cgitb.enable(format='text') logging.basicConfig(level=logging.INFO) @@ -976,11 +974,9 @@ if __name__ == '__main__': w.setMinimumHeight(600) w.setup_ui() code_editor_root_directory = os.path.dirname(__file__) - # w.slot_new_script( - # '/home/hzy/Desktop/create_dataset.py') # os.path.join(code_editor_root_directory, 'tests', 'test_file.py')) w.slot_new_script(r'C:/Users/12957/documents/developing/Python/pyminer_workdir/app_designer.py') w.currentWidget().goto_line(5) - # w.on_work_dir_changed(r'C:\Users\12957\AppData') - # w.slot_new_script(r'c:/users/12957/desktop/test.md') - # w.on_work_dir_changed('c:/users/12957/desktop') + # w.on_work_dir_changed(r'') + # w.slot_new_script(r'') + # w.on_work_dir_changed('') sys.exit(app.exec_()) diff --git a/packages/code_editor/main.py b/packages/code_editor/main.py index 7c1bd8fda30ac5b96691a54c68016ae3b308258b..d5e1fb5fc46ffa090b7d412400671c233dec7dc6 100644 --- a/packages/code_editor/main.py +++ b/packages/code_editor/main.py @@ -1,7 +1,6 @@ -import sys import os -import time -from typing import Dict, Union, Any +import sys +from typing import Dict, Union from PySide2.QtCore import QLocale, QTranslator from PySide2.QtWidgets import QApplication @@ -71,15 +70,14 @@ class Extension(BaseExtension): Deal with events that settings changed. :return: """ - theme = self.extension_lib.Program.get_settings()['theme'] - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + theme = self.extension_lib.Program.get_theme() + work_dir = self.extension_lib.Program.get_work_dir() if theme != self.editor_widget._color_scheme: if theme.lower() in ('fusion', 'windows', 'windowsvista'): self.editor_widget.set_color_scheme('light') else: self.editor_widget.set_color_scheme('dark') - - work_dir = self.extension_lib.Program.get_settings()['work_dir'] + print(work_dir) self.editor_widget.on_work_dir_changed(work_dir) def bind_event(self): diff --git a/packages/dataio/export.py b/packages/dataio/export.py index c8c2c0d032691d3573cf906f4690a78d3f8bbc1a..568e26693d5289adfe85e83d9c846a91414d007a 100644 --- a/packages/dataio/export.py +++ b/packages/dataio/export.py @@ -16,7 +16,7 @@ class ExportDialog(QDialog): super(ExportDialog, self).__init__(parent=None) self.normal = False if initial_path == '' and not in_unit_test(): - initial_path = self.extension_lib.Program.get_settings()['work_dir'] + initial_path = self.extension_lib.Program.get_work_dir() self.setLayout(QVBoxLayout()) self.initial_var_name = initial_var_name self.initial_path = initial_path diff --git a/packages/dataio/sample.py b/packages/dataio/sample.py index 30f0c790d73186a1cfbbc34c779a1a412e855c3c..4f207de6d1a9cc5bbf011b6f564d583620a2f942 100644 --- a/packages/dataio/sample.py +++ b/packages/dataio/sample.py @@ -237,7 +237,7 @@ class ImportDialog(QDialog): def get_work_dir(self) -> str: """获取工作路径""" - return self.extension_lib.Program.get_settings()['work_dir'] + return self.extension_lib.Program.get_work_dir() def center(self): """将窗口置于中心""" diff --git a/packages/file_tree/main.py b/packages/file_tree/main.py index d86e7bdc07ea33982b585c7948ccb6332032c330..697259db8ab8d6eae4938f4cb07f8393700d082b 100644 --- a/packages/file_tree/main.py +++ b/packages/file_tree/main.py @@ -1,11 +1,11 @@ import os from typing import Callable -from PySide2.QtWidgets import QApplication from PySide2.QtCore import QLocale, QTranslator +from PySide2.QtWidgets import QApplication -from .file_tree import PMFilesTree from features.extensions.extensionlib import BaseExtension, BaseInterface +from .file_tree import PMFilesTree file_name = os.path.join(os.path.dirname(__file__), 'translations', 'qt_%s.qm' % QLocale.system().name()) app = QApplication.instance() @@ -23,9 +23,11 @@ class Extension(BaseExtension): files_tree: 'PMFilesTree' = self.widgets['PMFilesTree'] files_tree.extension_lib = self.extension_lib self.interface.file_widget = files_tree - settings = self.extension_lib.Program.get_settings() - self.extension_lib.Signal.get_settings_changed_signal().connect( # 当主界面设置改变信号发出时,改变工作路径。 - lambda: files_tree.change_current_path(settings['work_dir'])) + def on_settings_changed(): + work_dir = self.extension_lib.Program.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") + files_tree.change_current_path(work_dir) + + self.extension_lib.Signal.get_settings_changed_signal().connect(on_settings_changed) # 当主界面设置改变信号发出时,改变工作路径。 class Interface(BaseInterface): diff --git a/packages/ipython_console/README.md b/packages/ipython_console/README.md new file mode 100644 index 0000000000000000000000000000000000000000..561b0588c98588add129d24189b828ce84e86aa1 --- /dev/null +++ b/packages/ipython_console/README.md @@ -0,0 +1,4 @@ +- requirements_ipython_node.txt:一个IPython作为连接到外部的IPython节点,至少需要装的包。 +这些包看起来不少,但是每个都比较小,即使用国外镜像源也很快。 + +- \ No newline at end of file diff --git a/packages/ipython_console/initialize.py b/packages/ipython_console/initialize.py index fd1f9dd362887959286ae89ddb3d0026ef1e44c9..433382e4b850f3be38234ef66d96445a5df9aad2 100644 --- a/packages/ipython_console/initialize.py +++ b/packages/ipython_console/initialize.py @@ -7,23 +7,30 @@ import types import typing -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import scipy.integrate -import scipy.stats - -__packages = [np, plt, pd, scipy.integrate, scipy.stats] -from IPython.core.magic import register_line_cell_magic -from core import * from pyminer_comm import modify_settings, set_data_desc_dic from pyminer_comm.base import dict_to_b64, b64_to_dict, dict_to_pickle, pickle_to_dict, DataDesc, is_big_variable, \ NoPreviewError +try: + import matplotlib.pyplot as plt + import numpy as np + import pandas as pd + import scipy.integrate + import scipy.stats + from core import * + + __packages = [np, plt, pd, scipy.integrate, scipy.stats] +except Exception as e: + import traceback + + traceback.print_exc() + if typing.TYPE_CHECKING: from IPython.core.interactiveshell import InteractiveShell from IPython.core.getipython import get_ipython +from IPython.core.magic import register_line_cell_magic + __ip: 'InteractiveShell' = get_ipython() __ip.builtin_vars = [__k for __k in globals().keys()] # 内置保留变量,不可删除或者清空。 @@ -51,31 +58,43 @@ def __init_server(): server = Flask('ipython_data_server') __ip = get_ipython() __ip.shms = {} + log.debug("started server!") def get_preview(var: typing.Any): if is_big_variable(var): - if isinstance(var, pd.DataFrame): - if var.shape[0] > DataDesc.max_pandas_rows: - return var.iloc[:DataDesc.max_pandas_rows, :] - else: - return var + if isinstance(var, (int, float, complex)): + return var + if isinstance(var, (list, tuple)): + return var[:DataDesc.max_len] elif isinstance(var, str): if len(var) > DataDesc.max_str_len: return var[:DataDesc.max_str_len] else: return var - elif isinstance(var, (list, tuple)): - return var[:DataDesc.max_len] - elif isinstance(var, np.ndarray): - if is_big_variable(var): - return NoPreviewError( - 'Big numpy.ndarray with shape: %s, dtype:%s, memory usage: %s MB, cannot be viewed.' - % (repr(var.shape), repr(var.dtype), repr(var.nbytes / 1024 / 1024))) - else: - return var + try: + if isinstance(var, pd.DataFrame): + if var.shape[0] > DataDesc.max_pandas_rows: + return var.iloc[:DataDesc.max_pandas_rows, :] + else: + return var + + if isinstance(var, np.ndarray): + if is_big_variable(var): + return NoPreviewError( + 'Big numpy.ndarray with shape: %s, dtype:%s, memory usage: %s MB, cannot be viewed.' + % (repr(var.shape), repr(var.dtype), repr(var.nbytes / 1024 / 1024))) + else: + return var + + except Exception as e: + return e + + else: return NoPreviewError('Big Variable typed %s cannot be viewed.' % (type(var))) # 无法产生预览视图。 + else: + return repr(var) def write_log(arg): if os.path.exists(r'c:\users\hzy\Desktop\log.txt'): @@ -361,59 +380,16 @@ def __cell_exec_func(raw_cell, store_history=False, silent=False, shell_futures= Returns: """ - import ast __ip = get_ipython() __cwd = os.getcwd() - class PyMinerIPyConsoleNodeTransformer(ast.NodeTransformer): - def __init__(self): - super(PyMinerIPyConsoleNodeTransformer, self).__init__() - self.identifier_list = [] - self.str_list = [] - - def visit_Name(self, node: ast.Name): - """ - - Args: - node: - - Returns: - - """ - self.identifier_list.append(node.id) - - def visit_Str(self, node: ast.Str): - """ - - Args: - node: - - Returns: - - """ - self.str_list.append(node.s) - - def show_identifiers_might_changed(self, code): - """ - - Args: - code: - - Returns: - - """ - self.identifier_list = [] - self.str_list = [] - self.visit(ast.parse(code)) - return [s for s in list(set(self.identifier_list + self.str_list)) if s.isidentifier()] - - s = get_ipython().original_run_cell_func(raw_cell, store_history=store_history, silent=silent, - shell_futures=shell_futures) + __ret = get_ipython().original_run_cell_func(raw_cell, store_history=store_history, silent=silent, + shell_futures=shell_futures) __ip.var_name_list = list(__ip.filter_vars(globals()).keys()) __ip.update_workspace() if __cwd != os.getcwd(): - modify_settings({'work_dir': os.getcwd()}) - return s + modify_settings({'MAIN/PATH_WORKDIR': os.getcwd()}) + return __ret def __chdir(path: str): diff --git a/packages/ipython_console/ipythonqtconsole.py b/packages/ipython_console/ipythonqtconsole.py index 3ea9eec9399235c69921f1823fda5798fdf3c20d..e10b1683c6fbf14cf9592d60807c7e7b3d5f8835 100644 --- a/packages/ipython_console/ipythonqtconsole.py +++ b/packages/ipython_console/ipythonqtconsole.py @@ -10,10 +10,9 @@ Created on 2020/8/24 """ import os from typing import Tuple -import time -from pmgwidgets import PMDockObject, get_ipython_console_class, in_unit_test +from pmgwidgets import PMDockObject, in_unit_test +from pmgwidgets.widgets.basic.others.console import PMGIpythonConsole from PySide2.QtWidgets import QApplication -PMGIpythonConsole = get_ipython_console_class() if QApplication.instance() is not None: PMGIpythonConsole.install_translator() @@ -38,7 +37,7 @@ class ConsoleWidget(PMGIpythonConsole, PMDockObject): def setup_ui(self): super().setup_ui() if not in_unit_test(): - style = self.lib.Program.get_settings()['theme'] + style = self.lib.Program.get_theme() self.change_ui_theme(style) def connect_to_datamanager(self, data_manager): @@ -57,11 +56,10 @@ class ConsoleWidget(PMGIpythonConsole, PMDockObject): if in_unit_test(): pwd = os.path.dirname(__file__) else: - pwd = self.extension_lib.Program.get_settings()['work_dir'] + pwd = self.extension_lib.Program.get_work_dir() pwd = pwd.replace('\\', '\\\\') from utils import get_root_dir cmd = 'import sys;sys.path.append(r\'%s\')' % get_root_dir() - print(cmd) self.execute_command(cmd, True, '') cdcmd = 'import os;os.chdir(\'%s\')' % pwd # 启动时切换到当前工作路径。 self.execute_command(cdcmd, True, '') diff --git a/packages/ipython_console/lexersplit.py b/packages/ipython_console/lexersplit.py deleted file mode 100644 index 7ee30f30b77e0e2060ab157c3e92cbb4937f1973..0000000000000000000000000000000000000000 --- a/packages/ipython_console/lexersplit.py +++ /dev/null @@ -1,69 +0,0 @@ -import ast -import typing - - -class NodeVisitor(ast.NodeVisitor): - def visit_Name(self, node: ast.Name) -> typing.Any: - print(node) - - -class NodeTransformer(ast.NodeTransformer): - # def visit_Str(self, tree_node): - # return ast.Str('String: ' + tree_node.s) - def __init__(self): - super(NodeTransformer, self).__init__() - self.identifier_list = [] - self.str_list = [] - - def visit_Name(self, node: ast.Name): - """ - - Args: - node: - - Returns: - - """ - self.identifier_list.append(node.id) - - def visit_Str(self, node: ast.Str) -> typing.Any: - """ - - Args: - node: - - Returns: - - """ - self.str_list.append(node.s) - - def show_identifiers_might_changed(self, code) -> list: - """ - - Args: - code: - - Returns: - - """ - self.identifier_list = [] - self.str_list = [] - self.visit(ast.parse(code)) - return [s for s in list(set(self.identifier_list + self.str_list)) if s.isidentifier()] - - -if __name__ == '__main__': - s = ''' - class A: - def aaa(): - b=123 - print(b) - fruits = ['grapes', 'mango'] - name = 'peter' - for fruit in fruits: - print('{} likes {}'.format(name, fruit)) - ''' - - # NodeTransformer().visit(tree_node) - print(NodeTransformer().show_identifiers_might_changed(s)) - # NodeVisitor().visit(tree_node) diff --git a/packages/ipython_console/main.py b/packages/ipython_console/main.py index c730eaae30249ae4ad10d9149e231081e061446f..1492208da5d4cd739105b32b3cb2408ba6a53f5b 100644 --- a/packages/ipython_console/main.py +++ b/packages/ipython_console/main.py @@ -37,16 +37,16 @@ class Extension(BaseExtension): self.interface.widget = self.console self.extension_lib.Signal.get_settings_changed_signal().connect(self.on_settings_changed) self.extension_lib.Signal.get_events_ready_signal().connect(self.on_settings_changed) - self._work_dir = self.extension_lib.Program.get_settings()['work_dir'] + self._work_dir = self.extension_lib.Program.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") def on_settings_changed(self): """ 如果设置项发生改变,重新加载主题文件。 TODO:倘若ipython发生改变之后,工作路径如何跟着改变? """ - settings = self.extension_lib.Program.get_settings() - self.console.change_ui_theme(settings['theme']) - work_dir = settings['work_dir'] + theme = self.extension_lib.Program.get_theme() + self.console.change_ui_theme(theme) + work_dir = self.extension_lib.Program.get_work_dir() if not os.path.samefile(work_dir, self._work_dir): # samefile函数:既可判断是否为同一文件,也可判断是否为同一文件夹 self.command = self.interface.run_command("get_ipython().chdir(\'%s\')" % work_dir.replace('\\', '\\\\'), hidden=False) diff --git a/packages/ipython_console/requirements_ipython_node.txt b/packages/ipython_console/requirements_ipython_node.txt new file mode 100644 index 0000000000000000000000000000000000000000..92f07469576fe5cbed28d8215cbda3d743568fd2 --- /dev/null +++ b/packages/ipython_console/requirements_ipython_node.txt @@ -0,0 +1,4 @@ +cloudpickle +Flask +ipykernel +ipython diff --git a/packages/pm_calc/fastui/dblquad.py b/packages/pm_calc/fastui/dblquad.py new file mode 100644 index 0000000000000000000000000000000000000000..853c7391b4f0c79078466e7f16859f133c696e66 --- /dev/null +++ b/packages/pm_calc/fastui/dblquad.py @@ -0,0 +1,210 @@ +import ast +from typing import Any, List, Tuple + +from PySide2.QtWidgets import QApplication + +from pmgwidgets import FunctionGUIDialog + + +class CodeVisitor(ast.NodeVisitor): + def __init__(self): + super(CodeVisitor, self).__init__() + self.preserved = {"pi", "e"} + self.called = set() + self.func_args = set() + self._names = set() + + def visit_Name(self, node: ast.Name) -> Any: + self._names.add(node.id) + + def visit_Call(self, node: ast.Call) -> Any: + self.generic_visit(node) + self.called.add(node.func.id) + + def get_result(self) -> Tuple[List[str], List[str]]: + """ + + Returns: 定义的名称,以及调用的ID名称。 + + """ + names = self._names.copy() + names.difference_update(self.preserved) + names.difference_update(self.called) + return list(names), list(self.called) + + +class Function(): + def __init__(self, s): + self.name = s + + def __repr__(self): + return self.name + + +def convert_to_lambda(code): + print(code) + original_code = code + code = code.replace("np.", "") + cv = CodeVisitor() + cv.visit(ast.parse(code)) + args_list, funcs = cv.get_result() + args = '' + for a in args_list: + args += a + "," + args = args.strip(", ") + + ret = "lambda {ARGS}: {FCN}".format(ARGS=args, FCN=original_code) + return Function(ret) + + +dic = { + "title": "数据透视", + "func_name": "scipy.integrate.dblquad", + "with_object": False, + "args": [ + { + "name": "func", + "title": "被积函数", + "optional": False, + "ctrl": { + "type": "multitype_ctrl", + "title": "选择被积函数", + "init": "x*y", + "types": + [{ + "type_title": "输入表达式", + "ctrls": [ + ("line_ctrl", "", '输入表达式并自动转换为函数', "np.sin(2*np.pi*x)"), + ], + "on_ok": lambda data: convert_to_lambda(data[""]) + }, { + "type_title": "输入函数", + "ctrls": [ + ("line_ctrl", "", '输入函数代码', "lambda x: np.sin(2*np.pi*x)"), + ], + "on_ok": lambda data: Function(data[""]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variable", "选择变量", ""), + ], + }] + } + }, + { + "name": "hfun", + "title": "内层函数上界", + "optional": False, + "ctrl": { + "type": "multitype_ctrl", + "title": "内层函数上界", + "init": "np.sin(2*np.pi*x)", + "types": + [{ + "type_title": "输入表达式", + "ctrls": [ + ("line_ctrl", "", '输入表达式并自动转换为函数', "np.sin(2*np.pi*x)"), + ], + "on_ok": lambda data: convert_to_lambda(data[""]) + }, { + "type_title": "输入函数", + "ctrls": [ + ("line_ctrl", "", '输入函数代码', "lambda x: np.sin(2*np.pi*x)"), + ], + "on_ok": lambda data: Function(data[""]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variable", "选择变量", ""), + ], + }] + } + }, + { + "name": "gfun", + "title": "内层函数下界", + "optional": False, + "ctrl": { + "type": "multitype_ctrl", + "title": "内层函数下界", + "init": "lambda x:0", + "types": + [{ + "type_title": "输入表达式", + "ctrls": [ + ("line_ctrl", "", '输入表达式并自动转换为函数', "np.sin(2*np.pi*x)"), + ], + "on_ok": lambda data: convert_to_lambda(data[""]) + }, { + "type_title": "输入函数", + "ctrls": [ + ("line_ctrl", "", '输入函数代码', "lambda x:0"), + ], + "on_ok": lambda data: Function(data[""]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variable", "选择变量", ""), + ], + }] + } + }, + { + "name": "a", + "title": "积分下限", + "optional": False, + "ctrl": { + "type": "multitype_ctrl", + "title": "积分下限", + "init": 0, + "types": + [{ + "type_title": "输入下界", + "ctrls": [ + ("number_ctrl", "", '输入下界', 0), + ], + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variable", "选择变量", ""), + ], + }] + } + }, + { + "name": "b", + "title": "积分上限", + "optional": False, + "ctrl": { + "type": "multitype_ctrl", + "title": "积分上限", + "init": 1, + "types": + [{ + "type_title": "输入数值", + "ctrls": [ + ("number_ctrl", "", '输入数值', 1), + ] + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variable", "选择变量", ""), + ], + }] + } + }, + ] + +} + + +class PivotDialog(FunctionGUIDialog): + def __init__(self): + FunctionGUIDialog.__init__(self, dic) + + +if __name__ == '__main__': + app = QApplication([]) + md = PivotDialog() + md.show() + app.exec_() diff --git a/packages/pmagg/ui/linestyles.py b/packages/pmagg/ui/linestyles.py index 9689a82fe7d4f4dc5be95b99121329af068049db..f59b5dc3b830a6b60ff76364b8a128ca876ebaf0 100644 --- a/packages/pmagg/ui/linestyles.py +++ b/packages/pmagg/ui/linestyles.py @@ -3,7 +3,7 @@ # @Author : 别着急慢慢来 # @FileName: linestyles.py # matplotlib中的常见颜色 -from matplotlib.colors import LogNorm, NoNorm, BoundaryNorm, DivergingNorm, PowerNorm, SymLogNorm, TwoSlopeNorm, \ +from matplotlib.colors import LogNorm, NoNorm, BoundaryNorm, PowerNorm, SymLogNorm, TwoSlopeNorm, \ Normalize languages = ['en', 'zh_CN'] diff --git a/packages/qt_vditor/client.py b/packages/qt_vditor/client.py index 74dde8215f4af3c2ace1a36953aaea959829ab18..2031bd9714864fe4842140271260808a8ecf4895 100644 --- a/packages/qt_vditor/client.py +++ b/packages/qt_vditor/client.py @@ -1,11 +1,12 @@ +import json import os -from PySide2 import QtWidgets, QtCore -from PySide2.QtWebEngineWidgets import QWebEngineView -import sys import re +import sys import time + import requests -import json +from PySide2 import QtWidgets, QtCore +from PySide2.QtWebEngineWidgets import QWebEngineView class Window(QtWidgets.QDialog): @@ -80,6 +81,6 @@ class Window(QtWidgets.QDialog): if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) win = Window(url='http://127.0.0.1:5000/qt_vditor') - win.load_file(file_path='D:/pyminer/pyminer2/extensions/packages/pmagg/sample/sample_code.md') + win.load_file(file_path=os.path.join(os.path.dirname(__file__), "examples", "sample.md")) win.show() app.exec_() diff --git a/packages/qt_vditor/examples/sample.md b/packages/qt_vditor/examples/sample.md new file mode 100644 index 0000000000000000000000000000000000000000..da6e237a1363dd5646a42d257fd7914ee2f66396 --- /dev/null +++ b/packages/qt_vditor/examples/sample.md @@ -0,0 +1,7 @@ +# 这是一个Markdown文件的一级标题 +## 这是一个Markdown文件的二级标题 +```python +def func(): + print("Hello Vditor!") +func() +``` \ No newline at end of file diff --git a/packages/socket_server/server_by_socket.py b/packages/socket_server/server_by_socket.py index 03895aa8cb079100e164e0778efac9a4258e69af..9975e4f22edfc9bfc896ea785ef72710900b80e1 100644 --- a/packages/socket_server/server_by_socket.py +++ b/packages/socket_server/server_by_socket.py @@ -1,14 +1,13 @@ import json import logging +import queue import sys -import time -from typing import Dict, Any, List, Tuple +import threading +from typing import Dict, Any -import cloudpickle +from PySide2.QtCore import Signal, QObject, QThread from PySide2.QtWidgets import QApplication -from PySide2.QtCore import Signal, QTimer, QObject, QThread -import threading -import queue + from pyminer_comm.base import b64_to_dict, dict_to_b64, DataDesc, get_protocol logger = logging.getLogger(__name__) @@ -18,17 +17,10 @@ INTERFACE_CALLED = 3 from flask import Flask, request app = Flask(__name__) -worker: 'LoopWork' = None message_queue: queue.Queue = queue.Queue() extension_lib = None -@app.route('/get_data_desc') -def hello_world(): - global message_queue - return 'Hello World!' - - @app.route('/modify_settings') def modify_settings(): global message_queue @@ -56,17 +48,27 @@ def get_settings(): :return: """ global message_queue - return json.dumps(extension_lib.Program.get_settings()) + return "['Warning this method was deprecated']" @app.route('/get_stylesheet') def get_stylesheet(): + """ + 获取QApplication的样式表 + Returns: + + """ assert QApplication.instance() is not None return QApplication.instance().styleSheet() @app.route('/set_data') def modify_data_descs(): + """ + 将工作空间中设置为data_desc型变量 + Returns: + + """ global message_queue try: datadesc_dict = b64_to_dict(request.args.get('msg')) @@ -83,6 +85,11 @@ def modify_data_descs(): @app.route('/interface_call') def run_command(): + """ + 运行命令,调用接口函数。 + Returns: + + """ global message_queue try: settings = request.args.get('msg') @@ -102,16 +109,6 @@ def run_command(): return "Failed!" -class LoopWork(QObject): - def __init__(self, server_obj: 'Flask'): - super().__init__() - self.server_obj = server_obj - self.threads = [] - - def work(self): - app.run(port=12306) - - class QueueWork(QObject): signal_queue_recv = Signal(object) @@ -149,10 +146,9 @@ class PMGServer(QObject): def __init__(self, parent=None): super().__init__(parent) - global worker self.queue_loop_thread = QThread() - self.queue_worker = QueueWork() + self.queue_worker = QueueWork() # 队列处理 self.queue_worker.moveToThread(self.queue_loop_thread) @@ -172,8 +168,11 @@ class PMGServer(QObject): def on_recv(self, msg: object): """ - :param msg: - :return: + Args: + msg: 收到的信息 + + Returns: + """ if msg[0] == DATA_CHANGED: self.signal_data_set.emit(msg[1]) @@ -181,8 +180,18 @@ class PMGServer(QObject): self.signal_settings_changed.emit(msg[1]) elif msg[0] == INTERFACE_CALLED: self.signal_interface_called.emit(msg[1]['interface'], msg[1]['method'], msg[1]['kwargs'], msg[2]) + else: + logger.error("received error:" + repr(object)) def on_data_set(self, data: Dict[str, Any]): + """ + 当数据设置时的回调 + Args: + data: + + Returns: + + """ names = self.extension_lib.Data.get_all_variable_names() for name in names: if name not in data.keys(): @@ -192,14 +201,16 @@ class PMGServer(QObject): def on_settings_changed(self, settings: Dict[str, Any]): """ - 改变设置 - :param settings - :return: + 改变设置时的回调 + Args: + settings: + + Returns: + """ - for param_name, param_val in settings.items(): - if param_name not in self.extension_lib.Program.get_settings().keys(): - raise ValueError('Parameter name \'%s\' not in settings!' % param_name) - self.extension_lib.Program.update_settings(settings) + for k, v in settings.items(): + self.extension_lib.Program.write_settings_item_to_file("config.ini", k, v) + self.extension_lib.Signal.get_settings_changed_signal().emit() def on_interface_called(self, interface_name: str, method_name: str, kwargs: Dict, res_queue: queue.Queue): interface = self.extension_lib.get_interface(interface_name) @@ -221,7 +232,7 @@ def run_server(port: int = None, ext_lib=None): extension_lib = ext_lib server = PMGServer() - server_thread = threading.Thread(target=app.run, kwargs={'port': 12306}) + server_thread = threading.Thread(target=app.run, kwargs={'port': port}) server_thread.setDaemon(True) server_thread.start() diff --git a/pmgui.py b/pmgui.py index 52f2bddc5d7f9a1db8c15a2678236d3e3730a57f..7e425da5bab7aad32aa82b6cdb63c38713d00081 100644 --- a/pmgui.py +++ b/pmgui.py @@ -1,33 +1,8 @@ -import datetime -import getpass -import os -import time - +from PySide2.QtCore import Qt from PySide2.QtWidgets import QTextEdit -from pmgwidgets import PMGToolBar, ActionWithMessage, PMDockObject, create_icon - -from typing import List, Callable, Tuple, ClassVar - -from PySide2.QtCore import Signal, QTimer, Qt, QTranslator, QLocale, QSize, QCoreApplication -from PySide2.QtGui import QCloseEvent, QTextCursor, QResizeEvent, QFontDatabase, QMoveEvent, QFont, QIcon, QPixmap -from PySide2.QtWidgets import QApplication, QTextEdit, QMessageBox, QToolBar, QSplashScreen, QStatusBar, QTextBrowser, \ - QDialog, QVBoxLayout, QLabel, QHBoxLayout, QPushButton -from utils import get_main_window, get_application from pmgwidgets import PMGToolBar, ActionWithMessage, PMDockObject, create_icon -from features.extensions.extensions_manager.manager import extensions_manager -from features.main_window import base -from features.io.settings import Settings -from features.io.settings import load_theme -from features.interpretermanager.interpretermanager import InterpreterManagerWidget -from features.util.update import perform_update, UpdateTipClient -from features.feedback import FeedbackClient -from features.ui.widgets.controlpanel import PMPageExt -from features.ui.pmwidgets import BaseMainWindow -from multiprocessing import shared_memory -import utils -import logging - +from utils import get_main_window, get_application def updateSplashMsg(ext_load_status: dict): @@ -40,6 +15,7 @@ def updateSplashMsg(ext_load_status: dict): except TypeError: return + class PMToolBarHome(PMGToolBar): """ 定义菜单工具栏按钮。 @@ -174,362 +150,3 @@ class PMToolBarHome(PMGToolBar): class LogOutputConsole(QTextEdit, PMDockObject): pass - -class MainWindow(BaseMainWindow): - setupui_tasks: List[Callable] = [] - boot_timer: QTimer = None - close_signal = Signal() - window_geometry_changed_signal = Signal() - - layouts_ready_signal = Signal() - widgets_ready_signal = Signal() - events_ready_signal = Signal() - - settings_changed_signal = Signal() - - @classmethod - def __new__(cls, *args): - if not hasattr(cls, 'instance'): - instance = super().__new__(cls) - cls.instance = instance - return cls.instance - - def __init__(self, parent=None): - super().__init__(parent) - - t00 = time.time() - settings = Settings() - self.main_option_form = base.OptionForm() - self.project_wizard: base.ProjectWizardForm = None - self.settings_changed_signal.connect(self.on_settings_changed) - self.main_option_form.add_page(self.tr('Interpreter'), InterpreterManagerWidget()) - - utils._main_window = self - - # 主窗体默认大小 - self.resize(1920, 1080) - self.setIconSize(QSize(40, 40)) - # 设置状态栏 - self.statusBar = QStatusBar() - version = utils.get_python_version() - self.statusBar.showMessage(version, 0) - self.setStatusBar(self.statusBar) - - root_dir = os.path.dirname(__file__) - utils._root_dir = root_dir - - self.init_toolbar_tab() - tb_home = PMToolBarHome() - self.add_toolbar('toolbar_home', tb_home, text=tb_home.get_toolbar_text()) - self.setDockNestingEnabled(True) - self.setWindowTitle('PyMiner') - - self.log_output_console = LogOutputConsole(self) - - self.add_widget_on_dock( - 'log_output_console', - self.log_output_console, - text=self.tr('Logs'), - side='right') - - # 初始化日志 - self.slot_flush_console('info', 'system', self.tr('Welcome to PyMiner')) - - self.extensions_manager = extensions_manager - self.extensions_manager.load_from_extension_folder(updateSplashMsg) - - self.ext_manager_widget = PMPageExt(self) - dw = self.add_widget_on_dock( - 'extension_panel', - self.ext_manager_widget, - text=self.tr('Plugs'), - side='left') - dw.setMaximumWidth(400) - - load_theme(settings['theme']) # 组件都加载后再设置主题,否则可能有些组件不生效 - self.show() - self.load_layout() - self.switch_toolbar('toolbar_home') # 启动完成时,将工具栏切换到‘主页’ - - self.on_main_window_shown() - # self.first_form_display() - - self.start_pmlocalserver() # 只要在插件加载完成之后启动就行,目前放在最后 - self.update_tip_client = UpdateTipClient() # 启动程序,检查更新,弹窗提醒 - - t01 = time.time() - logging.debug('Time Elapsed for loading main window contents: %f' % (t01 - t00)) - - def start_pmlocalserver(self): - """ - 启动本地flask服务器pmlocalserver - Returns:None - - """ - server.server_thread.start() - - def clear_workspace(self): - from features.extensions.extensionlib.extension_lib import extension_lib - extension_lib.get_interface('ipython_console').run_command('Clearing_Variables_ =\'Clear All\'', - hint_text=self.tr('Start Clear...'), hidden=False) - extension_lib.get_interface('ipython_console').run_command('get_ipython().clear_all()', - hint_text=self.tr('Clear all variables'), - hidden=False) - - def add_toolbar(self, name: str, toolbar: PMGToolBar, - text: str = 'untitled toolbar'): - """ - 添加一个工具栏。 - """ - if toolbar.insert_after() == '': - b = self.top_toolbar_tab.add_button(name, text) - else: - b = self.top_toolbar_tab.insert_button(name, text, toolbar.insert_after()) - toolbar.tab_button = b - b.clicked.connect(lambda: self.on_toolbar_switch_button_clicked(name)) - - if hasattr(self, 'toolbar_path'): - self.insertToolBar(self.toolbar_path, toolbar) - self.insertToolBarBreak(self.toolbar_path) - else: - self.addToolBarBreak(Qt.TopToolBarArea) - self.addToolBar(toolbar) - toolbar.setObjectName(name) - self.toolbars[name] = toolbar - toolbar.setMovable(False) - toolbar.setFloatable(False) - - if self._current_toolbar_name != '': - self.refresh_toolbar_appearance() - - # def insert_toolbar(self, name: str, toolbar: QToolBar, text: str, insert_after: str): - # """ - # 插入一个工具栏。 - # """ - # b = self.top_toolbar_tab.add_button(text) - # toolbar.tab_button = b - # b.clicked.connect(lambda: self.on_toolbar_switch_button_clicked(name)) - # - # if hasattr(self, 'toolbar_path'): - # self.insertToolBar(self.toolbar_path, toolbar) - # self.insertToolBarBreak(self.toolbar_path) - # else: - # self.addToolBarBreak(Qt.TopToolBarArea) - # self.addToolBar(toolbar) - # toolbar.setObjectName(name) - # self.toolbars[name] = toolbar - # toolbar.setMovable(False) - # toolbar.setFloatable(False) - # - # if self._current_toolbar_name != '': - # self.refresh_toolbar_appearance() - - def moveEvent(self, a0: 'QMoveEvent') -> None: - self.window_geometry_changed_signal.emit() - - def resizeEvent(self, a0: QResizeEvent) -> None: - """ - 窗口大小调节,或者位置改变的信号。 - Window size adjustment, or a signal of a change in position. - """ - self.size_restriction_acquire() - super().resizeEvent(a0) - self.delayed_call(500, self.size_restriction_release) - self.window_geometry_changed_signal.emit() - - def on_settings_changed(self): - load_theme(Settings.get_instance()['theme']) - - def delayed_call(self, time_ms: int, callback: Callable) -> None: - """ - 封装了QTimer.SingleShot - :param time_ms: - :param callback: - :return: - """ - timer = QTimer() - timer.singleShot(time_ms, callback) - - def size_restriction_acquire(self) -> None: - """ - 设置插件尺寸的最大值。 - 控件需要指定get_split_portion_hint才可以。 - :return: - """ - for k in self.dock_widgets.keys(): - dw = self.dock_widgets[k] - horizontal_portion_hint = dw.widget().get_split_portion_hint()[0] - if horizontal_portion_hint is not None: - dw.setMaximumWidth(int(self.width() * horizontal_portion_hint)) - dw.setMinimumWidth(int(self.width() * horizontal_portion_hint)) - - def size_restriction_release(self): - for w_name in self.dock_widgets.keys(): - self.dock_widgets[w_name].setMaximumWidth(100000) - self.dock_widgets[w_name].setMaximumHeight(100000) - self.dock_widgets[w_name].setMinimumHeight(0) - self.dock_widgets[w_name].setMinimumWidth(0) - - def on_main_window_shown(self): - """ - 在界面显示后触发的事件。 - Returns: None - """ - t0 = time.time() - super().on_main_window_shown() - - self.layouts_ready_signal.emit() - for task in self.setupui_tasks: - task() - self.widgets_ready_signal.emit() - t1 = time.time() - logging.info('Layout ready time elapsed:%f' % (t1 - t0)) - self.set_dock_titlebar_visible(Settings.get_instance()['dock_titlebar_visible']) - self.bind_events() - self.events_ready_signal.emit() - t2 = time.time() - logging.info('Events ready, time elapsed:%f' % (t2 - t1)) - - def first_form_display(self): - """ - 显示"快速操作"窗口 - Displays the "Quick Action" window - """ - self.main_first_form = base.FirstForm(parent=self) - self.main_first_form.show() - - def login_form_display(self): - """ - 显示"登录"窗口 - Displays the "Quick Action" window - """ - shared_memo = shared_memory.SharedMemory(name="sharedMemory") # 通过name找到共享内存token - buff = shared_memo.buf - token = bytes(buff[:199]).decode().replace("\x00", "") - if token != "": - self.main_login_form = base.LoginedForm(parent=self) - self.main_login_form.exec_() - else: - self.main_login_form = base.LoginForm(parent=self) - self.main_login_form.exec_() - - def main_appstore_dispaly(self): - """ - 显示"应用商店"窗口 - Displays the "App Store" window - """ - self.appstore = MarketplaceForm() - self.appstore.show() - - def main_option_display(self): - """ - 显示"选项"窗口 - """ - if self.main_option_form is None: - self.main_option_form = base.OptionForm() - self.main_option_form.exec_() - - def main_help_display(self): - """ - 打开帮助页面 - """ - utils.open_url("https://gitee.com/py2cn/pyminer/wikis") - - def main_check_update_display(self): - """ - 打开'检查更新'页面 - """ - perform_update() - - def main_install_update(self): - closed = self.close() - if closed: - from pmgwidgets import run_python_file_in_terminal - path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'update', 'update.py') - run_python_file_in_terminal(path + ' -i') - - def main_feedback_display(self): - """ - 打开'反馈'页面 - """ - FeedbackClient() - # reply = QMessageBox.information(self, self.tr('Feedback'), self.tr( - # 'You can give feedback through issue on suggestions or problems encountered in use'), - # QMessageBox.Yes | QMessageBox.No, - # QMessageBox.Yes) - # if reply == QMessageBox.Yes: - # utils.open_url("https://gitee.com/py2cn/pyminer/issues") - - def main_homesite_display(self): - """ - 打开官方网站页面 - """ - utils.open_url("http://www.pyminer.com") - - def main_markdown_display(self): - print("TODO 添加markdown编辑器代码") - - def main_new_script_display(self): - from features.extensions.extensionlib.extension_lib import extension_lib - extension_lib.get_interface('code_editor').open_script('') - - def main_community_display(self): - """ - 打开帮助页面 - """ - utils.open_url("https://www.kuxai.com/") - - def main_project_wizard_display(self): - """ - 打开新建项目向导 - """ - self.wizard = project_wizard = base.ProjectWizardForm(parent=self) - project_wizard.exec_() - - def main_about_display(self): - """ - 打开关于页面,并将当前操作系统信息写入页面 - """ - self.about_me = base.AboutForm() - self.about_me.show() - - def closeEvent(self, a0: QCloseEvent) -> None: - """ - 主窗体退出时的事件,包括弹框提示等。Mac 上测试点击无法退出,修改为QMessageBox.Warning - """ - reply = QMessageBox(QMessageBox.Warning, self.tr('Close'), self.tr('Are you sure close?')) - reply.addButton(self.tr('OK'), QMessageBox.ActionRole) - reply.addButton(self.tr('Cancel'), QMessageBox.RejectRole) - if reply.exec_() == QMessageBox.RejectRole: - a0.ignore() - return - else: - a0.accept() - self.delete_temporary_dock_windows() - self.save_layout() # TODO:PySide2上存储布局有问题。 - Settings.get_instance().save() - self.close_signal.emit() - self.extensions_manager.stop() - for k in self.dock_widgets.keys(): - self.dock_widgets[k].widget().closeEvent(a0) - super().closeEvent(a0) - - def slot_flush_console(self, level: str, module, content): - """刷新主窗体执行情况日志 - - Args: - level: 报错级别,包括 ``info`` , ``warnning`` , ``error`` 。 - module: 业务模块名称,例如 数据获取,数据处理,数据探索,统计,模型,可视化,评估 - content: 具体显示的内容 - """ - create_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 日志记录时间 - user = getpass.getuser() - msg = create_time + ' ' + user + ' ' + level.upper() + ' [' + module + ']' + ':' + content - if level == "error": - html = "" + msg + "" - else: - html = "" + msg + "" - - console = self.log_output_console # 由于代码重构,这里出现了不同。 - console.moveCursor(QTextCursor.End) - console.append(html) diff --git a/pmgwidgets/utilities/platform/fileutils.py b/pmgwidgets/utilities/platform/fileutils.py index 757a564575af47350f7fc19d17f44707257d95ba..f681e9dd412e92818792d333d97f6bc50315e29d 100644 --- a/pmgwidgets/utilities/platform/fileutils.py +++ b/pmgwidgets/utilities/platform/fileutils.py @@ -1,4 +1,5 @@ import os + import chardet @@ -129,9 +130,5 @@ def copy_paste(source_path: str, target_path: str): if __name__ == '__main__': - # move_to_trash('C:/Users/12957/Desktop/1.jpg') - create_file_if_not_exist(r'c:/users/12957/Desktop/rwrweew/ddddd/ggwewe/aaaaaa/as.a') - create_file_if_not_exist(r'c:/users/12957/Desktop/rwrweew/ddddd/ggwewe/aaaaaa/aa.s') - print(get_parent_path(r'c:/users/12957/desktop', 2)) - print(load_json( - r'/pyminer2/extensions/packages/code_editor/customized/settings.json')) + print(get_parent_path(os.path.dirname(__file__), 2)) + print(load_json(r'/pyminer2/extensions/packages/code_editor/customized/settings.json')) diff --git a/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.py b/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.py new file mode 100644 index 0000000000000000000000000000000000000000..f29581fd40a7539d9989e9cadd83cff8443c803c --- /dev/null +++ b/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.py @@ -0,0 +1,97 @@ +import os + +from PySide2 import QtCore +from PySide2.QtCore import QItemSelectionModel +from PySide2.QtWidgets import QApplication, QDialog +from .Ui_ConsoleHistoryDialog import Ui_ConsoleHistoryDialog + + +class ConsoleHistoryDialog(QDialog, Ui_ConsoleHistoryDialog): + """ + Class implementing the shell history dialog. + """ + + def __init__(self, console): + super().__init__() + self.setupUi(self) + self.__console = console + + self.deleteButton.clicked.connect(self.on_deleteButton_clicked) + self.copyButton.clicked.connect(self.on_copyButton_clicked) + self.reloadButton.clicked.connect(self.on_reloadButton_clicked) + self.historyList.itemSelectionChanged.connect( + self.on_historyList_itemSelectionChanged) + + self.reloadButton.click() + + @QtCore.Slot(QtCore.QModelIndex) + def select(self, item): + print(item.data()) + + @QtCore.Slot() + def on_historyList_itemSelectionChanged(self): + """ + Private slot to handle a change of the selection. + """ + selected = len(self.historyList.selectedItems()) > 0 + self.deleteButton.setEnabled(selected) + self.copyButton.setEnabled(selected) + self.executeButton.setEnabled(selected) + + @QtCore.Slot() + def on_deleteButton_clicked(self): + """ + Private slot to delete the selected entries from the history. + """ + for itm in self.historyList.selectedItems(): + ditm = self.historyList.takeItem(self.historyList.row(itm)) + del ditm + self.historyList.scrollToItem(self.historyList.currentItem()) + self.historyList.setFocus() + + @QtCore.Slot() + def on_copyButton_clicked(self): + cmds = self.selected_cmds() + QApplication.clipboard().setText(cmds) + + @QtCore.Slot() + def on_executeButton_clicked(self): + cmds = self.selected_cmds() + self.__console.hint_command(cmds) + self.__console.do_execute(cmds, True, '') + # reload the list because shell modified it + self.on_reloadButton_clicked() + + @QtCore.Slot() + def on_reloadButton_clicked(self): + """ + Private slot to reload the history. + """ + history = self.__console.history_tail(0) + + self.historyList.clear() + self.historyList.addItems(history) + self.historyList.setCurrentRow( + self.historyList.count() - 1, + QItemSelectionModel.SelectionFlag.Select) + + self.historyList.scrollToItem(self.historyList.currentItem()) + + @QtCore.Slot(QtCore.QModelIndex) + def on_historyList_doubleClicked(self, item): + self.on_executeButton_clicked() + + def get_history(self): + history = [] + for index in range(self.historyList.count()): + history.append(self.historyList.item(index).text()) + return history + + def selected_cmds(self): + lines = [] + for index in range(self.historyList.count()): + # selectedItems() doesn't seem to preserve the order + itm = self.historyList.item(index) + if itm.isSelected(): + lines.append(itm.text()) + return (os.linesep.join(lines) + os.linesep).rstrip(os.linesep) diff --git a/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.ui b/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..59fb25071e01b00c6c25df2d5d9fc3a43f153b58 --- /dev/null +++ b/pmgwidgets/widgets/basic/others/ConsoleHistoryDialog.ui @@ -0,0 +1,158 @@ + + ShellHistoryDialog + + + + 0 + 0 + 540 + 506 + + + + Shell History + + + true + + + + + + + Monospace + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + true + + + + + + + + + false + + + Delete the selected entries + + + &Delete + + + + + + + false + + + Copy the selected entries to the current editor + + + C&opy + + + + + + + false + + + Execute the selected entries + + + &Execute + + + + + + + Reload the history + + + &Reload + + + + + + + Qt::Vertical + + + + 72 + 208 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + historyList + deleteButton + copyButton + executeButton + reloadButton + buttonBox + + + + + buttonBox + accepted() + ShellHistoryDialog + accept() + + + 333 + 487 + + + 323 + 505 + + + + + buttonBox + rejected() + ShellHistoryDialog + reject() + + + 167 + 490 + + + 169 + 504 + + + + + diff --git a/pmgwidgets/widgets/basic/others/Ui_ConsoleHistoryDialog.py b/pmgwidgets/widgets/basic/others/Ui_ConsoleHistoryDialog.py new file mode 100644 index 0000000000000000000000000000000000000000..e555d7aeaa3f82bbcf87ef1b1d224832f118e046 --- /dev/null +++ b/pmgwidgets/widgets/basic/others/Ui_ConsoleHistoryDialog.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- + +################################################################################ +# Form generated from reading UI file 'ConsoleHistoryDialog.ui' +## +# Created by: Qt User Interface Compiler version 6.1.0 +## +# WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide2.QtCore import * +from PySide2.QtGui import * +from PySide2.QtWidgets import * + + +class Ui_ConsoleHistoryDialog(object): + def setupUi(self, ConsoleHistoryDialog): + if not ConsoleHistoryDialog.objectName(): + ConsoleHistoryDialog.setObjectName(u"ConsoleHistoryDialog") + ConsoleHistoryDialog.resize(540, 506) + ConsoleHistoryDialog.setSizeGripEnabled(True) + self.gridLayout = QGridLayout(ConsoleHistoryDialog) + self.gridLayout.setObjectName(u"gridLayout") + self.historyList = QListWidget(ConsoleHistoryDialog) + self.historyList.setObjectName(u"historyList") + font = QFont() + font.setFamilies([u"Monospace"]) + self.historyList.setFont(font) + self.historyList.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.historyList.setAlternatingRowColors(True) + self.historyList.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.historyList.setWordWrap(True) + + self.gridLayout.addWidget(self.historyList, 0, 0, 1, 1) + + self.verticalLayout = QVBoxLayout() + self.verticalLayout.setObjectName(u"verticalLayout") + self.deleteButton = QPushButton(ConsoleHistoryDialog) + self.deleteButton.setObjectName(u"deleteButton") + self.deleteButton.setEnabled(False) + + self.verticalLayout.addWidget(self.deleteButton) + + self.copyButton = QPushButton(ConsoleHistoryDialog) + self.copyButton.setObjectName(u"copyButton") + self.copyButton.setEnabled(False) + + self.verticalLayout.addWidget(self.copyButton) + + self.executeButton = QPushButton(ConsoleHistoryDialog) + self.executeButton.setObjectName(u"executeButton") + self.executeButton.setEnabled(False) + + self.verticalLayout.addWidget(self.executeButton) + + self.reloadButton = QPushButton(ConsoleHistoryDialog) + self.reloadButton.setObjectName(u"reloadButton") + + self.verticalLayout.addWidget(self.reloadButton) + + self.verticalSpacer = QSpacerItem( + 72, 208, QSizePolicy.Minimum, QSizePolicy.Expanding) + + self.verticalLayout.addItem(self.verticalSpacer) + + self.gridLayout.addLayout(self.verticalLayout, 0, 1, 1, 1) + + self.buttonBox = QDialogButtonBox(ConsoleHistoryDialog) + self.buttonBox.setObjectName(u"buttonBox") + self.buttonBox.setStandardButtons( + QDialogButtonBox.Cancel | QDialogButtonBox.Ok) + + self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 2) + + QWidget.setTabOrder(self.historyList, self.deleteButton) + QWidget.setTabOrder(self.deleteButton, self.copyButton) + QWidget.setTabOrder(self.copyButton, self.executeButton) + QWidget.setTabOrder(self.executeButton, self.reloadButton) + QWidget.setTabOrder(self.reloadButton, self.buttonBox) + + self.retranslateUi(ConsoleHistoryDialog) + self.buttonBox.accepted.connect(ConsoleHistoryDialog.accept) + self.buttonBox.rejected.connect(ConsoleHistoryDialog.reject) + + QMetaObject.connectSlotsByName(ConsoleHistoryDialog) + # setupUi + + def retranslateUi(self, ConsoleHistoryDialog): + ConsoleHistoryDialog.setWindowTitle(QCoreApplication.translate( + "ConsoleHistoryDialog", u"Shell History", None)) +# if QT_CONFIG(tooltip) + self.deleteButton.setToolTip(QCoreApplication.translate( + "ConsoleHistoryDialog", u"Delete the selected entries", None)) +#endif // QT_CONFIG(tooltip) + self.deleteButton.setText(QCoreApplication.translate( + "ConsoleHistoryDialog", u"&Delete", None)) +# if QT_CONFIG(tooltip) + self.copyButton.setToolTip(QCoreApplication.translate( + "ConsoleHistoryDialog", u"Copy the selected entries to the current editor", None)) +#endif // QT_CONFIG(tooltip) + self.copyButton.setText(QCoreApplication.translate( + "ConsoleHistoryDialog", u"C&opy", None)) +# if QT_CONFIG(tooltip) + self.executeButton.setToolTip(QCoreApplication.translate( + "ConsoleHistoryDialog", u"Execute the selected entries", None)) +#endif // QT_CONFIG(tooltip) + self.executeButton.setText(QCoreApplication.translate( + "ConsoleHistoryDialog", u"&Execute", None)) +# if QT_CONFIG(tooltip) + self.reloadButton.setToolTip(QCoreApplication.translate( + "ConsoleHistoryDialog", u"Reload the history", None)) +#endif // QT_CONFIG(tooltip) + self.reloadButton.setText(QCoreApplication.translate( + "ConsoleHistoryDialog", u"&Reload", None)) + # retranslateUi diff --git a/pmgwidgets/widgets/basic/others/console.py b/pmgwidgets/widgets/basic/others/console.py index 8cb374e41cc8b055d2d36acf061de410ae260c70..591f5e908683aadd7407b4d60942c49119787636 100644 --- a/pmgwidgets/widgets/basic/others/console.py +++ b/pmgwidgets/widgets/basic/others/console.py @@ -8,18 +8,21 @@ Created on 2020/8/24 @file: console.py @description: Console Widget """ - +import logging import os +import sys +import json from typing import Tuple, Dict, Callable from PySide2.QtCore import QObject, Signal, QThread, QWaitCondition, QMutex, QPoint, QCoreApplication, QTranslator, \ QLocale from PySide2.QtGui import QTextCursor -from PySide2.QtWidgets import QMessageBox, QMenu, QApplication +from PySide2.QtWidgets import QMessageBox, QMenu, QApplication, QDialog from qtconsole import styles from qtconsole.manager import QtKernelManager from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.styles import default_light_syntax_style, default_light_style_sheet +from features.io import settings default_dark_style_template = styles.default_template + """\ .in-prompt { color: #ff00ff; } @@ -29,6 +32,10 @@ default_dark_style_sheet = default_dark_style_template % dict( bgcolor='#19232d', fgcolor='white', select="#ccc") default_dark_syntax_style = 'monokai' # 'default' +logging.basicConfig(stream=sys.stderr) +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + class ConsoleInitThread(QObject): initialized = Signal(object, object) @@ -40,7 +47,12 @@ class ConsoleInitThread(QObject): def run(self): self.mutex.lock() - kernel_manager = QtKernelManager(kernel_name='python3') + kernel_manager = QtKernelManager(kernel_name="python3") + logger.debug("installed kernel start method") + + # kernel_manager.kernel_spec.argv[0] = r"C:\Users\12957\AppData\Local\Programs\Python\Python38\python.exe" + kernel_manager.kernel_spec.argv[0] = sys.executable + # 这一步的目的是,指明要连接到的解释器。 kernel_manager.start_kernel() kernel_client = kernel_manager.client() @@ -68,6 +80,8 @@ class PMGIpythonConsole(RichJupyterWidget): self.is_first_execution = True self.confirm_restart = False + self.history_path = os.path.join(settings.get_pyminer_data_path(), 'console_history.json') + self.history = [] self.commands_pool = [] self.command_callback_pool: Dict[str, Callable] = {} @@ -116,6 +130,7 @@ class PMGIpythonConsole(RichJupyterWidget): self.init_thread.finished.connect(self.init_thread.deleteLater) self.init_thread.started.connect(self.console_object.run) self.init_thread.start() + logger.debug("setup--ui") cursor: QTextCursor = self._prompt_cursor cursor.movePosition(QTextCursor.End) @@ -133,16 +148,33 @@ class PMGIpythonConsole(RichJupyterWidget): trans = trans_dic.get(action.text()) trans = trans if trans is not None else action.text() action.setText(trans) - restart_action = menu.addAction(_translate("PMGIpythonConsole", 'Restart')) + history_action = menu.addAction( + _translate("PMGIpythonConsole", 'History')) + history_action.triggered.connect(self.show_history) + + restart_action = menu.addAction( + _translate("PMGIpythonConsole", 'Restart')) restart_action.triggered.connect(self.slot_restart_kernel) - stop_action = menu.addAction(_translate("PMGIpythonConsole", 'Interrupt')) + stop_action = menu.addAction( + _translate("PMGIpythonConsole", 'Interrupt')) # stop_action.triggered.connect(self.request_interrupt_kernel) stop_action.triggered.connect(self.on_interrupt_kernel) # stop_action.setEnabled(self._executing) return menu + def show_history(self): + """ + Public slot to show the shell history dialog. + """ + from .ConsoleHistoryDialog import ConsoleHistoryDialog + # import readline + dlg = ConsoleHistoryDialog(self) + if dlg.exec() == QDialog.DialogCode.Accepted: + self.history = dlg.get_history() + super()._set_history(self.history) + def on_interrupt_kernel(self): """ 当点击中断执行时。 @@ -200,6 +232,8 @@ class PMGIpythonConsole(RichJupyterWidget): return 'Welcome To PMGWidgets Ipython Console!\n' def closeEvent(self, event): + self.history = self.history_tail(0) + self.save_history() if self.init_thread.isRunning(): self.console_object.stop() self.init_thread.quit() @@ -224,6 +258,14 @@ class PMGIpythonConsole(RichJupyterWidget): :param hint_text: 运行代码前显示的提示 :return: str 执行命令的 msgid """ + self.hint_command(hint_text) + if self.kernel_client is None: + self.commands_pool.append((source, hidden, hint_text)) + return '' + else: + return self.pmexecute(source, hidden) + + def hint_command(self, hint_text: str = '') -> str: cursor: QTextCursor = self._prompt_cursor cursor.movePosition(QTextCursor.End) # 运行文件时,显示文件名,无换行符,执行选中内容时,包含换行符 @@ -237,13 +279,9 @@ class PMGIpythonConsole(RichJupyterWidget): # 删除多余的continuation_prompt self.undo() - self._finalize_input_request() # display input string buffer in console. + # display input string buffer in console. + self._finalize_input_request() cursor.movePosition(QTextCursor.End) - if self.kernel_client is None: - self.commands_pool.append((source, hidden, hint_text)) - return '' - else: - return self.pmexecute(source, hidden) def _handle_stream(self, msg): parent_header = msg.get('parent_header') @@ -304,6 +342,22 @@ class PMGIpythonConsole(RichJupyterWidget): directory=os.path.join(os.path.dirname(__file__), 'translations')) app.installTranslator(_trans) + def _set_history(self, history): + """ 重写的方法。重新管理历史记录。 + """ + self.load_history() + super()._set_history(self.history) + + def load_history(self): + if os.path.exists(self.history_path): + with open(self.history_path) as f: + history = json.load(f) + self.history = history['history'] + + def save_history(self): + history = {'history': self.history} + with open(self.history_path, 'w') as f: + json.dump(history, f) if __name__ == '__main__': import sys @@ -311,7 +365,7 @@ if __name__ == '__main__': cgitb.enable(format='text') app = QApplication([]) - os.environ['IPYTHON_AS_PYMINER_NODE'] = '1' + # os.environ['IPYTHON_AS_PYMINER_NODE'] = '1' PMGIpythonConsole.install_translator() w = PMGIpythonConsole() w.show() diff --git a/pmgwidgets/widgets/composited/fastui.py b/pmgwidgets/widgets/composited/fastui.py index 6d2df6b05db6940f943ff0025e88cf1181ed345c..6f3a80a77e0c30f118f4cac1baa3f18190329ad7 100644 --- a/pmgwidgets/widgets/composited/fastui.py +++ b/pmgwidgets/widgets/composited/fastui.py @@ -241,15 +241,16 @@ class FunctionGUIDialog(DFOperationDialog): if arg["optional"]: optional_names.append(arg["name"]) views.append(("check_ctrl", arg["name"] + "#enable", "", True)) + arg["ctrl"]["name"] = arg["name"] views.append(arg["ctrl"]) self.panel.set_items(views) - for op_name in optional_names: self.panel.set_as_controller(op_name + "#enable", [op_name], True, ) def get_value_code(self) -> str: values = self.panel.get_value_with_filter() # 只获取使能并且可见的控件的值 + print(values,self.panel.widgets_dic) values = {k: v for k, v in values.items() if k.isidentifier()} varname = self.combo_box.currentText() args_str = '' @@ -261,7 +262,7 @@ class FunctionGUIDialog(DFOperationDialog): code = '{var_name}.{method_name}({args})'.format(var_name=varname, method_name=self.func_name, args=args_str) - + print(code,values) return code diff --git a/pmgwidgets/widgets/extended/entries/funcctrl.py b/pmgwidgets/widgets/extended/entries/funcctrl.py new file mode 100644 index 0000000000000000000000000000000000000000..a3820c22b51d4971476b06eb174de68355b0a68f --- /dev/null +++ b/pmgwidgets/widgets/extended/entries/funcctrl.py @@ -0,0 +1,101 @@ +import ast +import astunparse +from typing import Any, Tuple, List + +from PySide2.QtWidgets import QLineEdit, QLabel, QHBoxLayout, QPushButton, QMessageBox + +from pmgwidgets.widgets.extended.base.baseextendedwidget import BaseExtendedWidget + + +class CodeVisitor(ast.NodeVisitor): + def __init__(self): + super(CodeVisitor, self).__init__() + self.preserved = {"pi", "e"} + self.called = set() + self.func_args = set() + self._names = set() + + def visit_Name(self, node: ast.Name) -> Any: + self._names.add(node.id) + + + def visit_Call(self, node: ast.Call) -> Any: + self.generic_visit(node) + self.called.add(node.func.id) + + def get_result(self) -> Tuple[List[str], List[str]]: + """ + + Returns: 定义的名称,以及调用的ID名称。 + + """ + names = self._names.copy() + names.difference_update(self.preserved) + names.difference_update(self.called) + return list(names), list(self.called) + + +# n = ast.parse("x*cos(v,sin(2*pi*x))") +# print(CodeVisitor().visit(n)) +# print(cv := CodeVisitor()) +# cv.visit(n) +# print(cv.get_result()) +# # # print(ast.get_source_segment("x*cos(v,sin(2*pi*x))",n)) +# # print(ast.dump(n)) +# # print(astunparse.unparse(n)) + + +class PMGFuncCtrl(BaseExtendedWidget): + """ + + 输入:一个有效的函数表达式。 + 其中,里面的变量名会自动进行检测。 + + """ + + def __init__(self, layout_dir: str, title: str, initial_value: str): + super().__init__(layout_dir) + self.allowed_chars = set(' ,[](){}:1234567890.+-*/') + self.on_check_callback = None + self.prefix = QLabel(text=title) + self.type = type + entryLayout = QHBoxLayout() + self.ctrl = QLineEdit() + self.central_layout.addWidget(self.prefix) + self.central_layout.addLayout(entryLayout) + entryLayout.addWidget(self.ctrl) + self.set_value(initial_value) + + def get_code(self) -> str: + text = self.ctrl.text() + if self.type == 'safe': + for char in text: + if char not in self.allowed_chars: + return None + return text + + def set_value(self, obj: Any): + try: + self.ctrl.setText(repr(obj)) + except: + import traceback + traceback.print_exc() + + def get_value(self) -> object: + if self.get_code() is not None: + try: + return eval(self.ctrl.text()) + except: + import traceback + traceback.print_exc() + return None + else: + return None + + def on_eval_test(self): + """ + 点击计算按钮,弹出对话框显示计算结果。 + :return: + """ + val = self.get_value() + QMessageBox.information(self, self.tr('Result'), repr(val), QMessageBox.Ok) diff --git a/pmgwidgets/widgets/extended/others/multitypeparaminput.py b/pmgwidgets/widgets/extended/others/multitypeparaminput.py index fb60d0cf00fbc307c34312e350569a508f4d0e20..65f20666025da1d0ab1b450edf5b505768125939 100644 --- a/pmgwidgets/widgets/extended/others/multitypeparaminput.py +++ b/pmgwidgets/widgets/extended/others/multitypeparaminput.py @@ -35,7 +35,7 @@ class PMGMultiTypeCtrl(BaseExtendedWidget): index = self.get_type_index() self.sub_panel.set_items([self.ctrls[index]["ctrls"]]) - print(self.get_value()) + # print(self.get_value()) # self.setFixedSize(0) def on_param_changed(self, event): diff --git a/pmlocalserver/server.py b/pmlocalserver/server.py index 80e2d11a18462b9bd5d77eabe54cb70a5f4d3cb8..745325d7dc6f4aecde3bd60b6d987d2883f7dd47 100644 --- a/pmlocalserver/server.py +++ b/pmlocalserver/server.py @@ -1,6 +1,8 @@ -from flask import Flask +import threading +from flask import Flask from flask_cors import CORS + import threading from multiprocessing import shared_memory diff --git a/pyminer_comm/base/datadesc.py b/pyminer_comm/base/datadesc.py index 19cdf9967517202228b6786d662a961c02f1e730..7faa7753a6da4634be5347e304c03b85a0d37ed8 100644 --- a/pyminer_comm/base/datadesc.py +++ b/pyminer_comm/base/datadesc.py @@ -33,24 +33,34 @@ class NoPreviewError(Exception): def is_big_variable(var): - import pandas as pd - import numpy as np + """ + 判断是否为大型变量。 + 对于较大的变量,需要采用预览模式。 + Args: + var: + + Returns: + """ if isinstance(var, (int, bool, float)): return sys.getsizeof(var) > DataDesc.threshold_pandas - if isinstance(var, str): + elif isinstance(var, str): return len(var) > DataDesc.max_str_len - elif isinstance(var, (pd.DataFrame, pd.Series)): + elif isinstance(var, dict): + return len(list(var.keys())) > DataDesc.max_len + elif isinstance(var, (list, tuple)): + return len(var) > DataDesc.max_len + + import pandas as pd + import numpy as np + if isinstance(var, (pd.DataFrame, pd.Series)): if isinstance(var, pd.DataFrame): return int(var.memory_usage().sum()) - DataDesc.threshold_pandas > 0 else: return var.memory_usage() - DataDesc.threshold_pandas > 0 elif isinstance(var, np.ndarray): return var.nbytes > DataDesc.threshold_numpy - elif isinstance(var, dict): - return len(list(var.keys())) > DataDesc.max_len - elif isinstance(var, (list, tuple)): - return len(var) > DataDesc.max_len + else: return False diff --git a/pyminer_comm/pyminer_client/pm_client.py b/pyminer_comm/pyminer_client/pm_client.py index 12c259dc8308e0267206ebcc7897907529ddbef6..a16f941b36265f36fdee97f649e641c38117c8d4 100644 --- a/pyminer_comm/pyminer_client/pm_client.py +++ b/pyminer_comm/pyminer_client/pm_client.py @@ -21,7 +21,7 @@ def modify_settings(items: Dict[str, Any]): """ assert isinstance(items, dict), items res = get('modify_settings', dict_to_b64(items, protocol=get_protocol()), 12306, ) - + return res def set_data_desc_dic(data: Dict[str, Any]) -> None: for k, v in data.items(): diff --git a/requirements.txt b/requirements.txt index 6f0eae7e0526862efe32a418e8b3239970d0d772..45dd7f3790da85cc71eaf037ff5fb6c641db9c19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ jdcal>=1.4.1 joblib>=0.16.0 kiwisolver>=1.2.0 numpy==1.19.3 -matplotlib>=3.3.1 +matplotlib>=3.4.1 openpyxl>=3.0.4 xlrd>=1.2.0 pandas>=1.1.1 @@ -57,5 +57,6 @@ cx_Oracle>=8.1.0 pyminer_comm>=0.7.1 ipyparams pathspec +codegen diff --git a/requirements_linux.txt b/requirements_linux.txt index 0ebaca1065fa0b6621650af321ac7cceff31118c..aa3b7350fb4e504f406f8456907dd3bbd6828e24 100644 --- a/requirements_linux.txt +++ b/requirements_linux.txt @@ -6,7 +6,7 @@ helpdev>=0.7.1 jdcal>=1.4.1 joblib>=0.16.0 kiwisolver>=1.2.0 -matplotlib>=3.3.1 +matplotlib>=3.4.1 numpy==1.19.1 openpyxl>=3.0.4 pandas>=1.1.1 diff --git a/requirements_mac.txt b/requirements_mac.txt index fa5b0d707314f26163c21cc04eeca80553a70215..9c625abe4f30fe9af411ed1b0591aaaa3652ca3f 100644 --- a/requirements_mac.txt +++ b/requirements_mac.txt @@ -5,7 +5,7 @@ helpdev>=0.7.1 jdcal>=1.4.1 joblib>=0.16.0 kiwisolver>=1.2.0 -matplotlib>=3.3.1 +matplotlib>=3.4.1 numpy==1.19.1 openpyxl>=3.0.4 pandas>=1.1.1 diff --git a/resources/qss/Fusion.qss b/resources/qss/Fusion.qss index f4d798d8ffc0646ca7b202d914dd50b8f3e2f0ff..9b4ebb6ce36cb8d17f3fd113d20e06aaa2120a03 100644 --- a/resources/qss/Fusion.qss +++ b/resources/qss/Fusion.qss @@ -15,10 +15,10 @@ QDockWidget::title{ } QPushButton{ - background-color: MAIN_THEME; + background-color: #F7F7F7; } QPushButton:hover{ - background-color:MARGIN_THEME; + background-color:#DADADA; } QPushButton#pmtopToolbarButton[stat="unselected"]:hover{ @@ -29,7 +29,7 @@ QPushButton#pmtopToolbarButton[stat="selected"]{ margin: 5px 5px -5px 5px; min-width: 120px; border-radius:5px; - background-color: MAIN_THEME; + background-color: #F7F7F7; } QPushButton#pmtopToolbarButton[stat="unselected"]{ padding: 5px 0px 10px 0px; @@ -66,10 +66,10 @@ QToolButton{ border-radius:5px; } QToolButton:hover{ - background-color:MARGIN_THEME; + background-color:#DADADA; } QToolButton::checked{ - background-color:MARGIN_THEME; + background-color:#DADADA; } QToolButton#hidebutton { max-width: 15px; @@ -110,14 +110,14 @@ QToolButton#stacked_tool_button::menu-indicator:image{ /*以下几行设定的是滚动条的横竖向功能*/ QScrollBar::vertical { - background: MARGIN_THEME; + background: #DADADA; border: -0px solid grey; margin: 0px 0px 0px 0px; width: 10px; border-radius:5px; } QScrollBar::horizontal { - background: MARGIN_THEME; + background: #DADADA; border: -0px solid grey; margin: 0px 0px 0px 0px; height:10px; @@ -127,17 +127,17 @@ QScrollBar::horizontal { QScrollBar::handle:vertical{ border-radius:3px; min-height:30px; -background:MARGIN_THEME; +background:#DADADA; } QScrollBar::handle:horizontal{ border-radius:3px; min-width:30px; -background:MARGIN_THEME; +background:#DADADA; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: MAIN_THEME; + background: #F7F7F7; border:0px; } diff --git a/resources/qss/standard.qss b/resources/qss/standard.qss index b2e7ad2a86f030513bf0ada1cad540e3248bdf5b..cb94871684ae89f7df72beb11eff51352d9105ce 100644 --- a/resources/qss/standard.qss +++ b/resources/qss/standard.qss @@ -5,20 +5,20 @@ github链接: https://github.com/PyQt5/PyQt/blob/master/QScrollArea/Data/style.qss 文件加载时,将会自动替换MAIN_THEME和MARGIN_THEME为相应的颜色。 */ -QMainWindow{background-color:MAIN_THEME;} -QDialog{background-color:MAIN_THEME;} +QMainWindow{background-color:#F7F7F7;} +QDialog{background-color:#F7F7F7;} QAbstractScrollArea{ - border:1px solid MARGIN_THEME; + border:1px solid #DADADA; } TopToolBar { - border-bottom: 1px solid MARGIN_THEME; - border-top: 1px solid MARGIN_THEME; + border-bottom: 1px solid #DADADA; + border-top: 1px solid #DADADA; } TopToolBarRight { - border-bottom: 1px solid MARGIN_THEME; - border-top: 1px solid MARGIN_THEME; + border-bottom: 1px solid #DADADA; + border-top: 1px solid #DADADA; } QDockWidget { @@ -49,12 +49,12 @@ QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { } QPushButton{ - border:1px solid MARGIN_THEME; + border:1px solid #DADADA; border-radius:5px; height:20px; width:80px; } -QPushButton:hover{background-color:MARGIN_THEME;} +QPushButton:hover{background-color:#DADADA;} /*按钮停留态*/ QPushButton#pmtopToolbarButton[stat="selected"]{ /*背景颜色*/ @@ -62,7 +62,7 @@ QPushButton#pmtopToolbarButton[stat="selected"]{ padding:0px 0px 0px 0px; border:1px; border-radius:8px; - background-color:MARGIN_THEME; + background-color:#DADADA; } QPushButton#pmtopToolbarButton[stat="unselected"]{ @@ -71,7 +71,7 @@ QPushButton#pmtopToolbarButton[stat="unselected"]{ padding:0px 0px 0px 0px; border:1px; border-radius:8px; - background-color:MAIN_THEME; + background-color:#F7F7F7; } QPushButton#pmtopToolbarButton:hover{ @@ -86,7 +86,7 @@ QPushButton#pmtopToolbarButton:hover{ QPushButton:hover { /*背景颜色*/ - background-color:MARGIN_THEME; + background-color:#DADADA; } QToolBar{ height:100px; @@ -104,7 +104,7 @@ QToolButton{ QToolButton:hover { /*背景颜色*/ - background-color:MARGIN_THEME; + background-color:#DADADA; } /*toolbutton下三角居中靠下显示*/ QToolButton::menu-indicator:image{ @@ -115,14 +115,14 @@ QToolButton::menu-indicator:image{ QScrollBar::vertical { - background: MARGIN_THEME; + background: #DADADA; border: -0px solid grey; margin: 0px 0px 0px 0px; width: 10px; border-radius:5px; } QScrollBar::horizontal { - background: MARGIN_THEME; + background: #DADADA; margin: 0px 0px 0px 0px; height:10px; @@ -130,16 +130,16 @@ QScrollBar::horizontal { QScrollBar::handle:vertical{ border-radius:3px; -background:MARGIN_THEME; +background:#DADADA; } QScrollBar::handle:horizontal{ border-radius:3px; -background:MARGIN_THEME; +background:#DADADA; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: MAIN_THEME; + background: #F7F7F7; border:0px; } QMessageBox QPushButton[text="OK"] { diff --git a/resources/qss/windowsvista.qss b/resources/qss/windowsvista.qss index 5c87512ef931023fdaa025d7554a47b61dea10b5..dc95f29a72661d50de888ea57b12fee417dee2a0 100644 --- a/resources/qss/windowsvista.qss +++ b/resources/qss/windowsvista.qss @@ -53,7 +53,7 @@ QToolButton{ border-radius:5px; } QToolButton:hover{ - background-color:MARGIN_THEME; + background-color: #DADADA; } QToolButton#hidebutton { max-width: 15px; diff --git a/run_before_commit.py b/run_before_commit.py index 16b7e903a056cb0b0bf56c331c1660a5f4114483..7440b2e1a0f6b7d4024a864e61b8800be2516ce3 100644 --- a/run_before_commit.py +++ b/run_before_commit.py @@ -1,6 +1,16 @@ """ 这个文件需要在提交之前运行,从而正确的生成更新包。 +在更新之后,需要检查: +1、最新的版本启动后,是否还会提示要更新。如果是,那么说明可能文件存在错误,或者并未正确执行此文件。 +2、最新打包的版本,其中是否因为某些包的更新而发生错误。典型例子就是Matplotlib升级成3.4.1版本之后,废止了旧的某个类,导致PMAgg不能运行。 + +还有一些问题: +是否需要发布大版本更新的提示?比如,当版本更新中包含pip包版本升级的时候,是否需要提示用户? +如何处理用户升级解释器中包的问题?是否要提示,默认解释器不得安装requirements.txt中已有的包? + +是否需要支持切换解释器的工作空间? + """ import os @@ -9,4 +19,4 @@ import sys from utils import get_root_dir os.system(f"{sys.executable} {os.path.join(get_root_dir(), 'features', 'util', 'make_update.py')}") -print("已经运行完成,现在可以提交了。") +print("已经运行完成。现在还需要进行一次更新测试,确认已是最新版本,便可以提交到主分支了。") diff --git a/utils/__init__.py b/utils/__init__.py index fcb4d50b3614580f046dfb8fcdbcd9cfbea0da01..e70f3f750d54d570c6e0ec2f83899d1d0346d38b 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -3,17 +3,9 @@ # # 通用工具,可以在全局进行调用。 -import os -import datetime -import logging import webbrowser -from .path import ( - get_root_dir, - get_user_dir, - get_desktop_dir, - get_documents_dir, -) +from .debug import * from .environ import ( get_python_version, get_python_modules_directory, @@ -21,6 +13,17 @@ from .environ import ( getScriptsPath, getDesignerPath ) +from .io import * +from .path import ( + get_root_dir, + get_user_dir, + get_desktop_dir, + get_documents_dir, + get_pyminer_data_dir, + get_user_config_dir, + get_default_config_dir, + get_config_file_dir +) from .platform import ( is_windows_platform, is_mac_platform, @@ -28,23 +31,20 @@ from .platform import ( is_kde_desktop, is_gnome_desktop ) - -from .io import * -from .debug import * +from .settings import * from .ui import * -from typing import TYPE_CHECKING - if TYPE_CHECKING: - import pmgui -version = 'v2.1.0 Beta' + import app2 + from PySide2.QtWidgets import QApplication +# version = 'v2.1.0 Beta' 这里原有version ,但是考虑到这些信息写在静态文件中较好,所以就写在了文件中。 -_application = None +_application = Optional["QApplication"] _root_dir = None -_main_window: Optional["pmgui.MainWindow"] = None +_main_window: Optional["app2.MainWindow"] = None -def get_application() -> None: +def get_application() -> "QApplication": """ 获取QApplication Returns: @@ -54,7 +54,7 @@ def get_application() -> None: return _application -def get_main_window() -> Optional["pmgui.MainWindow"]: +def get_main_window() -> Optional["app2.MainWindow"]: """ 获取主窗口或者主控件。 Returns: @@ -67,8 +67,7 @@ def get_work_dir() -> 'str': 获取主窗口或者主控件。 Returns: """ - from features.io.settings import Settings - return Settings.get_instance()['work_dir'] + return get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") def open_url(url): @@ -80,47 +79,46 @@ def open_url(url): except Exception as e: webbrowser.open_new_tab(url) - -def unzip_file(zip_src: str, dst_dir: str): - """ - 解压文件 - Args: - zip_src: - dst_dir: - - Returns: - - """ - r = zipfile.is_zipfile(zip_src) - if r: - fz = zipfile.ZipFile(zip_src, 'r') - for file in fz.namelist(): - fz.extract(file, dst_dir) - else: - print('This is not zip') - - -def make_zip(src_path, zip_dist_path, root='', rules=None): - """ - 创建zip包 - Args: - src_path: - zip_dist_path: - root: - rules: - - Returns: - - """ - if rules is None: - rules = [] - z = zipfile.ZipFile(zip_dist_path, 'w', zipfile.ZIP_DEFLATED) - for dirpath, dirnames, filenames in os.walk(src_path): - relpath = os.path.relpath(dirpath, src_path) - if is_neglect_path(relpath, rules): - continue - fpath = os.path.relpath(dirpath, src_path) - for filename in filenames: - filepath = os.path.join(dirpath, filename) - z.write(filepath, os.path.join(root, fpath, filename)) - z.close() +# def unzip_file(zip_src: str, dst_dir: str): +# """ +# 解压文件 +# Args: +# zip_src: +# dst_dir: +# +# Returns: +# +# """ +# r = zipfile.is_zipfile(zip_src) +# if r: +# fz = zipfile.ZipFile(zip_src, 'r') +# for file in fz.namelist(): +# fz.extract(file, dst_dir) +# else: +# print('This is not zip') +# +# +# def make_zip(src_path, zip_dist_path, root='', rules=None): +# """ +# 创建zip包 +# Args: +# src_path: +# zip_dist_path: +# root: +# rules: +# +# Returns: +# +# """ +# if rules is None: +# rules = [] +# z = zipfile.ZipFile(zip_dist_path, 'w', zipfile.ZIP_DEFLATED) +# for dirpath, dirnames, filenames in os.walk(src_path): +# relpath = os.path.relpath(dirpath, src_path) +# if is_neglect_path(relpath, rules): +# continue +# fpath = os.path.relpath(dirpath, src_path) +# for filename in filenames: +# filepath = os.path.join(dirpath, filename) +# z.write(filepath, os.path.join(root, fpath, filename)) +# z.close() diff --git a/utils/path.py b/utils/path.py index dd93c32747998f2630c1b8ef4621f0750898a9c6..fc36a9629392bd27afbace3600a46a9eae2b431f 100644 --- a/utils/path.py +++ b/utils/path.py @@ -3,9 +3,12 @@ # Get application path information # 获取应用路径信息 import os -import sys +import shutil + from PySide2.QtCore import QStandardPaths +from .settings import SETTINGS_FILES + def get_root_dir() -> str: """ @@ -121,21 +124,71 @@ def get_path_generic_cache() -> str: return path -def get_path_generic_config() -> str: - path = QStandardPaths.writableLocation(QStandardPaths.GenericConfigLocation) +# def get_path_generic_config() -> str: +# path = QStandardPaths.writableLocation(QStandardPaths.GenericConfigLocation) +# return path + + +# def get_path_app_data() -> str: +# path = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation) +# return path + + +# def get_path_app_config() -> str: +# path = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation) +# return path + + +def get_path_app_local_data() -> str: + path = QStandardPaths.writableLocation(QStandardPaths.AppLocalDataLocation) return path -def get_path_app_data() -> str: - path = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation) +def get_pyminer_data_dir() -> str: + """ + 获取PyMiner的数据文件存储位置,一般为.pyminer文件夹。 + Returns: + + """ + path = os.path.join(os.path.expanduser('~'), '.pyminer') + if not os.path.exists(path): + os.makedirs(path) return path -def get_path_app_config() -> str: - path = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation) +def get_default_config_dir() -> str: + """ + 获取PyMiner默认设置的位置。软件第一次运行时,从这里获取设置,并且将设置拷贝到用户目录下面。。 + Returns: + + """ + return os.path.join(get_root_dir(), "configuration") + + +def get_user_config_dir() -> str: + """ + 获取用户的设置目录。 .pyminer/pyminer_config + Returns: + + """ + path = os.path.join(get_pyminer_data_dir(), "pyminer_config") + if not os.path.exists(path): + os.makedirs(path) return path -def get_path_app_local_data() -> str: - path = QStandardPaths.writableLocation(QStandardPaths.AppLocalDataLocation) - return path \ No newline at end of file +def get_config_file_dir(filename: str) -> str: + """ + 获取设置文件的路径 + Args: + filename: + + Returns: + + """ + assert filename in SETTINGS_FILES + user_cfg_file_path = os.path.join(get_user_config_dir(), filename) + if not os.path.exists(user_cfg_file_path): + default_cfg_file_path = os.path.join(get_default_config_dir(), filename) + shutil.copy(default_cfg_file_path, user_cfg_file_path) + return user_cfg_file_path diff --git a/utils/settings/__init__.py b/utils/settings/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c70020d5f7b2fe8c5d7f62e339f4c79895986c8f --- /dev/null +++ b/utils/settings/__init__.py @@ -0,0 +1 @@ +from .settings import * \ No newline at end of file diff --git a/utils/settings/settings.py b/utils/settings/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..6ededd52be97d214061c35ac3c88d8a9caee2931 --- /dev/null +++ b/utils/settings/settings.py @@ -0,0 +1,88 @@ +import ast +import os +import shutil +from typing import Any + +from PySide2.QtCore import QSettings + +# from features.ui.base.Preferences import Ui_Form + +Ui_Form = object + +SETTINGS_FILES = {"config.ini"} + + +def get_settings_from_file(file_name: str) -> QSettings: + """ + 从文件中获取设置 + Args: + file_name: + + Returns: + + """ + from ..path import get_config_file_dir + return QSettings(get_config_file_dir(file_name), QSettings.IniFormat) + + +def get_settings_item_from_file(file_name: str, item: str, mode="user") -> Any: + """ + 从文件中获取设置。如果用户目录下没有,就去默认目录下面寻找。 + 这样可以解决软件更新时引入的问题。 + 在此时会调用ast.literal_eval函数进行数据类型转换。 + Args: + file_name: + item: + mode: 两种选项,有user和default。 + + Returns: + + """ + assert mode in {"user", "default"} + assert file_name in SETTINGS_FILES + from utils.path import get_user_config_dir, get_default_config_dir + user_cfg_file_path = os.path.join(get_user_config_dir(), file_name) + default_cfg_file_path = os.path.join(get_default_config_dir(), file_name) + if mode == "user": + if not os.path.exists(user_cfg_file_path): + shutil.copy(default_cfg_file_path, user_cfg_file_path) + val = QSettings(user_cfg_file_path, QSettings.IniFormat).value(item) + if val is None: + val = QSettings(default_cfg_file_path, QSettings.IniFormat).value(item) + else: + val = QSettings(default_cfg_file_path, QSettings.IniFormat).value(item) + assert val is not None, (default_cfg_file_path, item, mode) + try: + val = ast.literal_eval(val) + except: + pass + return val + + +def write_settings_item_to_file(file_name: str, item: str, value: Any): + """ + 带有类型转换地写入。 + 一般的,设置类型只能是 + int,float,str以及由他们repr之后组成的结果。 + + Args: + file_name: + item: + value: + + Returns: + + """ + assert file_name in SETTINGS_FILES + from utils.path import get_config_file_dir + + value_str = "" + if isinstance(value, str): + value_str = value + else: + value_str = repr(value) + QSettings(os.path.join(get_config_file_dir(file_name)), QSettings.IniFormat).setValue(item, value_str) + + +if __name__ == '__main__': + print(get_settings_item_from_file("config.ini", "RUN/EXTERNAL_INTERPRETERS"))