diff --git a/packages/dataio/sample.py b/packages/dataio/sample.py index 29ea6d221fb845552c29efd7e4ff7b9293d5b1be..30f0c790d73186a1cfbbc34c779a1a412e855c3c 100644 --- a/packages/dataio/sample.py +++ b/packages/dataio/sample.py @@ -1,20 +1,19 @@ # -*- encoding: utf-8 -*- import logging +import os import time -import traceback -import typing -import os -import openpyxl -import xlrd -from PySide2.QtWidgets import * import numpy as np +import openpyxl import pandas as pd -from pyminer_comm import set_var -from dataImportModel import Ui_Form as dataImportFormEngine - +import xlrd # 导入PyQt5模块 from PySide2.QtCore import * +from PySide2.QtWidgets import * + +from dataImportModel import Ui_Form as dataImportFormEngine +from pmgwidgets import kwargs_to_str +from pyminer_comm import set_var, run_command # 导入matlab加载模块 @@ -198,12 +197,12 @@ class ImportDialog(QDialog): break if ok: self.newdatasetname["varname"][e] = var_name - #if self.import_param["ismerge"]: + # if self.import_param["ismerge"]: # self.newdatasetname["datasetname"] = var_name - # self.import_param["datasetname"] = var_name - #else: + # self.import_param["datasetname"] = var_name + # else: # self.newdatasetname["varname"][e] = var_name - # self.import_param["varname"][e] = var_name + # self.import_param["varname"][e] = var_name return (ok) def sendDataset(self): @@ -212,9 +211,9 @@ class ImportDialog(QDialog): 其实意思就是把pandas数据加入到工作空间中。 """ if self.import_param["status"]: - #if self.import_param["ismerge"]: + # if self.import_param["ismerge"]: # set_var(self.newdatasetname["datasetname"], self.current_dataset) - #else: + # else: for name_i, var_i in self.newdatasetname["varname"].items(): set_var(var_i, self.current_dataset[name_i]) # 将数据导入工作空间 QMessageBox.information(self, "{}导入结果".format(""), "数据导入完成!", QMessageBox.Yes) @@ -539,6 +538,7 @@ class ImportTextForm(ImportDialog, dataImportFormEngine): # 优化完成 class ImportCsvForm(ImportDialog, dataImportFormEngine): """导入CSV窗口""" + def __init__(self, parent=None): self.IconPath = ":/color/theme/default/icons/csv.svg" self.file_types = "*.csv" @@ -603,11 +603,10 @@ class ImportCsvForm(ImportDialog, dataImportFormEngine): self.current_dataset = {} varname = self.import_param["datasetname"] # CSV一次只导入一个文件,因此默认变名称即为数据集名称 - print('cc', self.import_param['varname']) self.current_dataset[varname] = pd.read_csv(**param) + run_command("", "pd.read_csv(%s)" % kwargs_to_str(param)) self.getDatasetInfo() self.import_param.update(status=True) - print('dd', self.import_param['varname']) def updateTableView(self): """ @@ -629,9 +628,11 @@ class ImportCsvForm(ImportDialog, dataImportFormEngine): self.showDatasetPreview(data=preview_data, header=header) + # 后续还需要进一步优化方案 class ImportExcelForm(ImportDialog, dataImportFormEngine): """打开excel导入窗口""" + def __init__(self, parent=None): super().__init__(parent) self.IconPath = ":/color/theme/default/icons/excel.svg" @@ -729,6 +730,7 @@ class ImportExcelForm(ImportDialog, dataImportFormEngine): # 默认都是全部加载后在处理 param.update(sheet_name=sheet_i) self.current_dataset[sheet_i] = pd.read_excel(**param) + run_command("", "pd.read_excel(%s)" % kwargs_to_str(param)) if not self.import_param["ispreview"]: sheet_ind = self.comboBox_sheetname.currentText() @@ -859,9 +861,11 @@ class ImportSpssForm(ImportDialog, dataImportFormEngine): name = self.import_param["datasetname"] self.showDatasetPreview(data=self.current_dataset[name], header=True) + # 优化完成 class ImportSasForm(ImportDialog, dataImportFormEngine): """打开从sas导入窗口""" + def __init__(self, parent=None): super().__init__(parent) self.file_types = "*.sas7bdat" @@ -941,9 +945,11 @@ class ImportSasForm(ImportDialog, dataImportFormEngine): name = self.import_param["datasetname"] self.showDatasetPreview(data=self.current_dataset[name], header=True) + # 优化完成 class ImportMatlabForm(ImportDialog, dataImportFormEngine): """打开matlab导入窗口""" + def __init__(self, parent=None): super().__init__(parent) self.new_import_filepath = "" @@ -1013,7 +1019,8 @@ class ImportMatlabForm(ImportDialog, dataImportFormEngine): if not self.import_param["ispreview"]: for name_i, var_i in self.current_dataset.items(): - self.current_dataset[name_i] = pd.DataFrame(var_i) if self.import_param["asdataframe"] and len(var_i.shape) <= 2 else var_i + self.current_dataset[name_i] = pd.DataFrame(var_i) if self.import_param["asdataframe"] and len( + var_i.shape) <= 2 else var_i varname = self.comboBox_varname.currentText() if varname != "(全部导入)": self.import_param.update(ismerge=False) @@ -1053,9 +1060,11 @@ class ImportMatlabForm(ImportDialog, dataImportFormEngine): preview_data = temp self.showDatasetPreview(data=preview_data, header=True) + # 优化完成 class ImportStataForm(ImportDialog, dataImportFormEngine): """打开stata导入窗口""" + def __init__(self, parent=None): super().__init__(parent) self.file_types = "*.dta" @@ -1067,7 +1076,7 @@ class ImportStataForm(ImportDialog, dataImportFormEngine): def AddUIFormActivity(self): """增加界面中的操作操作响应""" - self.checkBox_ifColIndex.stateChanged.connect(self.updateTableView) # 选择首行是否为列名 + self.checkBox_ifColIndex.stateChanged.connect(self.updateTableView) # 选择首行是否为列名 def updateUIForm(self): """ImportMatlabForm配置参数部分""" diff --git a/packages/ipython_console/initialize.py b/packages/ipython_console/initialize.py index e8faefc6da0d1d3ba3c0b6ca3c4545ba7f97f102..c50ffd9a336696d9183f858708430f1f1823c482 100644 --- a/packages/ipython_console/initialize.py +++ b/packages/ipython_console/initialize.py @@ -4,25 +4,23 @@ 之后重构可以考虑直接重写IPython.core.interactiveshell.InteractiveShell的各个方法,这样比在这里装配要好一些。 """ +import logging import types +import typing + +from IPython.core.magic import register_line_cell_magic -import numpy as np -import matplotlib.pyplot as plt -import pandas as pd -import scipy.integrate -import scipy.stats -import ast -from IPython.core.magic import register_line_magic, register_cell_magic, 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 -from pyminer_comm import modify_settings, set_data_desc_dic -import typing if typing.TYPE_CHECKING: from IPython.core.interactiveshell import InteractiveShell from IPython.core.getipython import get_ipython +logging.basicConfig(filename=r"c:\users\12957\desktop\log.txt", level=logging.DEBUG) +__logger = logging.getLogger("ipython") __ip: 'InteractiveShell' = get_ipython() __ip.builtin_vars = [__k for __k in globals().keys()] # 内置保留变量,不可删除或者清空。 @@ -44,9 +42,6 @@ def __init_server(): import threading import logging import typing - import cloudpickle - import base64 - import sys log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) @@ -119,7 +114,7 @@ def __init_server(): protocol = int(request.args.get('protocol')) assert 1 < protocol <= 5 - return dict_to_b64({"data_descs":get_ipython().get_data_descs()}, protocol) + return dict_to_b64({"data_descs": get_ipython().get_data_descs()}, protocol) @server.route('/set_data') def set_var_by_http(): @@ -289,7 +284,7 @@ def __filter_vars(__data: dict) -> dict: """ __ip = get_ipython() return {__k: __v for __k, __v in __data.items() if not ( - __k.startswith('_') or isinstance(__v, types.ModuleType) or __k in __ip.builtin_constants + __k.startswith('_') or isinstance(__v, types.ModuleType) or __k in __ip.builtin_constants or __k in __ip.builtin_values.keys())} @@ -353,6 +348,7 @@ __ip.original_run_cell_func = __ip.run_cell def __cell_exec_func(raw_cell, store_history=False, silent=False, shell_futures=True): """ 相当于重写IPython的执行代码的函数! + 在执行的外边包了一层抽象语法树解释。 Args: raw_cell: store_history: @@ -362,7 +358,7 @@ def __cell_exec_func(raw_cell, store_history=False, silent=False, shell_futures= Returns: """ - import ast, sys + import ast __ip = get_ipython() __cwd = os.getcwd() @@ -411,6 +407,7 @@ def __cell_exec_func(raw_cell, store_history=False, silent=False, shell_futures= s = 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()) + __logger.warning(repr((raw_cell, store_history))) __ip.update_workspace() if __cwd != os.getcwd(): modify_settings({'work_dir': os.getcwd()}) @@ -446,6 +443,7 @@ def __save_vars(var_names: list, path: str, save_type: str): def __load_vars(): __ip = get_ipython() + def __get_data_descs(): __var_dic = {} __ip = get_ipython() @@ -454,6 +452,7 @@ def __get_data_descs(): __var_dic[__k] = DataDesc(__var) return __var_dic + __init_server() __ip.builtin_values = {__k: __v for __k, __v in globals().items() if not __k.startswith('__')} @@ -467,4 +466,4 @@ __ip.original_reset_func = __ip.reset __ip.reset = __reset __ip.update_workspace = __update_workspace __ip.chdir = __chdir -__ip.get_data_descs =__get_data_descs +__ip.get_data_descs = __get_data_descs diff --git a/packages/pm_preprocess/fastui/base.py b/packages/pm_preprocess/fastui/base.py index 33e9f0ae80d88fe316f6de29fcf55402e671b6c4..b161210f57305f05695083711833e147c634ceff 100644 --- a/packages/pm_preprocess/fastui/base.py +++ b/packages/pm_preprocess/fastui/base.py @@ -208,6 +208,45 @@ class DFOperationDialog(BaseOperationDialog): self.combo_box.setCurrentText(vars[0]) +class FunctionGUIDialog(DFOperationDialog): + def __init__(self, dic): + super(FunctionGUIDialog, self).__init__() + self.func_name = dic["func_name"] + self.with_object = dic["with_object"] + if not self.with_object: + self.combo_box.hide() + self.setWindowTitle(dic["title"]) + views = [] + optional_names = [] + for arg in dic["args"]: + views.append("-"*60) + if arg["optional"]: + optional_names.append(arg["name"]) + views.append(("check_ctrl", arg["name"] + "#enable", "", True)) + 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() # 只获取使能并且可见的控件的值 + values = {k: v for k, v in values.items() if k.isidentifier()} + varname = self.combo_box.currentText() + args_str = '' + for k, v in values.items(): + args_str += '{k}={v},'.format(k=k, v=repr(v)) + if not self.with_object: + code = '{func_name}({args})'.format(func_name=self.func_name, args=args_str) + else: + code = '{var_name}.{method_name}({args})'.format(var_name=varname, + method_name=self.func_name, + args=args_str) + + return code + if __name__ == '__main__': app = QApplication([]) md = DFOperationDialog() diff --git a/packages/pm_preprocess/fastui/pivot.py b/packages/pm_preprocess/fastui/pivot.py new file mode 100644 index 0000000000000000000000000000000000000000..6583dc48a00609a8d66c0e75cddb61cdabf687a6 --- /dev/null +++ b/packages/pm_preprocess/fastui/pivot.py @@ -0,0 +1,98 @@ +from PySide2.QtWidgets import QApplication + +from pmgwidgets import FunctionGUIDialog + +dic = { + "title": "数据透视", + "func_name": "pivot", + "with_object": True, + "args": [ + { + "name": "index", + "title": "行", + "optional": True, + "ctrl": { + "type": "multitype_ctrl", + "name": "index", + "title": "选择行", + "init": "", + "types": + [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表',), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', ""), + ], + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }] + } + }, + { + "name": "columns", + "title": "选择列", + "optional": False, + "ctrl": ("multitype_ctrl", "columns", "选择列", "", [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表',), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', ""), + ], + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }]), + }, + + { + "name": "values", + "title": "值", + "optional": True, + "ctrl": ("multitype_ctrl", 'values', "值", "", [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表', [[None, ], ['']], lambda: None), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', "Please input a string"), + ], + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }]) + } + ] + +} + + +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/pm_preprocess/main.py b/packages/pm_preprocess/main.py index 7511738703c190296e2f0397f0fce1a2469ea873..9b1827a0ef14630002b83401b073df6cd34ddf0b 100644 --- a/packages/pm_preprocess/main.py +++ b/packages/pm_preprocess/main.py @@ -3,22 +3,21 @@ import os from typing import TYPE_CHECKING, Callable from PySide2.QtCore import QTranslator, QLocale, Qt -from PySide2.QtWidgets import QApplication, QDialog from PySide2.QtCore import Signal +from PySide2.QtWidgets import QApplication, QDialog -from pmgwidgets import PMGToolBar, create_icon, Dict -from pyminer_comm import get_var from features.extensions.extensionlib import BaseExtension, BaseInterface +from pmgwidgets import PMGToolBar, create_icon, Dict logger = logging.getLogger(__name__) if TYPE_CHECKING: from features.extensions.extensionlib import extension_lib -from . import preprocess from .datareplace import DataReplaceForm from .datamissingvalue import DataMissingValueForm from .data_filter import DataFilterForm from .fastui import MergeDialog +from .fastui.pivot import PivotDialog logger = logging.getLogger(__name__) if TYPE_CHECKING: @@ -52,6 +51,10 @@ class PMPreprocessToolBar(PMGToolBar): create_icon(':/color/theme/default/icons/replace.svg')) self.add_tool_button('button_data_info', self.tr('Data Info'), self.tr('Data Info'), create_icon(':/color/theme/default/icons/data_info.svg')) + + self.add_tool_button('button_pivot', self.tr('数据透视'), self.tr('选择行列并制作数据透视表'), + create_icon(':/color/theme/default/icons/data_info.svg')) + self.add_tool_button('button_data_column', self.tr('Column'), self.tr('Column'), create_icon(':/color/theme/default/icons/column.svg')) self.addSeparator() @@ -115,7 +118,8 @@ class PMPreprocessToolBar(PMGToolBar): window_dic = { 'transpose': TransposeDialog, 'fillna': FillNADialog, - 'dropna': DropNADialog + 'dropna': DropNADialog, + "pivot": PivotDialog, } if window_name not in self.op_windows.keys(): self.op_windows[window_name] = window_dic[window_name]() @@ -128,7 +132,8 @@ class PMPreprocessToolBar(PMGToolBar): 绑定事件。这个将在界面加载完成之后被调用。 """ self.get_control_widget('button_data_filter').clicked.connect(self.show_data_filter) - # self.get_control_widget('button_data_filter').setEnabled(False) + self.get_control_widget('button_pivot').clicked.connect(lambda: self.show_window("pivot")) + self.get_control_widget('button_data_replace').setEnabled(False) # clicked.connect(self.show_data_replace) self.get_control_widget('button_data_fillna').clicked.connect(lambda: self.show_window('fillna')) @@ -199,6 +204,7 @@ class PMPreprocessToolBar(PMGToolBar): self.data_replace = DataReplaceForm() self.data_replace.show() + class Extension(BaseExtension): if TYPE_CHECKING: interface: 'DrawingsInterface' = None diff --git a/pmgwidgets/widgets/basic/others/console.py b/pmgwidgets/widgets/basic/others/console.py index 34083e554c0972b3b6497c7e44015d7359ca97ed..8cb374e41cc8b055d2d36acf061de410ae260c70 100644 --- a/pmgwidgets/widgets/basic/others/console.py +++ b/pmgwidgets/widgets/basic/others/console.py @@ -8,20 +8,19 @@ Created on 2020/8/24 @file: console.py @description: Console Widget """ -import time import os 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 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 PySide2.QtCore import QObject, Signal, QThread, QWaitCondition, QMutex, QPoint, QCoreApplication, QTranslator, QLocale -from PySide2.QtGui import QTextCursor -from PySide2.QtWidgets import QMessageBox, QMenu, QApplication - default_dark_style_template = styles.default_template + """\ .in-prompt { color: #ff00ff; } .out-prompt { color: #ff0000; } @@ -279,7 +278,6 @@ class PMGIpythonConsole(RichJupyterWidget): if not hidden: self.executing.emit(source) return msg_id - # super()._execute(source, hidden) def is_source_code_legal(self, source_code: str) -> Tuple[bool, str]: """判断注入到shell中的命令是否合法。 diff --git a/pmgwidgets/widgets/composited/__init__.py b/pmgwidgets/widgets/composited/__init__.py index ec0bb68980ac993134fc3ff500fab7c5894ff4af..5a23169cfd5081625ae885232aa70aa7c635443c 100644 --- a/pmgwidgets/widgets/composited/__init__.py +++ b/pmgwidgets/widgets/composited/__init__.py @@ -1,4 +1,5 @@ """ 综合控件 """ -from .generalpanel import * \ No newline at end of file +from .generalpanel import * +from .fastui import * \ No newline at end of file diff --git a/pmgwidgets/widgets/composited/fastui.py b/pmgwidgets/widgets/composited/fastui.py new file mode 100644 index 0000000000000000000000000000000000000000..6d2df6b05db6940f943ff0025e88cf1181ed345c --- /dev/null +++ b/pmgwidgets/widgets/composited/fastui.py @@ -0,0 +1,272 @@ +""" +这是定义基类的基础文件,里面是用代码生成方式来制作功能性面板的示例。比如 +可以参阅同一目录下的其他文件。这些面板默认样式为悬浮在最上方,即使面板弹出,也可以操作主界面。 + +面板文件与PyMiner主程序完全解耦,启动PyMiner主程序后,可以直接运行此文件,或者fastui文件夹下的任何.py文件, +以此调试面板。此时,需要确保PyMiner的工作空间中有pandas.DataFrame类型的数据。 + +如datamerge.py(数据集合并)/ dropdata.py(去除缺失值)/ fillna(填充缺失值) 等。 + +最底层基类是BaseOperationDialog,是一个QDialog对话框基类,里面定义了一系列的基础方法,比如变量获取等。它是一个完全空白的对话框。 +次底层基类是DFOperationDialog,是用于数据集操作的基类。它定义有一个变量选择下拉菜单self.combo_box、一个参数输入面板self.panel,以及一个带有 +四个按钮(预览、保存、关闭和帮助)的按钮面板。 + +数据合并的面板继承于BaseOperationDialog,其余面板都继承于DFOperationDialog。这是因为数据合并面板需要多个选择数据的下拉框,用后者满足不了要求。 + +有关底层基类中的相关方法,请移步相应类定义的位置。 + +这些面板生成的函数,应当是一个有返回值的函数,参数为工作空间中的变量,或者是字符串、数字等。 + +比如,工作空间中有函数a、b。数据合并面板希望生成一个横向合并a和b的函数。那么,最终生成的代码应该为: +pd.concat([a,b],axis=0) + +这段函数有两种执行方式。一种是预览(preview),由preview按钮触发,调用基类的preview方法,直接执行pd.concat([a,b],axis=0);另一种是储存(store), +由“Save to var“按钮触发,调用基类store方法,要求用户输入一个新的变量名,然后执行代码f = pd.concat([a,b],axis=0),(f为用户输入的变量名)。 + +pd.concat函数有返回值。在点击“preview” + +@Time: 2021/2/8 12:54 +@Author: Zhanyi Hou +@Email: 1295752786@qq.com +@File: pmgui.py +""" +from abc import abstractmethod +from typing import List + +from PySide2.QtCore import Qt, QCoreApplication, Signal +from PySide2.QtWidgets import QDialog, QVBoxLayout, QApplication, QPushButton, QComboBox, QHBoxLayout, QLabel, \ + QTextBrowser + +from pyminer_comm import get_var_names, run_command, call_interface +from utils import input_identifier, bind_combo_with_workspace + + +def kwargs_to_str(kwargs: dict) -> str: + """ + 将字典参数转化为字符串的简易方法 + Args: + kwargs: + + Returns: + + """ + args_str = '' + for k, v in kwargs.items(): + args_str += '{k}={v},'.format(k=k, v=repr(v)) + return args_str + + +class BaseOperationDialog(QDialog): + """ + 最底层的功能面板基类。 + 定义了代码生成的有关方法。 + + """ + signal_update_combo = Signal() + + def __init__(self): + from pmgwidgets.utilities import PMGOneShotThreadRunner + self.thrunner: PMGOneShotThreadRunner = None + super(BaseOperationDialog, self).__init__() + + def store(self): + """ + 执行存储到变量的命令。 + + Returns: + + """ + code = self.get_assignment_code() + if code != '': + run_command(command=code, hint_text=self.get_prompt_template() + code, hidden=False) + + def preview(self) -> None: + """ + 预览分析结果。 + Returns: + + """ + code = self.get_value_code() + if code != '': + run_command(command=code, hint_text=self.get_prompt_template() + code, hidden=False) + + def help(self) -> None: + """ + 弹出帮助面板 + Returns: None + + """ + dlg = QDialog() + dlg.setLayout(QVBoxLayout()) + textBrowser = QTextBrowser(self) + dlg.layout().addWidget(textBrowser) + textBrowser.setMarkdown(self.get_help_content()) + dlg.exec_() + + def get_help_content(self) -> str: + """ + 生成帮助内容 + + 为markdown格式 + + Notes:子类最好重写这个方法 + Returns: + + """ + return """# 帮助\n\n暂未定义帮助""" + + def get_prompt_template(self) -> str: + """ + 获取提示模板 + + Notes:子类可以不重写这个方法 + Returns: + """ + return '' + + def kwargs_to_str(self, kwargs: dict) -> str: + """ + 将字典参数转化为字符串的简易方法 + Args: + kwargs: + + Returns: + + """ + args_str = '' + for k, v in kwargs.items(): + args_str += '{k}={v},'.format(k=k, v=repr(v)) + return args_str + + @abstractmethod + def get_value_code(self) -> str: + """ + 获取一段代码,这段代码应当有返回值,而不是赋值代码。 + 例如, “a = pd.concat([c,d])” + + Notes:子类必须重写这个方法。 + + Returns: + """ + return '' + + def get_assignment_code(self) -> str: + """ + 获取赋值语句。实际上就是先让用户输入一个变量名,然后将这个变量名和赋值语句放在get_value_code方法 + 生成的代码的左侧。 + + 倘若用户输入的变量名是“a”,get_value_code方法生成的代码为“pd.concat([c,d])”,那么这个方法生成的代码就是: + “a = pd.concat([c,d])” + + 子类中通常无需重写这个方法 + + Returns: 合并好的代码 + """ + code = self.get_value_code() + identifier = input_identifier(parent=self, default_name=self.combo_box.currentText(), allow_existing_name=True) + if identifier != '': + code = identifier + ' = ' + code + return code + return '' + + def get_selected_variables(self) -> List[str]: + """ + 获取当前界面上选中的变量。 + 当界面为PyMiner调用时,此方法不能在主进程中调用,否则会阻塞消息循环。 + Returns: 当前界面上选中的变量,可能是一个列表。 + """ + return call_interface('workspace_inspector', 'get_selected_variables', {}, request_ret=True) + + def showEvent(self, arg__1: 'QShowEvent') -> None: + from pmgwidgets.utilities import PMGOneShotThreadRunner + if self.thrunner is None or not self.thrunner.is_running(): + self.thrunner = PMGOneShotThreadRunner(callback=self.get_selected_variables) + self.thrunner.signal_finished.connect(self.on_update_combo) + + +class DFOperationDialog(BaseOperationDialog): + signal_update_combo = Signal() + + def __init__(self): + from pmgwidgets.widgets.composited import PMGPanel + super(DFOperationDialog, self).__init__() + self.setLayout(QVBoxLayout()) + self.combo_box = QComboBox() + bind_combo_with_workspace(self.combo_box) + self.panel = PMGPanel() + self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) + self.button_layout = QHBoxLayout() + + self.button_preview = QPushButton(QCoreApplication.translate('BaseDFOperationDialog', "预览")) + self.button_store = QPushButton(QCoreApplication.translate('BaseDFOperationDialog', "保存到变量")) + self.button_close = QPushButton(QCoreApplication.translate('BaseDFOperationDialog', "关闭")) + self.button_help = QPushButton(QCoreApplication.translate('BaseDFOperationDialog', "帮助")) + + # self.button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Help) + self.button_layout.addWidget(self.button_preview) + self.button_layout.addWidget(self.button_store) + self.button_layout.addWidget(self.button_help) + self.button_layout.addWidget(self.button_close) + + self.button_preview.clicked.connect(self.preview) # 预览 + self.button_store.clicked.connect(self.store) # 储存 + self.button_close.clicked.connect(self.close) # 关闭对话框 + self.button_help.clicked.connect(self.help) # 显示帮助 + + self.hint_label = QLabel(self.tr('选择变量')) + self.layout().addWidget(self.hint_label) + self.layout().addWidget(self.combo_box) + self.layout().addWidget(self.panel) + self.layout().addLayout(self.button_layout) + + def on_update_combo(self, vars: List[str]): + self.combo_box.clear() + self.combo_box.addItems(get_var_names()) + if len(vars) > 0: + self.combo_box.setCurrentText(vars[0]) + + +class FunctionGUIDialog(DFOperationDialog): + def __init__(self, dic): + super(FunctionGUIDialog, self).__init__() + self.func_name = dic["func_name"] + self.with_object = dic["with_object"] + if not self.with_object: + self.combo_box.hide() + self.setWindowTitle(dic["title"]) + views = [] + optional_names = [] + for arg in dic["args"]: + views.append("-" * 60) + if arg["optional"]: + optional_names.append(arg["name"]) + views.append(("check_ctrl", arg["name"] + "#enable", "", True)) + 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() # 只获取使能并且可见的控件的值 + values = {k: v for k, v in values.items() if k.isidentifier()} + varname = self.combo_box.currentText() + args_str = '' + for k, v in values.items(): + args_str += '{k}={v},'.format(k=k, v=repr(v)) + if not self.with_object: + code = '{func_name}({args})'.format(func_name=self.func_name, args=args_str) + else: + code = '{var_name}.{method_name}({args})'.format(var_name=varname, + method_name=self.func_name, + args=args_str) + + return code + + +if __name__ == '__main__': + app = QApplication([]) + md = DFOperationDialog() + md.show() + app.exec_() diff --git a/pmgwidgets/widgets/composited/generalpanel.py b/pmgwidgets/widgets/composited/generalpanel.py index 51bcbf60ff1d88447f28cd2224c2ad0b017a9c82..df345e0f08fbe4ee362d9ae67724a1ca1406db92 100644 --- a/pmgwidgets/widgets/composited/generalpanel.py +++ b/pmgwidgets/widgets/composited/generalpanel.py @@ -15,10 +15,11 @@ from typing import Any, List, Tuple, Dict, Callable, Union, Optional from PySide2.QtCore import Signal from PySide2.QtGui import QCloseEvent -from PySide2.QtWidgets import QSpacerItem, QSizePolicy, QDialog, QFrame, QVBoxLayout, QHBoxLayout +from PySide2.QtWidgets import QSpacerItem, QSizePolicy, QDialog, QFrame, QVBoxLayout, QHBoxLayout, QDialogButtonBox, \ + QApplication, QLabel from pmgwidgets.widgets.extended import (BaseExtendedWidget, PMGCheckCtrl, PMGColorCtrl, PMGEvalCtrl, PMGComboCtrl, - PMGFileCtrl, + PMGFileCtrl, PMGMultiTypeCtrl, PMGVariablesComboCtrl, PMGKeyMapCtrl, PMGFolderCtrl, PMGLineCtrl, PMGNumberCtrl, PMGPasswordCtrl, PMGListCtrl, PMGDateCtrl, PMGTimeCtrl, PMGDateTimeCtrl, PMGNumberSpinCtrl, PMGTableShow, PMGLabelShow, PMGRuleCtrl) @@ -39,7 +40,9 @@ views_dic.update({'color_ctrl': PMGColorCtrl, 'folder_ctrl': PMGFolderCtrl, 'line_ctrl': PMGLineCtrl, 'number_ctrl': PMGNumberCtrl, - 'password_ctrl': PMGPasswordCtrl}) + 'password_ctrl': PMGPasswordCtrl, + 'multitype_ctrl': PMGMultiTypeCtrl, + "vars_combo_ctrl": PMGVariablesComboCtrl}) views_dic.update({'editor_ctrl': globals().get('PMGEditorCtrl'), 'check_ctrl': PMGCheckCtrl, @@ -153,7 +156,9 @@ class PMGPanel(QFrame): :param argument: :return: """ - if isinstance(argument, (tuple, list)): + if isinstance(argument, str): + layout.addWidget(QLabel(argument)) + elif isinstance(argument, (tuple, list)): name = argument[1] try: widget = views_dic[argument[0]](self.layout_dir, *argument[2:]) @@ -372,7 +377,6 @@ def parse_simplified_pmgjson(identifier, data, params) -> Optional[List[Union[in if __name__ == '__main__': import sys - from PySide2.QtWidgets import QApplication, QWidget, QDialogButtonBox app = QApplication(sys.argv) # 类型;名称;显示的提示文字;初始值;//单位;范围 @@ -401,41 +405,54 @@ if __name__ == '__main__': sp = PMGPanel(views=views1, layout_dir='v') sp.set_items(views1) sp.signal_settings_changed.connect(lambda settings: print('views1-settings', settings)) - sp.show() + # sp.show() sp2 = PMGPanel(views=views2, layout_dir='v') sp2.signal_settings_changed.connect(lambda settings: print('views2-settings', settings)) sp2.set_items(views2) - sp2.show() + # sp2.show() import random views3 = \ [ - {'type': 'timeseries_show', - 'name': 'cpu_occupation', - 'title': 'CPU占用', - 'init': {'timestamps': [i + 1 for i in range(10)], - 'line1': {'tag': 'CPU利用率1', - 'data': [random.randint(0, 100) for i in - range(10)]}, - 'line2': {'tag': 'CPU利用率2', - 'data': [random.randint(0, 100) for i in - range(10)]} - }, - 'y_range': (0, 100), - 'xlabel': '时间', - 'ylabel': '占用率', - 'threshold_range': (0, 78), - 'legend_face_color': "#00ff00" - }, + # {'type': 'timeseries_show', + # 'name': 'cpu_occupation', + # 'title': 'CPU占用', + # 'init': {'timestamps': [i + 1 for i in range(10)], + # 'line1': {'tag': 'CPU利用率1', + # 'data': [random.randint(0, 100) for i in + # range(10)]}, + # 'line2': {'tag': 'CPU利用率2', + # 'data': [random.randint(0, 100) for i in + # range(10)]} + # }, + # 'y_range': (0, 100), + # 'xlabel': '时间', + # 'ylabel': '占用率', + # 'threshold_range': (0, 78), + # 'legend_face_color': "#00ff00" + # }, + ("vars_combo_ctrl", "variables", "选择变量", ""), + ("multitype_ctrl", "multiple_types", "多种类型", [[None, None, None], ['诶诶诶', '嗷嗷喊', 'qq']], [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", 'input list of strings', [[None, None, None], ['##1', '##2', '##3']], + lambda: None), + ], + "on_ok": lambda values: values["list_ctrl"][1]}, + { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", 'input a string', "Please input a string"), + ], + "on_ok": None + } + ]) ] - # sp3 = PMGPanel(views=views3, layout_dir='v') - # sp3.signal_settings_changed.connect(lambda settings: print('views2-settings', settings)) - # sp3.set_items(views3) - # sp3.show() - - val = sp.get_value() # 返回一个字典。初始值为表格的第二列:第四列。 - print(val) - sp.signal_settings_changed.connect(lambda x: print(x)) + sp3 = PMGPanel(views=views3, layout_dir='v') + sp3.signal_settings_changed.connect(lambda settings: print('views2-settings', settings)) + sp3.set_items(views3) + sp3.show() + sys.exit(app.exec_()) diff --git a/pmgwidgets/widgets/extended/comboboxes/__init__.py b/pmgwidgets/widgets/extended/comboboxes/__init__.py index 247d4d87d23fe0c70dc695419bd70745120d5bf7..89123c2fb9244fea94042d03277a689c58e1489d 100644 --- a/pmgwidgets/widgets/extended/comboboxes/__init__.py +++ b/pmgwidgets/widgets/extended/comboboxes/__init__.py @@ -1 +1,2 @@ -from .combo import * \ No newline at end of file +from .combo import * +from .variables_combo import PMGVariablesComboCtrl \ No newline at end of file diff --git a/pmgwidgets/widgets/extended/comboboxes/combo.py b/pmgwidgets/widgets/extended/comboboxes/combo.py index 74f015992b58daa275786dd9a047131c13df3dc1..3671b5acf53884ab4ce21b1e4f914420f267ebab 100644 --- a/pmgwidgets/widgets/extended/comboboxes/combo.py +++ b/pmgwidgets/widgets/extended/comboboxes/combo.py @@ -55,7 +55,7 @@ class PMGComboCtrl(BaseExtendedWidget): index = self.choices.index(value) self.check.setCurrentIndex(index) - def get_value(self) -> List[List[str]]: + def get_value(self) -> Any: try: return self.choices[self.check.currentIndex()] except: diff --git a/pmgwidgets/widgets/extended/comboboxes/variables_combo.py b/pmgwidgets/widgets/extended/comboboxes/variables_combo.py new file mode 100644 index 0000000000000000000000000000000000000000..c0b301241812359e3f86ab75d245b51af17ae543 --- /dev/null +++ b/pmgwidgets/widgets/extended/comboboxes/variables_combo.py @@ -0,0 +1,28 @@ +from typing import Any + +from .combo import PMGComboCtrl + + +class Var(): + def __init__(self, name: str): + self.name = name + + def __repr__(self): + return self.name + + +class PMGVariablesComboCtrl(PMGComboCtrl): + def __init__(self, layout_dir: str, title: str, initial_value: Any = ""): + """ + ComboBox control to select values + Args: + layout_dir: + title: + initial_value: + """ + from utils import bind_panel_combo_ctrl_with_workspace + super().__init__(layout_dir, title, initial_value, [""], [""]) + bind_panel_combo_ctrl_with_workspace(self) + + def get_value(self) ->str: + return Var(super().get_value()) diff --git a/pmgwidgets/widgets/extended/lists/listwgt.py b/pmgwidgets/widgets/extended/lists/listwgt.py index ac61010305ad59202fbcb2f59961b3a09c4b795c..46d14e75d19310f0afef4ec531dd3eff3ca66bf3 100644 --- a/pmgwidgets/widgets/extended/lists/listwgt.py +++ b/pmgwidgets/widgets/extended/lists/listwgt.py @@ -1,16 +1,19 @@ from typing import List, Callable -from PySide2.QtGui import QStandardItemModel, QStandardItem, QMouseEvent -from PySide2.QtWidgets import QListWidgetItem, QCompleter from PySide2.QtCore import Qt, QStringListModel +from PySide2.QtGui import QMouseEvent from PySide2.QtWidgets import QLabel, QHBoxLayout, QListWidget, QVBoxLayout, QPushButton, QLineEdit +from PySide2.QtWidgets import QListWidgetItem, QCompleter + from pmgwidgets.widgets.extended.base.baseextendedwidget import BaseExtendedWidget class PMGListCtrl(BaseExtendedWidget): - def __init__(self, layout_dir: str, title: str, initial_value: List[List[str]], new_id_func: Callable = None): + def __init__(self, layout_dir: str, title: str, initial_value: List[List[str]] = None, + new_id_func: Callable = None): super().__init__(layout_dir) self.choices = [] + initial_value = initial_value if initial_value is not None else [[], []] self.text_list = [] lab_title = QLabel(text=title) layout = QHBoxLayout() @@ -18,6 +21,7 @@ class PMGListCtrl(BaseExtendedWidget): self.on_check_callback = None self.list_widget = QListWidget() self.list_widget.mouseDoubleClickEvent = self.on_listwidget_double_cicked + # if initial_value is not None: self.set_value(initial_value) layout_tools = QVBoxLayout() @@ -93,10 +97,11 @@ class PMGListCtrl(BaseExtendedWidget): for i in range(self.list_widget.count()): text.append(self.list_widget.item(i).text()) self.data[1] = text - assert len(self.data[1]) == len(self.data[0]),repr(self.data) + assert len(self.data[1]) == len(self.data[0]), repr(self.data) return self.data def set_value(self, data: List[List[str]]): + assert isinstance(data, list), data self.list_widget.clear() self.list_widget.addItems(data[1]) self.data = data diff --git a/pmgwidgets/widgets/extended/others/__init__.py b/pmgwidgets/widgets/extended/others/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..975bf08bafcf825b3e7be598e1a5ef4817a70f29 100644 --- a/pmgwidgets/widgets/extended/others/__init__.py +++ b/pmgwidgets/widgets/extended/others/__init__.py @@ -0,0 +1 @@ +from .multitypeparaminput import PMGMultiTypeCtrl \ No newline at end of file diff --git a/pmgwidgets/widgets/extended/others/multitypeparaminput.py b/pmgwidgets/widgets/extended/others/multitypeparaminput.py new file mode 100644 index 0000000000000000000000000000000000000000..fb60d0cf00fbc307c34312e350569a508f4d0e20 --- /dev/null +++ b/pmgwidgets/widgets/extended/others/multitypeparaminput.py @@ -0,0 +1,103 @@ +from typing import List, Any, Dict + +from PySide2.QtWidgets import QApplication, QRadioButton, QLayout +from PySide2.QtWidgets import QLabel + +from pmgwidgets.widgets.extended.base.baseextendedwidget import BaseExtendedWidget + + +class PMGMultiTypeCtrl(BaseExtendedWidget): + def __init__(self, layout_dir: str, title: str, initial_value: Any=None, types: List[Dict]=None): + super().__init__(layout_dir) + from pmgwidgets.widgets.composited.generalpanel import PMGPanel + self.on_check_callback = None + + self.prefix = QLabel(text=title) + + self.central_layout.addWidget(self.prefix) + + self.radio_buttons: List[QRadioButton] = [] + self.ctrls = types + radiobtn_texts = [self.ctrls[i]["type_title"] for i in range(len(self.ctrls))] + for i, ctrl in enumerate(self.ctrls): + radio_button = QRadioButton(radiobtn_texts[i]) + radio_button.toggled.connect(self.on_type_changed) + self.radio_buttons.append(radio_button) + self.central_layout.addWidget(radio_button) + + self.sub_panel = PMGPanel() + self.central_layout.addWidget(self.sub_panel) + + self.set_value(initial_value) + + def on_type_changed(self, event): + + index = self.get_type_index() + self.sub_panel.set_items([self.ctrls[index]["ctrls"]]) + + print(self.get_value()) + # self.setFixedSize(0) + + def on_param_changed(self, event): + print(self.get_value()) + + def ontext(self, event): + self.para_changed() + + def set_type_index(self, index: int): + self.radio_buttons[index].setChecked(True) + + def get_type_index(self) -> int: + for i, btn in enumerate(self.radio_buttons): + if btn.isChecked(): + return i + + def set_value(self, value: Any): + for i, ctrl in enumerate(self.ctrls): + try: + self.set_type_index(i) + keys = list(self.sub_panel.widgets_dic.keys()) + assert len(keys) == 1 + print(i, ctrl, {keys[0]: value}) + self.sub_panel.set_value({keys[0]: value}) + break + except: + import traceback + traceback.print_exc() + + def get_value(self) -> Any: + values = self.sub_panel.get_value() + fcn = self.ctrls[self.get_type_index()].get("on_ok") + if fcn is None: + if len(list(values.keys())) == 1: + return values[list(values.keys())[0]] + else: + return values + else: + return fcn(values) + + +if __name__ == '__main__': + app = QApplication() + types = [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", 'input list of strings',), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, + { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", 'input a string', "Please input a string"), + ], + "on_ok": None + } + ] + + e = PMGMultiTypeCtrl("v", "aaaaaa", [[None, None, None], ["aaaaaa", "aaaaaa", "aaaaaa"]], types) + + # e.set_value([[None, None, None], ["aaaaaa", "aaaaaa", "aaaaaa"]]) + e.show() + + app.exec_() diff --git a/pmgwidgets/widgets/extended/radiobuttons/radiobuttonctrl.py b/pmgwidgets/widgets/extended/radiobuttons/radiobuttonctrl.py new file mode 100644 index 0000000000000000000000000000000000000000..26c8a0c89233654166cfc7af5133909ee0d789a1 --- /dev/null +++ b/pmgwidgets/widgets/extended/radiobuttons/radiobuttonctrl.py @@ -0,0 +1,63 @@ +import sys +from typing import List + +from PySide2.QtWidgets import * +from PySide2.QtWidgets import QApplication +from PySide2.QtWidgets import QLabel + +from pmgwidgets.widgets.extended.base.baseextendedwidget import BaseExtendedWidget + + +class PMGRadioCtrl(BaseExtendedWidget): + def __init__(self, layout_dir: str, title: str, initial_value: str, + choices: list, texts=None): + super().__init__(layout_dir) + if texts is not None: + assert len(choices) == len(texts) + else: + texts = [str(c) for c in choices] + self.on_check_callback = None + + self.prefix = QLabel(text=title) + + # entryLayout = QHBoxLayout() + # entryLayout.setContentsMargins(0, 0, 0, 0) + # self.ctrl = QLineEdit() + # self.ctrl.textChanged.connect(self.ontext) + + # self.central_layout.addWidget(self.prefix) + # self.central_layout.addLayout(entryLayout) + # entryLayout.addWidget(self.ctrl) + self.radio_buttons: List[QRadioButton] = [] + self.choices = choices + self.texts = texts + for text in texts: + radio_button = QRadioButton(text) + radio_button.toggled.connect(self.on_param_changed) + self.radio_buttons.append(radio_button) + self.central_layout.addWidget(radio_button) + + self.set_value(initial_value) + + def on_param_changed(self, event): + if event: + self.para_changed() + + def set_value(self, value: str): + for i, c in enumerate(self.choices): + if c == value: + self.radio_buttons[i].setChecked(True) + + def get_value(self) -> str: + for i, btn in enumerate(self.radio_buttons): + if btn.isChecked(): + return self.choices[i] + raise ValueError(self.choices) + + +if __name__ == '__main__': + app = QApplication() + radioDemo = PMGRadioCtrl("v", "Radio Demo", "aaa", ["aaa", "bbb", "ccc"], ["啊啊啊", "波波波", "呲呲呲"]) + radioDemo.set_value("bbb") + radioDemo.show() + sys.exit(app.exec_()) diff --git a/pyminer_comm/pyminer_client/pm_client.py b/pyminer_comm/pyminer_client/pm_client.py index 75722899e2a261adb3dc6535f0b32e3ecaa360b7..12c259dc8308e0267206ebcc7897907529ddbef6 100644 --- a/pyminer_comm/pyminer_client/pm_client.py +++ b/pyminer_comm/pyminer_client/pm_client.py @@ -43,6 +43,17 @@ def get_style_sheet() -> str: def call_interface(interface: str, method: str, kwargs: dict = None, request_ret=False) -> Optional[Any]: + """ + 通过网络,调用PyMiner内部Extension插件的Interface接口。 + Args: + interface: 插件的名称 + method: 插件接口的方法名 + kwargs: 插件的接口函数调用的参数,要求需要能被序列化 + request_ret: 是否要求传回结果,默认为False + + Returns: + + """ kwargs = {} if kwargs is None else kwargs res = get('interface_call', dict_to_b64({'interface': interface, 'method': method, 'kwargs': kwargs}, protocol=get_protocol()), 12306, diff --git a/tests/test_ui/test_pmgpanel.py b/tests/test_ui/test_pmgpanel.py new file mode 100644 index 0000000000000000000000000000000000000000..801cea86db98fee7bec26e66a1048ee7433c70b1 --- /dev/null +++ b/tests/test_ui/test_pmgpanel.py @@ -0,0 +1,77 @@ +import sys + +from PySide2.QtWidgets import QApplication + +from pmgwidgets import PMGPanelDialog + +app = QApplication(sys.argv) + +views3 = \ + [ + + ("multitype_ctrl", "index", "选择行", [[None, ], ['']], [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表', [[None, ], ['']], lambda: None), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', "Please input a string"), + ], "on_ok": lambda values: repr(values["aaaa"]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }]), + + ("multitype_ctrl", "column", "选择列", [[None, ], ['']], [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表', [[None, ], ['']], lambda: None), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', "Please input a string"), + ], + "on_ok": lambda values: repr(values["aaaa"]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }]), + + ("multitype_ctrl", 'values', "值", [[None, ], ['']], [{ + "type_title": "字符串列表", + "ctrls": [ + ("list_ctrl", "list_ctrl", '输入字符串列表', [[None, ], ['']], lambda: None), + ], + "on_ok": lambda values: values["list_ctrl"][1] + }, { + "type_title": "字符串", + "ctrls": [ + ("line_ctrl", "aaaa", '输入一个字符串', "Please input a string"), + ], + "on_ok": lambda values: repr(values["aaaa"]) + }, { + "type_title": "选择变量", + "ctrls": [ + ("vars_combo_ctrl", "variables", "选择变量", ""), + ], + }]) + ] + +optionals = ["index", "values"] # 列出了所有的可选参数 +sp3 = PMGPanelDialog(parent=None, views=views3) +sp3.panel.signal_settings_changed.connect(lambda settings: print('views2-settings', settings)) +sp3.panel.set_items(views3) +sp3.show() +ok = sp3.exec_() + +if ok: + print(sp3.get_value()) diff --git a/utils/ui/uiutil/workspaceutil.py b/utils/ui/uiutil/workspaceutil.py index cfc741a54ed27120b640cbfbd8d8b3b2777bbcda..2761cd643e1652952b999ffd602ced7e9035da99 100644 --- a/utils/ui/uiutil/workspaceutil.py +++ b/utils/ui/uiutil/workspaceutil.py @@ -4,7 +4,7 @@ from typing import Union, Callable from PySide2.QtGui import QMouseEvent from PySide2.QtWidgets import QComboBox, QInputDialog, QWidget, QLineEdit, QMessageBox -from pmgwidgets import PMGComboCtrl + def input_identifier(parent: QWidget, default_name: str = '', allow_existing_name: bool = False) -> str: @@ -18,7 +18,7 @@ def input_identifier(parent: QWidget, default_name: str = '', allow_existing_nam var_names = get_var_names() if var_names is None: var_names = [] - while (1): + while 1: name, ok = QInputDialog.getText(parent, "变量命名", "输入新的变量名称:", QLineEdit.Normal, default_name) if ok and (len(name) != 0): @@ -73,7 +73,7 @@ def bind_combo_with_workspace(combo: QComboBox, type_filter: str = ''): combo.mousePressEvent = on_combo_var_name_mouse_pressed -def bind_panel_combo_ctrl_with_workspace(combo_ctrl: PMGComboCtrl, type_filter: Union[str, Callable[[str], bool]] = '', +def bind_panel_combo_ctrl_with_workspace(combo_ctrl: "PMGComboCtrl", type_filter: Union[str, Callable[[str], bool]] = '', shape_filter: Callable[[Union[int, tuple]], bool] = None): """ 将pmgpanel 中的 下拉列表框PMGComboCtrl与工作空间的变量变更绑定起来。自动获取相应的变量名。 @@ -84,6 +84,7 @@ def bind_panel_combo_ctrl_with_workspace(combo_ctrl: PMGComboCtrl, type_filter: :return: """ from pyminer_comm import get_data_descs + from pmgwidgets import PMGComboCtrl assert isinstance(combo_ctrl, PMGComboCtrl), combo_ctrl combo = combo_ctrl.check if type_filter == "":