diff --git a/pyminer/config/qtlayout.ini b/pyminer/config/qtlayout.ini new file mode 100644 index 0000000000000000000000000000000000000000..2529f682a1cceb64df69df634a722d6e50658cb0 Binary files /dev/null and b/pyminer/config/qtlayout.ini differ diff --git a/pyminer/features/extensions/extensions_manager/manager.py b/pyminer/features/extensions/extensions_manager/manager.py index 1d96f3e3f6eb99a70e72665d3b61a9985c84732d..4423de25357f583e3777e22d8401014073362da0 100644 --- a/pyminer/features/extensions/extensions_manager/manager.py +++ b/pyminer/features/extensions/extensions_manager/manager.py @@ -16,6 +16,7 @@ BASEDIR = os.path.dirname(os.path.dirname( def _log(text): '''日志输出text''' # 放在函数体中防止循环引用,接下来的也是 + get_main_window().slot_flush_console('info', 'Extension') diff --git a/pyminer/pmapp.py b/pyminer/pmapp.py index e4c2bf79aea8ed184947cd44315004cecf8a4abb..b5b0afce4f0a7883ae2c9c71ed28d8a1999f53ed 100644 --- a/pyminer/pmapp.py +++ b/pyminer/pmapp.py @@ -8,8 +8,9 @@ import webbrowser import qdarkstyle import os, sys import pandas as pd -import win32api -import pywintypes + +# import win32api +# import pywintypes import shutil # 导入PyQt5模块 @@ -193,7 +194,6 @@ class CustomRect(QGraphicsObject): painter.drawRoundedRect(self.boundingRect(), 10, 10) # 绘制函数 painter.end() - class MyMainForm(QMainWindow, Ui_MainWindow): """ 主窗体 @@ -214,16 +214,20 @@ class MyMainForm(QMainWindow, Ui_MainWindow): self.setupUi(self) self.center() + self.page_data.setup_ui() + self.console_widget.setup_ui() self.page_data.bind_events(self) self.page_assess.bind_events(self) self.page_model.bind_events(self) self.page_plot.bind_events(self) self.page_stats.bind_events(self) + init_actions(self) self.toolBar_right.bind_events(self) self.toolBar_left.bind_events(self) self.menubar.bind_events(self) + self.menubar.translate() self.__file_path_choose = "" # 将原有的数据存储工具注释掉。方便之后查阅。 diff --git a/pyminer/pmappmodern.py b/pyminer/pmappmodern.py new file mode 100644 index 0000000000000000000000000000000000000000..8d5411102283a608c2ffe77eb5a9432ba57b3c44 --- /dev/null +++ b/pyminer/pmappmodern.py @@ -0,0 +1,254 @@ +''' +pmappmodern.py +作者:侯展意 +主界面 + +√任务:写出显示浮动窗口数量的部件(下拉式菜单等),点击之后,可以进行浮动窗口显示或者隐藏。 + +任务:将主界面中所有功能都迁移到目前的界面中去。 + +主界面由一系列PMDockWidget组成,每一个DockWidget可以获取主界面。 +1、注册工具栏的方法 +主界面的工具栏使用的是okokPMToolBar这个类,继承自QToolBar,结构为1+n。最顶端的选项卡样控件实为QToolBar,可以依靠其按钮的点击来进行工具栏的切换。 +切换工具栏时,其他工具栏隐藏,只有按钮对应的工具栏可以显示。详见switch_toolbar方法。 +主窗口add_tool_bar()方法,可以将QToolBar或者继承它的控件添加到主窗口。 + +当需要在已有的工具栏上添加按钮的时候,比方说要获取主页对应的工具栏,那么就使用 MainWindow.toolbars.get('toolbar_home')进行获取就可以了。 +获取工具栏后,调用add_tool_button()添加一个按钮,或者调用add_tool_buttons()添加多个竖排的按钮。这两个函数的返回值分别为QPushButton +和List[QPushButton] + +如果需要菜单效果,可以自己写一个菜单,然后添加到按钮之上。 +2、添加dockwidget的方法 +MainWindow.add_widget_on_dock()方法可以用来将任意控件添加到dock,而且下次加载之时布局会被保存。 + +为了加快软件启动速度,widget可以定义方法setup_ui(也可以没有)。当加载时,首先执行控件的__init__,并且将setup_ui压入任务栈之中,等到主 +界面显示出来之后再用定时器调用执行控件的setup_ui方法。对于核心控件可以定义show_directly=True,保证立即执行setup_ui方法。或者干脆不写 +setup_ui方法,而是将启动方法放在__init__之中。 + +当dockwidget点击右上角按钮关闭的时候,它并不会被关闭,而是会被隐藏。 +''' +import datetime +import getpass +import os +import sys +import threading +import time + +from PyQt5.QtCore import pyqtSignal, Qt, QTimer, QThread +from PyQt5.QtGui import QIcon, QPixmap, QFont, QCloseEvent, QTextCursor +from PyQt5.QtWidgets import QHBoxLayout, QDockWidget, QMainWindow, QPushButton, QApplication, \ + QWidget, QToolBar, QAction, QVBoxLayout, QMenu, QToolButton, QMessageBox, QStackedWidget, QTabWidget, QTextEdit +from typing import List, Dict, Callable + +from pyminer.ui.base.widgets.generalwidgets.containers import PMTabWidget +from pyminer.ui.base.widgets.generalwidgets.containers.pmscrollarea import PMScrollArea +from pyminer.ui.base.widgets.generalwidgets.sourcemgr import create_icon +from pyminer.ui.base.widgets.generalwidgets.buttons import PushButtonPane +from pyminer.ui.base.widgets.generalwidgets.window import PMDockWidget, BaseMainWindow +from pyminer.ui.base.widgets.generalwidgets.toolbars import PMToolBar, ActionWithMessage + +from pyminer.ui.base.widgets.treeviews import PMFilesTreeview, PMDatasetsTreeview +from pyminer.ui.base.widgets.consolewidget import ConsoleWidget +from pyminer.ui.base.widgets.controlpanel import PMPageData, PMPageExt +from pyminer.ui.base.widgets.codeeditwidget import PMCodeEditTabWidget +from pyminer.ui.base.widgets.tablewidget import PMTableWidget + +from pyminer.features.extensions.extensions_manager.manager import extensions_manager +from pyminer.pmutil import get_main_window, get_root_dir + + +# 继承QThread +class Runthread(QThread): + # 通过类成员对象定义信号对象 + _signal = pyqtSignal(str) + + def __init__(self): + super(Runthread, self).__init__() + + def __del__(self): + self.wait() + + def run(self): + # for i in range(100): + # time.sleep(0.2) + self._signal.emit('') # 注意这里与_signal = pyqtSignal(str)中的类型相同 + + +class PMToolBarHome(PMToolBar): + def __init__(self): + super().__init__() + + self.add_tool_button('新建\n脚本', create_icon(":/pyqt/source/images/lc_newdoc.png")) + self.add_tool_button('新建', create_icon(":/pyqt/source/images/New.png")) + self.add_tool_button('打开', create_icon(":/pyqt/source/images/lc_open.png")) + pp = PushButtonPane() + pp.add_buttons(2, ['查找文件', '文件比较'], + [":/pyqt/source/images/lc_searchdialog.png", ":/pyqt/source/images/lc_pickthrough.png"]) + self.addWidget(pp) + + self.addSeparator() + + self.add_tool_button('导入\n数据', create_icon(":/pyqt/source/images/lc_dataimport.png")) + self.add_tool_button('保存\n工作区', create_icon(":/pyqt/source/images/lc_save.png")) + + pp = PushButtonPane() + b = pp.add_buttons(3, ['新建变量', '打开变量', '清除工作区'], + [":/pyqt/source/images/lc_dbnewform.png", ":/pyqt/source/images/lc_open.png", + ":/pyqt/source/images/lc_dbclearquery.png"]) + self.addWidget(pp) + self.addSeparator() + self.add_tool_button('设置', create_icon(':/pyqt/source/images/lc_config.png')) + self.add_tool_button('帮助', create_icon(':/pyqt/source/images/lc_helpindex.png')) + + self.view_menu = QMenu() + self.view_menu.addMenu('erere') + a = ActionWithMessage(text='aaaaaa', parent=self.view_menu) + a.message = 'tree_widget' + a.setCheckable(True) + self.view_menu.addAction(a) + self.view_menu.triggered.connect(self.process_events) + + pp = PushButtonPane() + buttons = pp.add_buttons(2, ['视图', '']) + self.addWidget(pp) + buttons[0].setMenu(self.view_menu) + self.view_config_button = buttons[0] + + def process_events(self, e: ActionWithMessage): + print(e.text()) + print(e.isChecked()) + main_window = get_main_window() + dws = main_window.dock_widgets + if e.message in dws.keys(): + dws[e.message].setVisible(e.isChecked()) + + + + + +class MainWindow(BaseMainWindow): + setupui_tasks: List[Callable] = [] + boot_timer: QTimer = None + + @classmethod + def __new__(cls, *args): + instance = super().__new__(cls) + cls.instance = instance + return instance + + def __init__(self, parent=None): + super().__init__(parent) + + import pyminer.pmutil + pyminer.pmutil._main_window = self + root_dir = os.path.dirname(__file__) + pyminer.pmutil._root_dir = root_dir + # print(get_root_dir()) + self.init_toolbar_tab() + + self.add_toolbar('toolbar_home', PMToolBarHome(), text='主页') + self.switch_toolbar('toolbar_home') + + self.setDockNestingEnabled(True) + self.setWindowTitle('Dock 例子') + + self.add_widget_on_dock('editor_panel', PMCodeEditTabWidget(), text='编辑器面板', side='right') + self.add_widget_on_dock('tree_widget', PMFilesTreeview(self), text='文件浏览器') + + self.console_widget = ConsoleWidget(self) + self.add_widget_on_dock('console_widget', self.console_widget, text='控制台', side='right') + + self.log_output_console = QTextEdit() + + self.add_widget_on_dock('log_output_console', self.log_output_console, text='日志输出', side='right') + self.tabifyDockWidget(self.dock_widgets['console_widget'], self.dock_widgets['log_output_console']) + + self.add_widget_on_dock('table_panel', PMTableWidget(self), text='数据表格', side='top') + self.tabifyDockWidget(self.dock_widgets['editor_panel'],self.dock_widgets['table_panel']) + + + + self.add_widget_on_dock('dataset_treeview_panel', PMDatasetsTreeview(self), text='数据集视图', side='left') + + tab_widget = PMTabWidget() + + self.data_control_page = PMPageData() + tab_widget.addScrolledAreaTab(self.data_control_page, '数据分析') + + self.add_widget_on_dock('function_panel', tab_widget, text='功能面板', side='left') + + + + self.extensions_manager = extensions_manager + self.extensions_manager.load_setting() + self.extensions_manager.load() + + self.ext_manager_widget = PMPageExt(self) + self.add_widget_on_dock('extension_panel', self.ext_manager_widget, text='插件管理器', side='left') + + + self.tabifyDockWidget(self.dock_widgets['extension_panel'],self.dock_widgets['function_panel']) + + self.load_layout()# 注掉或者删除配置文件之后,可以按照默认方式加载布局。 + self.switch_toolbar('toolbar_home') + self.show() + self.on_boot_finished() + + def action_menu_new(self): + print('new') + + def action_menu_open(self): + print('open!') + + def on_boot_finished(self): + super().on_boot_finished() + self.boot_timer = QTimer() + self.boot_timer.start(50) + self.boot_timer.timeout.connect(self.on_boot_timer_timeout) + + def on_boot_timer_timeout(self): + if len(self.setupui_tasks) > 0: + task = self.setupui_tasks.pop(0) + print('start:', task) + task() + else: + self.boot_timer.stop() + print('boot ended!') + + def get_toolbar_list(self): + pass + + def closeEvent(self, a0: QCloseEvent) -> None: + self.save_layout() + super().closeEvent(a0) + + def slot_flush_console(self, level, module, content): + """ + 刷新主窗体执行情况日志 + :return: + level:文本,warnning error info + module:业务模块名称,例如 数据获取,数据处理,数据探索,统计,模型,可视化,评估 + """ + 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 # 由于代码重构,这里出现了不同。 + # [!TODO]应当创建方法,一次性的完成这个工作。 + console.moveCursor(QTextCursor.End) + console.append(html) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + demo = MainWindow() + # demo.setMaximumHeight(700) + id(demo) + # b=MainWindow() + # id(b) + sys.exit(app.exec_()) diff --git a/pyminer/pmutil.py b/pyminer/pmutil.py index 96a92ba2af4432fb619ead813091d42baff8fd9f..c709e79b6e51b910675859d93f995a67ce78735f 100644 --- a/pyminer/pmutil.py +++ b/pyminer/pmutil.py @@ -5,9 +5,13 @@ import os import sys from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow +from typing import TYPE_CHECKING +if TYPE_CHECKING: + import pyminer.pmappmodern + _application = None _root_dir = None -_main_window = None +_main_window:'pyminer.pmappmodern.MainWindow' = None def get_root_dir() -> str: @@ -30,13 +34,11 @@ def get_application() -> QApplication: return _application -def get_main_window() -> QMainWindow: +def get_main_window() -> 'pyminer.pmappmodern.MainWindow': ''' 获取主窗口或者主控件。 Returns: - ''' - assert _main_window is not None return _main_window @@ -49,6 +51,7 @@ def test_widget_run(widget_type): Returns:None ''' + _root_dir = os.path.dirname(os.path.abspath(__file__)) app = QApplication(sys.argv) diff --git a/pyminer/ui/base/mainForm.ui b/pyminer/ui/base/mainForm.ui index 83b3fc0ba8b079bb5747b907fb3e5438bec3d84e..2ad27c0079913e8de4c5579e23dc863dddfaa0ff 100644 --- a/pyminer/ui/base/mainForm.ui +++ b/pyminer/ui/base/mainForm.ui @@ -167,7 +167,7 @@ - 0 + 3 @@ -496,6 +496,11 @@ 输出 + + + 编辑器 + + diff --git a/pyminer/ui/base/mainFormmodern.ui b/pyminer/ui/base/mainFormmodern.ui new file mode 100644 index 0000000000000000000000000000000000000000..6d3f53af238c8f08511141953eba291ffbf99345 --- /dev/null +++ b/pyminer/ui/base/mainFormmodern.ui @@ -0,0 +1,1465 @@ + + + MainWindow + + + + 0 + 0 + 1366 + 768 + + + + + 1024 + 768 + + + + + Microsoft YaHei UI + + + + Patata + + + + + + + Qt::Horizontal + + + + + 300 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + Qt::CustomContextMenu + + + + 工作区间 + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + 文件 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + true + + + true + + + false + + + true + + + true + + + true + + + false + + + false + + + false + + + true + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 3 + + + + 数据 + + + + + + + + + false + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + + + 13 + + + + + 14 + + + + + 15 + + + + + 16 + + + + + 17 + + + + + 18 + + + + + 19 + + + + + 20 + + + + + 21 + + + + + 22 + + + + + 23 + + + + + 24 + + + + + 25 + + + + + 26 + + + + + 27 + + + + + 28 + + + + + 29 + + + + + 30 + + + + + C1 + + + + + C2 + + + + + C3 + + + + + C4 + + + + + C5 + + + + + C6 + + + + + C7 + + + + + C8 + + + + + C9 + + + + + C10 + + + + + C11 + + + + + C12 + + + + + C13 + + + + + C14 + + + + + C15 + + + + + C16 + + + + + C17 + + + + + C18 + + + + + C19 + + + + + C20 + + + + + C21 + + + + + C22 + + + + + C23 + + + + + C24 + + + + + C25 + + + + + C26 + + + + + C27 + + + + + C28 + + + + + C29 + + + + + C30 + + + + + + + + + 流程 + + + + + 输出 + + + + + 编辑器 + + + + + + QTabWidget::South + + + 1 + + + + 日志 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 16777215 + + + + true + + + + + + + + 控制台 + + + + + + + + + + + + + + 280 + 0 + + + + + 280 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + 标准工具栏 + + + TopToolBarArea + + + false + + + + + + 分析工具栏 + + + Qt::RightToLeft + + + RightToolBarArea + + + false + + + + + + Microsoft YaHei UI + + + + false + + + background-color: rgb(240, 240, 240); + + + + + + 0 + 0 + 1366 + 23 + + + + + + true + + + 4 + + + + + + + + + + + + + :/pyqt/source/images/dbviewtables.png:/pyqt/source/images/dbviewtables.png + + + 数据 + + + 查看数据窗口 + + + + + + :/pyqt/source/images/lc_autosum.png:/pyqt/source/images/lc_autosum.png + + + 统计 + + + 统计 + + + + + + :/pyqt/source/images/lc_drawchart.png:/pyqt/source/images/lc_drawchart.png + + + 可视化 + + + 可视化 + + + + + + :/pyqt/source/images/lc_statisticsmenu.png:/pyqt/source/images/lc_statisticsmenu.png + + + 模型 + + + 模型 + + + + + + :/pyqt/source/images/New.png:/pyqt/source/images/New.png + + + 新建(&N) + + + + Microsoft YaHei UI + + + + Ctrl+N + + + + + + :/pyqt/source/images/folder.png:/pyqt/source/images/folder.png + + + 打开(&O) + + + + Microsoft YaHei UI + + + + Ctrl+O + + + + + + :/pyqt/source/images/input.png:/pyqt/source/images/input.png + + + 导入(&I) + + + + Microsoft YaHei UI + + + + Ctrl+I + + + + + + :/pyqt/source/images/Save.png:/pyqt/source/images/Save.png + + + 保存(&S) + + + Ctrl+S + + + + + 另存为 + + + + + + :/pyqt/source/images/wb-setting-normal.png:/pyqt/source/images/wb-setting-normal.png + + + 设置(&X) + + + + Microsoft YaHei UI + + + + Alt+M + + + + + + :/pyqt/source/images/Delete.png:/pyqt/source/images/Delete.png + + + 退出(&Q) + + + Alt+Q + + + + + + :/pyqt/source/images/Cut.png:/pyqt/source/images/Cut.png + + + 剪切(&X) + + + + + + :/pyqt/source/images/Copy.png:/pyqt/source/images/Copy.png + + + 复制(&C) + + + + + + :/pyqt/source/images/Paste.png:/pyqt/source/images/Paste.png + + + 粘贴(P) + + + + + true + + + true + + + 工具栏 + + + + + true + + + true + + + 状态栏 + + + + + 转置 + + + Ctrl+T + + + + + + :/pyqt/source/images/lc_formfilternavigator.png:/pyqt/source/images/lc_formfilternavigator.png + + + 筛选 + + + + Microsoft YaHei UI + + + + Alt+F + + + + + + :/pyqt/source/images/mergedocuments.png:/pyqt/source/images/mergedocuments.png + + + 纵向合并 + + + + + + :/pyqt/source/images/lc_mergecells.png:/pyqt/source/images/lc_mergecells.png + + + 横向合并 + + + + + 描述统计 + + + + Microsoft YaHei UI + + + + + + 数据分布 + + + + + 缺失值 + + + + + 异常值 + + + + + 相关 + + + + Microsoft YaHei UI + + + + + + 回归 + + + + + 分类 + + + + + 降维 + + + + + 非参数检验 + + + + + 时间序列预测 + + + + + 生存分析 + + + + + 折线图 + + + + Microsoft YaHei UI + + + + Alt+L + + + + + 直方图 + + + Alt+H + + + + + 散点图 + + + Alt+S + + + + + 盒型图 + + + Alt+B + + + + + 条形图 + + + + Microsoft YaHei UI + + + + Alt+P + + + + + 决策树 + + + Alt+T + + + + + ROC曲线 + + + Alt+R + + + + + WOE&&IV + + + Alt+W + + + + + 评分卡 + + + Alt+S + + + + + KS值 + + + Alt+K + + + + + + :/pyqt/source/images/hlinettp.png:/pyqt/source/images/hlinettp.png + + + 官方网站 + + + + Microsoft YaHei UI + + + + + + 检查更新 + + + + Microsoft YaHei UI + + + + + + + :/pyqt/source/images/hlinettp.png:/pyqt/source/images/hlinettp.png + + + 帮助文档 + + + + Microsoft YaHei UI + + + + + + 关于 + + + + Microsoft YaHei UI + + + + + + 删除行 + + + + + 删除列 + + + + + 拆分列 + + + + + 重新编码 + + + + + 唯一值 + + + + + 数据角色 + + + + Microsoft YaHei UI + + + + + + + :/pyqt/source/images/dbqueryedit.png:/pyqt/source/images/dbqueryedit.png + + + 从数据库导入 + + + + Microsoft YaHei UI + + + + + + + :/pyqt/source/images/infobox.png:/pyqt/source/images/infobox.png + + + 快速退出 + + + Alt+Shift+Q + + + + + + :/pyqt/source/images/lc_dbreportedit.png:/pyqt/source/images/lc_dbreportedit.png + + + 评估 + + + 评估 + + + + + + :/pyqt/source/images/lc_dataarearefresh.png:/pyqt/source/images/lc_dataarearefresh.png + + + 测试 + + + 测试 + + + + + + :/pyqt/source/images/lc_save.png:/pyqt/source/images/lc_save.png + + + 导出数据集 + + + 导出数据集 + + + + + + :/pyqt/source/images/lc_dbqueryrename.png:/pyqt/source/images/lc_dbqueryrename.png + + + 重命名 + + + 重命名数据集 + + + + + 行筛选 + + + + + + :/pyqt/source/images/lc_dia.png:/pyqt/source/images/lc_dia.png + + + 结果 + + + + + + :/pyqt/source/images/lc_dbsortingandgrouping.png:/pyqt/source/images/lc_dbsortingandgrouping.png + + + 排序 + + + + + + :/pyqt/source/images/lc_insertplugin.png:/pyqt/source/images/lc_insertplugin.png + + + Python包管理工具 + + + Python包管理工具 + + + + + + :/pyqt/source/images/jupyter.png:/pyqt/source/images/jupyter.png + + + jupyter_notebook + + + + + Windows + + + + + Fusion + + + + + QDarkStyle + + + + + + :/pyqt/source/images/python.png:/pyqt/source/images/python.png + + + ipython + + + + + 直方图 + + + + Microsoft YaHei UI + + + + + + WindowsVista + + + + + + :/pyqt/source/images/lc_arrowshapes.right-arrow-callout.png:/pyqt/source/images/lc_arrowshapes.right-arrow-callout.png + + + 隐藏侧边栏 + + + 隐藏侧边栏 + + + + + true + + + true + + + 工作区间 + + + + + true + + + true + + + 任务列表 + + + + + true + + + true + + + 工具窗口 + + + + + 最小化到托盘 + + + + + 信息提示 + + + + + 成功消息 + + + + + 警告消息 + + + + + 错误消息 + + + + + + ConsoleWidget + QWidget +
pyminer.ui.base.widgets.consolewidget
+ 1 +
+ + PMToolBarTop + QToolBar +
pyminer.ui.base.widgets.menu_tool_stat_bars.h
+
+ + PMToolBarRight + QToolBar +
pyminer.ui.base.widgets.menu_tool_stat_bars.h
+
+ + PMTableWidget + QTableWidget +
pyminer.ui.base.widgets.tablewidget
+
+ + PMFlowWidget + QWidget +
pyminer.ui.base.widgets.flowwidget.h
+ 1 +
+ + PMPageData + QWidget +
pyminer.ui.base.widgets.controlpanel.h
+ 1 +
+ + PMPageStats + QWidget +
pyminer.ui.base.widgets.controlpanel.h
+ 1 +
+ + PMPagePlot + QWidget +
pyminer.ui.base.widgets.controlpanel.h
+ 1 +
+ + PMPageModel + QWidget +
pyminer.ui.base.widgets.controlpanel.h
+ 1 +
+ + PMPage + QWidget +
pyminer.ui.base.widgets.controlpanel.h
+ 1 +
+ + PMMenuBar + QMenuBar +
pyminer.ui.base.widgets.menu_tool_stat_bars.h
+
+ + PMFilesTreeview + QTreeView +
pyminer.ui.base.widgets.treeviews.h
+
+ + PMDatasetsTreeview + QTreeWidget +
pyminer.ui.base.widgets.treeviews.h
+
+ + PMReportWidget + QWidget +
pyminer.ui.base.widgets.reportwidget.h
+ 1 +
+
+ + + + + + action_quit + triggered() + MainWindow + close() + + + -1 + -1 + + + 399 + 299 + + + + +
diff --git a/pyminer/ui/base/widgets/codeeditwidget.py b/pyminer/ui/base/widgets/codeeditwidget.py new file mode 100644 index 0000000000000000000000000000000000000000..75c9a964442cc91c73602bd80851cf831bc58e80 --- /dev/null +++ b/pyminer/ui/base/widgets/codeeditwidget.py @@ -0,0 +1,740 @@ +#!/usr/bin/env python3 +''' +来源: +https://blog.csdn.net/xiaoyangyang20/article/details/68923133 +''' +import os +import sys +from typing import List + +sys.path.append('/media/hzy/程序/novalide/forgitcommit/pyminer/pyminer') + +from PyQt5.QtCore import QEvent, QFile, QFileInfo, QIODevice, QRegExp, QTextStream, Qt +from PyQt5.QtWidgets import QAction, QApplication, QFileDialog, QMainWindow, QMessageBox, QTextEdit, QTabWidget, \ + QVBoxLayout, QMessageBox, QWidget, QPushButton, QMenu, QToolBar, QToolButton +from PyQt5.QtGui import QFont, QIcon, QColor, QKeySequence, QSyntaxHighlighter, QTextCharFormat, QTextCursor, QCursor, \ + QKeyEvent, QPixmap + +# import pyqtresource_rc + + +__version__ = "1.1.0" + +def create_icon(icon_path: str = ":/pyqt/source/images/New.png"): + icon = QIcon() + icon.addPixmap(QPixmap(icon_path), QIcon.Normal, QIcon.Off) + return icon + +class PushButtonPane(QWidget): + def __init__(self): + super().__init__() + self.layout = QVBoxLayout() + self.layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(self.layout) + + def add_buttons(self, button_num: int = 2, text: list = None, icon_path: list = None, + menu: list = None) -> List[QPushButton]: + if text is None: + text = [''] * button_num + if icon_path == None: + icon_path = [None] * button_num + if menu == None: + menu = [None] * button_num + if len(text) != button_num or len(icon_path) != button_num or len(menu) != button_num: + raise Exception('text,icon和menu参数都必须为长为2的可迭代对象。') + if button_num == 2: + height = 30 + font_size = 12 + else: + height = 25 + font_size = 12 + btn_list = [] + for i in range(button_num): + b = self.add_button(text=text[i], icon=create_icon(icon_path[i]), menu=menu[i], height=height, + font_size=font_size) + btn_list.append(b) + return btn_list + + def add_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None, height: int = 30, + font_size: int = 14) -> QPushButton: + b = QPushButton() + b.setText(text) + if icon is not None: + b.setIcon(icon) + if menu is not None: + b.setMenu(menu) + b.setStyleSheet( + 'QPushButton{border:0px;font-size:%dpx;padding:2px 2px;width:80px;height:%dpx;text-align:left;}' % ( + font_size, height) + \ + 'QPushButton:hover{background-color:#ededed;}') + self.layout.addWidget(b) + return b + +class PMToolBar(QToolBar): + tab_button: QPushButton = None + widget_dic = {} + + def __init__(self): + super().__init__() + self.setFixedHeight(90) + + def add_tool_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None): + tb = QToolButton() + tb.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + tb.setText(text) + tb.setStyleSheet('QToolButton{height:60px;width:50px;border:0px;}QToolButton::hover{background-color:#ededed;}') + if QIcon is not None: + tb.setIcon(icon) + self.addWidget(tb) + return tb + +class PythonHighlighter(QSyntaxHighlighter): + Rules = [] + Formats = {} + + def __init__(self, parent=None): + super(PythonHighlighter, self).__init__(parent) + + self.initializeFormats() + + KEYWORDS = ["and", "as", "assert", "break", "class", + "continue", "def", "del", "elif", "else", "except", + "exec", "finally", "for", "from", "global", "if", + "import", "in", "is", "lambda", "not", "or", "pass", + "print", "raise", "return", "try", "while", "with", + "yield"] + BUILTINS = ["abs", "all", "any", "basestring", "bool", + "callable", "chr", "classmethod", "cmp", "compile", + "complex", "delattr", "dict", "dir", "divmod", + "enumerate", "eval", "execfile", "exit", "file", + "filter", "float", "frozenset", "getattr", "globals", + "hasattr", "hex", "id", "int", "isinstance", + "issubclass", "iter", "len", "list", "locals", "map", + "max", "min", "object", "oct", "open", "ord", "pow", + "property", "range", "reduce", "repr", "reversed", + "round", "set", "setattr", "slice", "sorted", + "staticmethod", "str", "sum", "super", "tuple", "type", + "vars", "zip"] + CONSTANTS = ["False", "True", "None", "NotImplemented", + "Ellipsis"] + + PythonHighlighter.Rules.append((QRegExp( + "|".join([r"\b%s\b" % keyword for keyword in KEYWORDS])), + "keyword")) + PythonHighlighter.Rules.append((QRegExp( + "|".join([r"\b%s\b" % builtin for builtin in BUILTINS])), + "builtin")) + PythonHighlighter.Rules.append((QRegExp( + "|".join([r"\b%s\b" % constant + for constant in CONSTANTS])), "constant")) + PythonHighlighter.Rules.append((QRegExp( + r"\b[+-]?[0-9]+[lL]?\b" + r"|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b" + r"|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"), + "number")) + PythonHighlighter.Rules.append((QRegExp( + r"\bPyQt4\b|\bQt?[A-Z][a-z]\w+\b"), "pyqt")) + PythonHighlighter.Rules.append((QRegExp(r"\b@\w+\b"), + "decorator")) + stringRe = QRegExp(r"""(?:'[^']*'|"[^"]*")""") + stringRe.setMinimal(True) + PythonHighlighter.Rules.append((stringRe, "string")) + self.stringRe = QRegExp(r"""(:?"["]".*"["]"|'''.*''')""") + self.stringRe.setMinimal(True) + PythonHighlighter.Rules.append((self.stringRe, "string")) + self.tripleSingleRe = QRegExp(r"""'''(?!")""") + self.tripleDoubleRe = QRegExp(r'''"""(?!')''') + + @staticmethod + def initializeFormats(): + baseFormat = QTextCharFormat() + baseFormat.setFontFamily("courier") + baseFormat.setFontPointSize(12) + for name, color in (("normal", Qt.black), + ("keyword", Qt.darkBlue), ("builtin", Qt.darkRed), + ("constant", Qt.darkGreen), + ("decorator", Qt.darkBlue), ("comment", Qt.darkGreen), + ("string", Qt.darkYellow), ("number", Qt.darkMagenta), + ("error", Qt.darkRed), ("pyqt", Qt.darkCyan)): + format = QTextCharFormat(baseFormat) + format.setForeground(QColor(color)) + if name in ("keyword", "decorator"): + format.setFontWeight(QFont.Bold) + if name == "comment": + format.setFontItalic(True) + PythonHighlighter.Formats[name] = format + + def highlightBlock(self, text): + NORMAL, TRIPLESINGLE, TRIPLEDOUBLE, ERROR = range(4) + + textLength = len(text) + prevState = self.previousBlockState() + + self.setFormat(0, textLength, + PythonHighlighter.Formats["normal"]) + + if text.startswith("Traceback") or text.startswith("Error: "): + self.setCurrentBlockState(ERROR) + self.setFormat(0, textLength, + PythonHighlighter.Formats["error"]) + return + if (prevState == ERROR and + not (text.startswith(sys.ps1) or text.startswith("#"))): + self.setCurrentBlockState(ERROR) + self.setFormat(0, textLength, + PythonHighlighter.Formats["error"]) + return + + for regex, format in PythonHighlighter.Rules: + i = regex.indexIn(text) + while i >= 0: + length = regex.matchedLength() + self.setFormat(i, length, + PythonHighlighter.Formats[format]) + i = regex.indexIn(text, i + length) + + # Slow but good quality highlighting for comments. For more + # speed, comment this out and add the following to __init__: + # PythonHighlighter.Rules.append((QRegExp(r"#.*"), "comment")) + if not text: + pass + elif text[0] == "#": + self.setFormat(0, len(text), + PythonHighlighter.Formats["comment"]) + else: + stack = [] + for i, c in enumerate(text): + if c in ('"', "'"): + if stack and stack[-1] == c: + stack.pop() + else: + stack.append(c) + elif c == "#" and len(stack) == 0: + self.setFormat(i, len(text), + PythonHighlighter.Formats["comment"]) + break + + self.setCurrentBlockState(NORMAL) + + if self.stringRe.indexIn(text) != -1: + return + # This is fooled by triple quotes inside single quoted strings + for i, state in ((self.tripleSingleRe.indexIn(text), + TRIPLESINGLE), + (self.tripleDoubleRe.indexIn(text), + TRIPLEDOUBLE)): + if self.previousBlockState() == state: + if i == -1: + i = text.length() + self.setCurrentBlockState(state) + self.setFormat(0, i + 3, + PythonHighlighter.Formats["string"]) + elif i > -1: + self.setCurrentBlockState(state) + self.setFormat(i, text.length(), + PythonHighlighter.Formats["string"]) + + def rehighlight(self): + QApplication.setOverrideCursor(QCursor( + Qt.WaitCursor)) + QSyntaxHighlighter.rehighlight(self) + QApplication.restoreOverrideCursor() + + +class PMCodeEdit(QTextEdit): + + def __init__(self, parent=None): + super(PMCodeEdit, self).__init__(parent) + self.doc_tab_widget = parent + self.filename = '*' + self.path = '' + self.modified = True + + # self.selectionChanged.connect(self.updateUi) + # self.document().modificationChanged.connect(self.on_text_changed) + # QApplication.clipboard().dataChanged.connect(self.updateUi) + self.highlighter = PythonHighlighter(self.document()) + self.setText('aaaaaaaaaaaaaa\ndaaaaaaaaaaaa') + self.setTabChangesFocus(False) + # self.change + self.textChanged.connect(self.on_text_changed) + # self.textChanged.conn/ect(lambda :print('text changed!!')) + + def on_text_changed(self): + print('text changed', self.modified) + if self.modified == True: + return + else: + self.modified = True + self.updateUi() + + def updateUi(self): + self.doc_tab_widget.refresh() + self.doc_tab_widget.is_all_files_saved() + # self.fileSaveAction.setEnabled(self.document().isModified()) + # enable = not self.editor.document().isEmpty() + # self.fileSaveAsAction.setEnabled(enable) + # self.editIndentAction.setEnabled(enable) + # self.editUnindentAction.setEnabled(enable) + # enable = self.editor.textCursor().hasSelection() + # self.editCopyAction.setEnabled(enable) + # self.editCutAction.setEnabled(enable) + # self.editPasteAction.setEnabled(self.editor.canPaste()) + return + + def keyPressEvent(self, event: QKeyEvent) -> None: + # print(event.modifiers() == Qt.ShiftModifier, event.key() == Qt.Key_Tab, event.key() == Qt.Key_CapsLock, + # event.key() == Qt.Key_Backtab) + if event.key() == Qt.Key_Tab: + self.on_tab() + return + if event.key() == Qt.Key_Backtab: + self.on_back_tab() + return + if event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier: + self.save() + return + super().keyPressEvent(event) + + # def textChanged(self) -> None: + # self.modified = True + # print(self.modified, 'aaaaaaaaaaaaaaa') + + def on_back_tab(self): + # print('shift+tab(backtab)') + cursor = self.textCursor() + if cursor.hasSelection(): + # print('has selection') + self.editUnindent() + + else: + cursor = self.textCursor() + cursor.clearSelection() + + cursor.movePosition(QTextCursor.StartOfBlock) + + for i in range(4): + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, 1) + if not cursor.selectedText().endswith(' '): + cursor.movePosition(QTextCursor.PreviousCharacter, + QTextCursor.KeepAnchor, 1) + break + # print('cursor.selected',cursor.selectedText()) + cursor.removeSelectedText() + + def on_tab(self): + cursor = self.textCursor() + if cursor.hasSelection(): + # print('has selection') + self.editIndent() + return + # return True + else: + cursor = self.textCursor() + cursor.insertText(" ") + + def editIndent(self): + cursor = self.textCursor() + cursor.beginEditBlock() + if cursor.hasSelection(): + start = pos = cursor.anchor() + start_line = self.document().findBlock(start) + end = cursor.position() + + if start > end: + start, end = end, start + pos = start + cursor.clearSelection() + + cursor.setPosition(end) + cursor.movePosition(QTextCursor.StartOfLine) + end = cursor.position() + # print(end) + cursor.setPosition(start) + cursor.movePosition(QTextCursor.StartOfLine) + start = cursor.position() + + cursor.setPosition(end) + while pos >= start: + # print(pos, start) + cursor.insertText(" ") + + cursor.movePosition(QTextCursor.Up) + cursor.movePosition(QTextCursor.StartOfLine) + lastPos = pos + pos = cursor.position() + if lastPos == pos: + break + + print('end loop', pos, start) + cursor.setPosition(start) + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, end - start) + cursor.endEditBlock() + return True + + def editUnindent(self): + cursor = self.textCursor() + cursor.beginEditBlock() + if cursor.hasSelection(): + start = pos = cursor.anchor() + end = cursor.position() + if start > end: + start, end = end, start + pos = start + cursor.clearSelection() + cursor.setPosition(start) + cursor.movePosition(QTextCursor.StartOfLine) + start = cursor.position() + cursor.setPosition(end) + cursor.movePosition(QTextCursor.StartOfLine) + end = cursor.position() + print(start, end) + while pos >= start: + # print('a',start,pos) + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, 4) + if cursor.selectedText() == " ": + cursor.removeSelectedText() + cursor.movePosition(QTextCursor.Up) + cursor.movePosition(QTextCursor.StartOfLine) + lastpos = pos + pos = cursor.position() + if pos == lastpos: + break + cursor.setPosition(start) + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, end - start) + + cursor.endEditBlock() + + def save(self): + print(self.toPlainText()) + if not os.path.isfile(self.path): + print(os.getcwd()) + path = QFileDialog.getSaveFileName(self, "选择保存的文件", '/home/hzy/Desktop', filter='*.py')[0] + print('path', path) + if not path: # 说明对话框被关闭,未选择文件,则直接返回。 + return + if not path.endswith('.py'): + path += '.py' + self.path = path + self.filename = os.path.basename(path) + + with open(self.path, 'w') as f: + f.write(self.toPlainText()) + self.modified = False + self.updateUi() + + def on_close_request(self): + if self.modified == True: + answer = QMessageBox.question(self, '保存文件', '%s有未保存的更改,是否要保存?' % self.filename, + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + if answer == QMessageBox.No: + return + else: + self.save() + + +class PMCodeEditTabWidget(QTabWidget): + def __init__(self): + super().__init__() + self.show_directly = True + + def init_toolbar(self): + ''' + 新建一个toolbar并且插入到主界面中。 + Returns: + + ''' + toolbar = PMToolBar() + toolbar.add_tool_button('新建\n脚本', create_icon(':/pyqt/source/images/lc_newdoc.png')) + toolbar.add_tool_button('打开\n脚本', create_icon(':/pyqt/source/images/lc_open.png')) + toolbar.add_tool_button('打开\n脚本', create_icon(':/pyqt/source/images/lc_save.png')) + pp = PushButtonPane() + pp.add_buttons(3, ['查找文件', '剪贴板', '打印'], + [":/pyqt/source/images/lc_searchdialog.png", ":/pyqt/source/images/lc_pickthrough.png", + ':/pyqt/source/images/lc_print.png']) + toolbar.addWidget(pp) + toolbar.addSeparator() + + pp = PushButtonPane() + buttons=pp.add_buttons(3, ['查找', '替换', '跳转到行'], + [":/pyqt/source/images/lc_searchdialog.png", ":/pyqt/source/images/lc_pickthrough.png", + ':/pyqt/source/images/lc_print.png']) + buttons[0].clicked.connect(lambda :print('查找!')) + buttons[1].clicked.connect(lambda :print('替换!')) + buttons[2].clicked.connect(lambda :print('跳转!')) + toolbar.addWidget(pp) + + pp = PushButtonPane() + pp.add_buttons(2, ['批量注释', '缩进'], + [":/pyqt/source/images/lc_searchdialog.png", ":/pyqt/source/images/lc_pickthrough.png"]) + toolbar.addWidget(pp) + pp = PushButtonPane() + pp.add_buttons(2, ['取消注释', '减少缩进'], + [":/pyqt/source/images/lc_searchdialog.png", ":/pyqt/source/images/lc_pickthrough.png"]) + toolbar.addWidget(pp) + toolbar.addSeparator() + toolbar.add_tool_button('运行', create_icon(':/pyqt/source/images/run_exc.png')) + self.toolbar = toolbar + + + def setup_ui(self): + self.init_toolbar() + + from pyminer.pmutil import get_main_window + mw = get_main_window() + mw.add_toolbar('toolbar_editor', self.toolbar, text='编辑器') + self.editor_tabs = [PMCodeEdit(self), PMCodeEdit(self), PMCodeEdit(self)] + for tab in self.editor_tabs: + self.addTab(tab, tab.filename) + self.is_all_files_saved() + + self.setTabsClosable(True) + self.tabCloseRequested.connect(self.on_tab_close_request) + + def on_tab_close_request(self, close_index: int): + tab_to_close = self.widget(close_index) + tab_to_close.deleteLater() + tab_to_close.on_close_request() + self.removeTab(close_index) + + def keyPressEvent(self, a0: QKeyEvent) -> None: + return + + def is_all_files_saved(self): + for tab_id in range(self.count()): + modified = self.widget(tab_id).modified + print(modified) + print(self.widget(0), self.count()) + # cursor.movePosition(QTextCursor.NextCharacter, + # QTextCursor.KeepAnchor, 1) + + def refresh(self): + for tab_id in range(self.count()): + modified = self.widget(tab_id).modified + filename = self.widget(tab_id).filename + if not modified: + self.setTabText(tab_id, filename) + else: + if filename != '*': + self.setTabText(tab_id, filename + ' *') + print(self.widget(0), self.count()) + + +# from pyminer.ui.base.widgets.menu_tool_stat_bars import PMModernToolbar + + +class MainWindow(QMainWindow): + + def __init__(self, filename=None, parent=None): + super(MainWindow, self).__init__(parent) + + self.setCentralWidget(PMCodeEditTabWidget()) # PMCodeEditTabWidget()) + # font = QFont("Courier", 11) + # font.setFixedPitch(True) + # self.editor = TextEdit() + # self.editor.setFont(font) + # self.highlighter = PythonHighlighter(self.editor.document()) + # self.setCentralWidget(self.editor) + # + # status = self.statusBar() + # status.setSizeGripEnabled(False) + # status.showMessage("Ready", 5000) + # + # fileNewAction = self.createAction("&New...", self.fileNew, + # QKeySequence.New, "filenew", "Create a Python file") + # fileOpenAction = self.createAction("&Open...", self.fileOpen, + # QKeySequence.Open, "fileopen", + # "Open an existing Python file") + # self.fileSaveAction = self.createAction("&Save", self.fileSave, + # QKeySequence.Save, "filesave", "Save the file") + # self.fileSaveAsAction = self.createAction("Save &As...", + # self.fileSaveAs, icon_path="filesaveas", + # tip="Save the file using a new name") + # fileQuitAction = self.createAction("&Quit", self.close, + # "Ctrl+Q", "filequit", "Close the application") + # self.editCopyAction = self.createAction("&Copy", + # self.editor.copy, QKeySequence.Copy, "editcopy", + # "Copy text to the clipboard") + # self.editCutAction = self.createAction("Cu&t", self.editor.cut, + # QKeySequence.Cut, "editcut", + # "Cut text to the clipboard") + # self.editPasteAction = self.createAction("&Paste", + # self.editor.paste, QKeySequence.Paste, "editpaste", + # "Paste in the clipboard's text") + # self.editIndentAction = self.createAction("&Indent", + # self.editIndent, "Ctrl+]", "editindent", + # "Indent the current line or selection") + # self.editUnindentAction = self.createAction("&Unindent", + # self.editUnindent, "Ctrl+[", "editunindent", + # "Unindent the current line or selection") + # + # fileMenu = self.menuBar().addMenu("&File") + # self.addActions(fileMenu, (fileNewAction, fileOpenAction, + # self.fileSaveAction, self.fileSaveAsAction, None, + # fileQuitAction)) + # editMenu = self.menuBar().addMenu("&Edit") + # self.addActions(editMenu, (self.editCopyAction, + # self.editCutAction, self.editPasteAction, None, + # self.editIndentAction, self.editUnindentAction)) + # fileToolbar = self.addToolBar("File") + # fileToolbar.setObjectName("FileToolBar") + # self.addActions(fileToolbar, (fileNewAction, fileOpenAction, + # self.fileSaveAction)) + # editToolbar = self.addToolBar("Edit") + # editToolbar.setObjectName("EditToolBar") + # self.addActions(editToolbar, (self.editCopyAction, + # self.editCutAction, self.editPasteAction, None, + # self.editIndentAction, self.editUnindentAction)) + # + # + # + # self.resize(800, 600) + # self.setWindowTitle("Python Editor") + # self.filename = filename + # if self.filename is not None: + # self.loadFile() + # self.updateUi() + + def updateUi(self, arg=None): + self.fileSaveAction.setEnabled( + self.editor.document().isModified()) + enable = not self.editor.document().isEmpty() + self.fileSaveAsAction.setEnabled(enable) + self.editIndentAction.setEnabled(enable) + self.editUnindentAction.setEnabled(enable) + enable = self.editor.textCursor().hasSelection() + self.editCopyAction.setEnabled(enable) + self.editCutAction.setEnabled(enable) + self.editPasteAction.setEnabled(self.editor.canPaste()) + + def createAction(self, text, slot=None, shortcut=None, icon=None, + tip=None, checkable=False, signal="triggered()"): + action = QAction(text, self) + if icon is not None: + action.setIcon(QIcon(":/{0}.png".format(icon))) + if shortcut is not None: + action.setShortcut(shortcut) + if tip is not None: + action.setToolTip(tip) + action.setStatusTip(tip) + if slot is not None: + action.triggered.connect(slot) + if checkable: + action.setCheckable(True) + return action + + def addActions(self, target, actions): + for action in actions: + if action is None: + target.addSeparator() + else: + target.addAction(action) + + def closeEvent(self, event): + if not self.okToContinue(): + event.ignore() + + def okToContinue(self): + if self.editor.document().isModified(): + reply = QMessageBox.question(self, + "Python Editor - Unsaved Changes", + "Save unsaved changes?", + QMessageBox.Yes | QMessageBox.No | + QMessageBox.Cancel) + if reply == QMessageBox.Cancel: + return False + elif reply == QMessageBox.Yes: + return self.fileSave() + return True + + def fileNew(self): + if not self.okToContinue(): + return + document = self.editor.document() + document.clear() + document.setModified(False) + self.filename = None + self.setWindowTitle("Python Editor - Unnamed") + self.updateUi() + + def fileOpen(self): + if not self.okToContinue(): + return + dir = (os.path.dirname(self.filename) + if self.filename is not None else ".") + fname = str(QFileDialog.getOpenFileName(self, + "Python Editor - Choose File", dir, + "Python files (*.py *.pyw)")[0]) + if fname: + self.filename = fname + self.loadFile() + + def loadFile(self): + fh = None + try: + fh = QFile(self.filename) + if not fh.open(QIODevice.ReadOnly): + raise IOError(str(fh.errorString())) + stream = QTextStream(fh) + stream.setCodec("UTF-8") + self.editor.setPlainText(stream.readAll()) + self.editor.document().setModified(False) + except EnvironmentError as e: + QMessageBox.warning(self, "Python Editor -- Load Error", + "Failed to load {0}: {1}".format(self.filename, e)) + finally: + if fh is not None: + fh.close() + self.setWindowTitle("Python Editor - {0}".format( + QFileInfo(self.filename).fileName())) + + def fileSave(self): + if self.filename is None: + return self.fileSaveAs() + fh = None + try: + fh = QFile(self.filename) + if not fh.open(QIODevice.WriteOnly): + raise IOError(str(fh.errorString())) + stream = QTextStream(fh) + stream.setCodec("UTF-8") + stream << self.editor.toPlainText() + self.editor.document().setModified(False) + except EnvironmentError as e: + QMessageBox.warning(self, "Python Editor -- Save Error", + "Failed to save {0}: {1}".format(self.filename, e)) + return False + finally: + if fh is not None: + fh.close() + return True + + def fileSaveAs(self): + filename = self.filename if self.filename is not None else "." + filename, filetype = QFileDialog.getSaveFileName(self, + "Python Editor -- Save File As", filename, + "Python files (*.py *.pyw)") + if filename: + self.filename = filename + self.setWindowTitle("Python Editor - {0}".format( + QFileInfo(self.filename).fileName())) + return self.fileSave() + return False + + +if __name__ == '__main__': + + app = QApplication(sys.argv) + print(4465) + app.setWindowIcon(QIcon(":/icon_path.png")) + fname = None + + if len(sys.argv) > 1: + fname = sys.argv[1] + form = MainWindow(fname) + form.show() + app.exec_() diff --git a/pyminer/ui/base/widgets/consolewidget.py b/pyminer/ui/base/widgets/consolewidget.py index 1c1a19cdd0f90a328f0f92cdb65d8c1fe77ba7fb..29f0b0b7a0a39ddd8f3439f0a0836dce1b46c80e 100644 --- a/pyminer/ui/base/widgets/consolewidget.py +++ b/pyminer/ui/base/widgets/consolewidget.py @@ -48,6 +48,8 @@ class ConsoleWidget(RichJupyterWidget): def __init__(self, *args, **kwargs): super(ConsoleWidget, self).__init__(*args, **kwargs) + + def setup_ui(self): self.kernel_manager = None self.kernel_client = None # initialize by thread diff --git a/pyminer/ui/base/widgets/controlpanel.py b/pyminer/ui/base/widgets/controlpanel.py index e49b478df87c4f8e89bbf96a38c720852ff3300a..05222382b072c59364ae8e5d8609e764c2264fcf 100644 --- a/pyminer/ui/base/widgets/controlpanel.py +++ b/pyminer/ui/base/widgets/controlpanel.py @@ -2,13 +2,13 @@ import os import webbrowser # 也就是widget_right -from PyQt5.QtWidgets import QWidget, QGridLayout, QSpacerItem, QSizePolicy, QMainWindow, QListWidget, QListWidgetItem, QHBoxLayout, QLabel, QPushButton, QFileDialog, QVBoxLayout, QMenu +from PyQt5.QtWidgets import QWidget, QGridLayout, QSpacerItem, QSizePolicy, QMainWindow, QListWidget, QListWidgetItem, \ + QHBoxLayout, QLabel, QPushButton, QFileDialog, QVBoxLayout, QMenu, QScrollArea from PyQt5.QtGui import QIcon, QPixmap, QCursor from PyQt5.QtCore import QSize, Qt, QCoreApplication, QSize from pyminer.features.preprocess.PMToolButton import PMToolButton - from pyminer.pmutil import get_root_dir from pyminer.features.extensions.extensions_manager.manager import extensions_manager @@ -17,9 +17,13 @@ class ControlPanel(): pass + class PMPageData(QWidget): def __init__(self): super().__init__() + + def setup_ui(self): + # self.setMaximumHeight(300) from .resources import icon_lc_connectorcurve, icon_transition_random, icon_formfilternavigator, \ icon_data_provider, icon_lc_togglemergecells, icon_lc_datadatapilotrun, icon_delete_columns, \ icon_mergedocuments, icon_lc_accepttrackedchange, \ @@ -836,6 +840,7 @@ class PMPageExt(QWidget): main_window:主窗口 ''' super().__init__() + self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.main_window = main_window self.ext_manager = extensions_manager self.init_ui() @@ -849,6 +854,7 @@ class PMPageExt(QWidget): filter='所有文件(*.*);;zip文件(*.zip)', initialFilter='zip文件(*.zip)' ) + print(path, _) self.ext_manager.install(path) # 刷新扩展列表 self.init_extensions() @@ -857,7 +863,7 @@ class PMPageExt(QWidget): self.ext_list.clear() # 先清空 for ext in self.ext_manager.extensions: item = QListWidgetItem(self.ext_list, 0) - item.setSizeHint(QSize(self.ext_list.width()-20, 50)) + item.setSizeHint(QSize(self.ext_list.width() - 20, 50)) w = ExtInfoWidget(self, ext, self.ext_manager) self.ext_list.addItem(item) self.ext_list.setItemWidget(item, w) diff --git a/pyminer/ui/base/widgets/generalwidgets/buttons/__init__.py b/pyminer/ui/base/widgets/generalwidgets/buttons/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70baa606ea9e2a456a13d89fa36b7ddbaec9f51b --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/buttons/__init__.py @@ -0,0 +1 @@ +from .pmpushbuttons import PushButtonPane \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/buttons/pmpushbuttons.py b/pyminer/ui/base/widgets/generalwidgets/buttons/pmpushbuttons.py new file mode 100644 index 0000000000000000000000000000000000000000..dad2208816d7a9eb8096303b627609810711496e --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/buttons/pmpushbuttons.py @@ -0,0 +1,52 @@ +from typing import List + +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QMenu + +from pyminer.ui.base.widgets.generalwidgets.sourcemgr import create_icon + + +class PushButtonPane(QWidget): + def __init__(self): + super().__init__() + self.layout = QVBoxLayout() + self.layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(self.layout) + + def add_buttons(self, button_num: int = 2, text: list = None, icon_path: list = None, + menu: list = None) -> List[QPushButton]: + if text is None: + text = [''] * button_num + if icon_path == None: + icon_path = [None] * button_num + if menu == None: + menu = [None] * button_num + if len(text) != button_num or len(icon_path) != button_num or len(menu) != button_num: + raise Exception('text,icon和menu参数都必须为长为2的可迭代对象。') + if button_num == 2: + height = 30 + font_size = 12 + else: + height = 25 + font_size = 12 + btn_list = [] + for i in range(button_num): + b = self.add_button(text=text[i], icon=create_icon(icon_path[i]), menu=menu[i], height=height, + font_size=font_size) + btn_list.append(b) + return btn_list + + def add_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None, height: int = 30, + font_size: int = 14) -> QPushButton: + b = QPushButton() + b.setText(text) + if icon is not None: + b.setIcon(icon) + if menu is not None: + b.setMenu(menu) + b.setStyleSheet( + 'QPushButton{border:0px;font-size:%dpx;padding:2px 2px;width:80px;height:%dpx;text-align:left;}' % ( + font_size, height) + \ + 'QPushButton:hover{background-color:#ededed;}') + self.layout.addWidget(b) + return b \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/containers/PMTab.py b/pyminer/ui/base/widgets/generalwidgets/containers/PMTab.py new file mode 100644 index 0000000000000000000000000000000000000000..9357ee1c4b7e6ce9d238895867f48cc9f4fbd59d --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/containers/PMTab.py @@ -0,0 +1,22 @@ +from PyQt5.QtWidgets import QTabWidget, QWidget, QScrollArea + + +class PMTabWidget(QTabWidget): + def setup_ui(self): + for tab_id in range(self.count()): # 遍历所有的tab + w = self.widget(tab_id) + if hasattr(w, 'setup_ui'): + self.widget(tab_id).setup_ui() + + def addScrolledAreaTab(self, widget: QWidget, a1: str) -> int: + ''' + 添加使用QScrollArea包裹的Tab。 + :param widget: + :param a1: + :return: + ''' + from pyminer.ui.base.widgets.generalwidgets.containers import PMScrollArea + scroll = PMScrollArea() + scroll.setWidget(widget) + + super().addTab(scroll, a1) diff --git a/pyminer/ui/base/widgets/generalwidgets/containers/__init__.py b/pyminer/ui/base/widgets/generalwidgets/containers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6aabb372e099aa38c080b37c58e67faaf61d205c --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/containers/__init__.py @@ -0,0 +1,2 @@ +from .PMTab import PMTabWidget +from .pmscrollarea import PMScrollArea \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/containers/pmscrollarea.py b/pyminer/ui/base/widgets/generalwidgets/containers/pmscrollarea.py new file mode 100644 index 0000000000000000000000000000000000000000..f60f00be5308002728ffee1d190464ef680b2b7b --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/containers/pmscrollarea.py @@ -0,0 +1,13 @@ +from PyQt5.QtWidgets import QScrollArea +from PyQt5.QtCore import Qt + +class PMScrollArea(QScrollArea): + def __init__(self): + super().__init__() + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.setWidgetResizable(True) + + def setup_ui(self): + if hasattr(self.widget(),'setup_ui'): + self.widget().setup_ui() + # self.page_data.setup_ui() \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/sourcemgr/__init__.py b/pyminer/ui/base/widgets/generalwidgets/sourcemgr/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16adf940f3133bfa6bee16fc96ec17e32a4e1fd3 --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/sourcemgr/__init__.py @@ -0,0 +1 @@ +from .iconutils import create_icon \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/sourcemgr/iconutils.py b/pyminer/ui/base/widgets/generalwidgets/sourcemgr/iconutils.py new file mode 100644 index 0000000000000000000000000000000000000000..ec7cc9060a2939e27046bdf11533ff8c0d84c4b9 --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/sourcemgr/iconutils.py @@ -0,0 +1,7 @@ +from PyQt5.QtGui import QIcon, QPixmap + + +def create_icon(icon_path: str = ":/pyqt/source/images/New.png"): + icon = QIcon() + icon.addPixmap(QPixmap(icon_path), QIcon.Normal, QIcon.Off) + return icon \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/threads/__init__.py b/pyminer/ui/base/widgets/generalwidgets/threads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pyminer/ui/base/widgets/generalwidgets/toolbars/__init__.py b/pyminer/ui/base/widgets/generalwidgets/toolbars/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f990e99a4702ffa814a17c6757943cbc9f16f871 --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/toolbars/__init__.py @@ -0,0 +1 @@ +from .toolbar import TopToolBar,PMToolBar,ActionWithMessage \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/toolbars/toolbar.py b/pyminer/ui/base/widgets/generalwidgets/toolbars/toolbar.py new file mode 100644 index 0000000000000000000000000000000000000000..9b523e641dbcbe7956a60bd52fc37b1e0f7c63d0 --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/toolbars/toolbar.py @@ -0,0 +1,45 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QToolBar, QPushButton, QMenu, QToolButton, QAction,QWidget + + +class ActionWithMessage(QAction): + def __init__(self,text:str='',icon:QIcon=None,parent:QWidget=None,message:str = ''): + super().__init__(parent) + self.setText(text) + if icon is not None: + self.setIcon(icon) + self.message=message + +class TopToolBar(QToolBar): + def __init__(self): + super().__init__() + + def add_button(self, name: str, text: str): + b = QPushButton(text) + b.setObjectName(name) + + self.addWidget(b) + + def get_button(self, name: str): + return self.findChild(QPushButton, name) + +class PMToolBar(QToolBar): + tab_button: QPushButton = None + widget_dic = {} + + def __init__(self): + super().__init__() + self.setFixedHeight(90) + + def add_tool_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None): + tb = QToolButton() + tb.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + tb.setText(text) + tb.setStyleSheet('QToolButton{height:60px;width:50px;border:0px;}QToolButton::hover{background-color:#ededed;}') + if icon is not None: + tb.setIcon(icon) + if menu is not None: + tb.setMenu(menu) + self.addWidget(tb) + return tb \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/window/__init__.py b/pyminer/ui/base/widgets/generalwidgets/window/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e5a02d4eadc1a5c131ae16a247f9c0ebe2a514fd --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/window/__init__.py @@ -0,0 +1,2 @@ +from .pmdockwidget import PMDockWidget +from .pmmainwindow import BaseMainWindow \ No newline at end of file diff --git a/pyminer/ui/base/widgets/generalwidgets/window/pmdockwidget.py b/pyminer/ui/base/widgets/generalwidgets/window/pmdockwidget.py new file mode 100644 index 0000000000000000000000000000000000000000..3fc956473164afca565ddd6a684bd30f969eba72 --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/window/pmdockwidget.py @@ -0,0 +1,22 @@ +from PyQt5.QtGui import QCloseEvent +from PyQt5.QtWidgets import QDockWidget, QMainWindow + +from pyminer.pmutil import get_main_window + + + +class PMDockWidget(QDockWidget): + def __init__(self, text='', parent: QMainWindow = None): + super().__init__(text, parent) + self.parent = parent + print(self.titleBarWidget()) + + def closeEvent(self, event: QCloseEvent): + print('close') + self.hide() + event.accept() + + # dock_widgets = get_main_window().dock_widgets + # for k in dock_widgets.keys(): + # print(k, dock_widgets[k].isVisible()) + get_main_window().refresh_view_configs() diff --git a/pyminer/ui/base/widgets/generalwidgets/window/pmmainwindow.py b/pyminer/ui/base/widgets/generalwidgets/window/pmmainwindow.py new file mode 100644 index 0000000000000000000000000000000000000000..ee691c92095d6c3ea23dc608bd916ea06ce564ac --- /dev/null +++ b/pyminer/ui/base/widgets/generalwidgets/window/pmmainwindow.py @@ -0,0 +1,136 @@ +''' +这里定义了MainWindow的基类。 +基类主要包含带选项卡工具栏的管理功能,以及浮动窗口的管理功能 +添加浮动窗口时,默认‘关闭’事件就是隐藏。如果是彻底的关闭,需要进行重写。 +每次界面关闭时,布局会被存入文件pyminer/config/qtlayout.ini之中。再次启动时,若这个文件存在,就会加载,反之不会加载。 +''' +import os +from typing import Dict + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QCloseEvent +from PyQt5.QtWidgets import QMainWindow, QToolBar, QPushButton, QMessageBox, QWidget, QMenu + +# from pyminer.pmappmodern import TopToolBar, PMToolBarHome +from pyminer.ui.base.widgets.generalwidgets.toolbars import TopToolBar, ActionWithMessage +from pyminer.ui.base.widgets.codeeditwidget import PMToolBar +from pyminer.ui.base.widgets.generalwidgets.buttons import PushButtonPane +from pyminer.ui.base.widgets.generalwidgets.window import PMDockWidget +from pyminer.pmutil import get_root_dir + + +class BaseMainWindow(QMainWindow): + toolbars: Dict[str, QToolBar] = {} + dock_widgets: Dict[str, PMDockWidget] = {} + dock_places = {'left': Qt.LeftDockWidgetArea, 'right': Qt.RightDockWidgetArea, 'top': Qt.TopDockWidgetArea, + 'bottom': Qt.BottomDockWidgetArea} + + def init_toolbar_tab(self): + tt = TopToolBar() + tt.setFloatable(False) + tt.setLayoutDirection(Qt.LeftToRight) + tt.setMovable(False) + tt.setObjectName('tab_bar_for_tool_bar') + + self.addToolBar(tt) + self.top_toolbar_tab = tt + + def switch_toolbar(self, name: str): + for k in self.toolbars.keys(): + if k == name: + self.toolbars[k].show() + self.toolbars[k].tab_button.setStyleSheet( + 'QPushButton{color:#444444;background-color:#ffffff;border:0px;width:100px;}') + else: + self.toolbars[k].hide() + self.toolbars[k].tab_button.setStyleSheet( + 'QPushButton{color:#efefef;background-color:#1234ee;border:0px;width:100px;}') + + def add_toolbar(self, name: str, toolbar: QToolBar, text: str = 'untitled toolbar'): + + b = QPushButton(text) + self.top_toolbar_tab.addWidget(b) + toolbar.tab_button = b + b.clicked.connect(lambda: self.switch_toolbar(name)) + + self.addToolBarBreak(Qt.TopToolBarArea) + self.addToolBar(toolbar) + toolbar.setObjectName(name) + self.toolbars[name] = toolbar + toolbar.setMovable(False) + toolbar.setFloatable(False) + toolbar.addWidget(PushButtonPane()) + + def save_layout(self): + + p = os.path.join(get_root_dir(), 'config', 'qtlayout.ini') + with open(p, 'wb') as f: + s = self.saveState() + f.write(s) + + def load_layout(self): + p = os.path.join(get_root_dir(), 'config', 'qtlayout.ini') + if os.path.exists(p): + with open(p, 'rb') as f: + s = f.read() + self.restoreState(s) + + def closeEvent(self, event: QCloseEvent) -> None: + + reply = QMessageBox.question(self, '注意', '确认退出吗?', QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) + if reply == QMessageBox.Ok: + event.accept() + else: + event.ignore() + + def add_widget_on_dock(self, dock_name: str, widget: QWidget, text: str = '', side='left'): + dw = PMDockWidget(text=text, parent=self) + dw.text = text + dw.setObjectName(dock_name) + dw.setWidget(widget) + if hasattr(widget, 'load_actions'): + widget.load_actions() + if hasattr(widget, 'setup_ui'): + if hasattr(widget, 'show_directly'): + if widget.show_directly: + widget.setup_ui() + else: + self.setupui_tasks.append(widget.setup_ui) + else: + self.setupui_tasks.append(widget.setup_ui) + + self.addDockWidget(self.dock_places[side], dw) + if dock_name not in self.dock_widgets.keys(): + self.dock_widgets[dock_name] = dw + else: + raise Exception('docked widget name: \'%s\' is already used!' % dock_name) + + home_toolbar = self.toolbars.get('toolbar_home') + + menu = QMenu() + menu.triggered.connect(home_toolbar.process_events) + for k in self.dock_widgets.keys(): + a = ActionWithMessage(text=self.dock_widgets[k].text, parent=home_toolbar, message=k) + + a.setCheckable(True) + + menu.addAction(a) + + home_toolbar.view_config_button.setMenu(menu) + + def refresh_view_configs(self): + home_toolbar = self.toolbars.get('toolbar_home') + menu = QMenu() + menu.triggered.connect(home_toolbar.process_events) + for k in self.dock_widgets.keys(): + a = ActionWithMessage(text=self.dock_widgets[k].text, parent=home_toolbar, message=k) + + a.setCheckable(True) + a.setChecked(self.dock_widgets[k].widget().isVisible()) + + menu.addAction(a) + + home_toolbar.view_config_button.setMenu(menu) + + def on_boot_finished(self): + self.refresh_view_configs() diff --git a/pyminer/ui/base/widgets/menu_tool_stat_bars.py b/pyminer/ui/base/widgets/menu_tool_stat_bars.py index 27331505179f8a7a8d6402dad556071095d162dd..7514ed6b6a1507c5f21f0166f8120203693a6cb8 100644 --- a/pyminer/ui/base/widgets/menu_tool_stat_bars.py +++ b/pyminer/ui/base/widgets/menu_tool_stat_bars.py @@ -1,6 +1,12 @@ -from PyQt5.QtWidgets import QMenuBar, QMenu, QMainWindow, QAction, QToolBar, QStatusBar -from PyQt5.QtGui import QFont -from PyQt5.QtCore import QRect, QCoreApplication, Qt +import os +import sys + +from PyQt5.QtWidgets import QMenuBar, QMenu, QMainWindow, QAction, QToolBar, QStatusBar, QWidget, QApplication, \ + QGridLayout, QPushButton, QSizePolicy, QTabWidget, QSpacerItem, QHBoxLayout, QVBoxLayout, QToolButton, QSplitter, \ + QLabel +from PyQt5.QtGui import QFont, QIcon, QPixmap +from PyQt5.QtCore import QRect, QCoreApplication, Qt, QSize +from pyminer.features.preprocess.PMToolButton import PMToolButton def init_actions(app): @@ -226,10 +232,11 @@ def init_actions(app): app.action_dataset_rename.setObjectName("action_dataset_rename") app.action_menu_data_row_filter = QAction(app) app.action_menu_data_row_filter.setObjectName("action_menu_data_row_filter") - app.action_menu_result = QAction(app) + app.action_menu_result = QAction(app) app.action_menu_result.setIcon(icon_lc_dia) app.action_menu_result.setObjectName("action_menu_result") + app.action_menu_sort = QAction(app) app.action_menu_sort.setIcon(icon_lc_dbsortingandgrouping) @@ -287,12 +294,135 @@ def init_actions(app): app.action_error.setObjectName("action_error") +class PMModernToolbar(QTabWidget): + def __init__(self): + super().__init__() + tab1 = PMToolbarTabHome('主页') + self.addTab(tab1, tab1.tab_name) + tab = PMModernToolbarTab('绘图') + self.addTab(tab, tab.tab_name) + tab = PMModernToolbarTab('APP') + self.addTab(tab, tab.tab_name) + self.setContentsMargins(0, 0, 0, 0) + # self.setFixedHeight(105) + # self.setMaximumHeight(120) + self.setStyleSheet("QTabWidget::pane{border-width: 0px;background-color:#ffffff}" + "QTabBar::tab{border-width: 0px;width:60px;font-size:14px;background-color:#618Bd5;height:30px;}") + # "QTabBar::tab:selected{background-color:#618Bd5;font-size:14px;border-bottom:2px solid #618BE5;}" + # "QTabBar::tab:hover{color:#918Bf5;font-size:14px;border-bottom:2px solid #618BE5;}") + + +class PMModernToolbarTab(QWidget): + def __init__(self, tab_name: str): + super().__init__() + self.tab_name = tab_name + + self.grid_layout = QGridLayout() + self.grid_layout.setContentsMargins(0, 0, 0, 0) + self.grid_layout.setAlignment(Qt.AlignTop | Qt.AlignBottom) + self.grid_layout.setSpacing(0) + layout = QHBoxLayout() + layout.addLayout(self.grid_layout) + layout.addItem(QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + self.setContentsMargins(0, 0, 0, 0) + + def add_action(self, from_row: int, from_column: int, row_span: int, column_span: int, icon_path: str = ''): + if os.path.isfile(icon_path): + b = PMToolButton(text='hahaha', icon=QIcon(icon_path)) + else: + b = PMToolButton(text='hahaha') + b.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + b.setFixedHeight(30 * row_span) + b.setFixedWidth(30 * column_span) + self.grid_layout.addWidget(b, from_row, from_column, row_span, column_span, + alignment=Qt.AlignTop | Qt.AlignBottom) + + +class PMToolbarTabHome(PMModernToolbarTab): + def __init__(self, tab_name: str): + from pyminer.ui.base.widgets.resources import icon_python, icon_lc_save, icon_New + super().__init__(tab_name) + + icon_open = QIcon() + icon_open.addPixmap( + QPixmap(":/pyqt/source/images/folder.png").scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation), + QIcon.Normal, QIcon.Off) + + icon_load_data = QIcon( + '/media/hzy/程序/novalide/forgitcommit/pyminer/pyminer/pyminer/ui' + '/source/images/dbqueryedit.png') + self.current_col = 0 + + self.add_default_sized_action(2, icon=icon_New, text='新建\n脚本') + self.add_default_sized_action(2, icon=icon_New, text='新建\n数据') + self.add_splitter(self.current_col) + self.add_default_sized_action(1, text='打开') + self.add_default_sized_action(2, text='导入\n数据', icon=icon_load_data) + self.add_default_sized_action(2, text='保存\n工作区') + self.add_default_sized_action(2, text='插件') + self.add_default_sized_action(2, text='帮助') + self.add_three_stacked_actions(3, text=['分析代码', '运行并计时', '清除命令'], icons=[icon_load_data, icon_open, icon_New]) + + def add_action(self, from_row: int, from_column: int, row_span: int = 3, column_span: int = 2, text: str = '', + icon: QIcon = None, text_under_icon=True): + if text_under_icon: + b = QToolButton() + b.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + b.setStyleSheet('QToolButton{background-color:white;border:1px;padding:2px 2px} ' + 'QToolButton:hover{background-color:#ededed}' + ) + else: + b = QPushButton() + b.setStyleSheet('QPushButton{background-color:white;border:1px;padding:2px 2px;text-align:left;} ' + 'QPushButton:hover{background-color:#ededed}' + ) + if icon is not None: + b.setIcon(icon) + b.setText(text) + + b.setFixedHeight(25 * row_span) + b.setFixedWidth(30 * column_span) + self.grid_layout.addWidget(b, from_row, from_column, row_span, column_span, + alignment=Qt.AlignTop | Qt.AlignBottom) + d = min(row_span, column_span) + + # b.setIconSize(QSize(30 * d, 30 * d)) + # spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + # self.grid_layout.addWidget(spacer,2,2) + + def add_default_sized_action(self, col_span, text: str = '', icon: QIcon = None): + self.add_action(0, self.current_col, row_span=3, column_span=col_span, text=text, icon=icon) + self.current_col += col_span + + def add_three_stacked_actions(self, col_span, text=None, icons=None): + if text is None: + text = ['', '', ''] + if icons is None: + icons = [None, None, None] + for i in range(3): + self.add_action(i, self.current_col, row_span=1, column_span=col_span, text=text[i], icon=icons[i], + text_under_icon=False) + # self.add_action(0, self.current_col, row_span=1, column_span=col_span,text=text,icon_path=icon_path) + # self.add_action(0, self.current_col, row_span=1, column_span=col_span,text=text,icon_path=icon_path) + + def add_splitter(self, xpos): + l = QLabel() + l.setFixedWidth(1) + l.setFixedHeight(75) + # l.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding) + l.setStyleSheet('QLabel{margin:3px;background-color:#cccccc;border-radius:0px;border:0px}') + self.grid_layout.addWidget(l, 0, xpos, 3, 1) + self.current_col += 1 + + class PMMenuBar(QMenuBar): def __init__(self, main_window: QMainWindow): super().__init__() self.main_window = main_window self.setGeometry(QRect(0, 0, 1366, 23)) self.setObjectName("menubar") + self.menu_file = QMenu(self) font = QFont() font.setFamily("Microsoft YaHei UI") @@ -331,7 +461,7 @@ class PMMenuBar(QMenuBar): init_actions(self.main_window) self.translate() - def bind_events(self,app): + def bind_events(self, app): self.menu_file.addAction(app.action_menu_new) self.menu_file.addAction(app.action_menu_open) self.menu_file.addSeparator() @@ -523,7 +653,7 @@ class PMToolBarTop(QToolBar): app = self.main_window self.setObjectName("toolBar_left") - def bind_events(self,app): + def bind_events(self, app): self.addAction(app.action_menu_new) self.addAction(app.action_menu_open) self.addSeparator() @@ -551,7 +681,8 @@ class PMToolBarRight(QToolBar): def __init__(self, parent): super().__init__(parent) self.main_window = parent - def bind_events(self,app): + + def bind_events(self, app): _translate = QCoreApplication.translate self.setLayoutDirection(Qt.RightToLeft) self.setObjectName("toolBar_right") @@ -573,3 +704,24 @@ class PMStatusBar(QStatusBar): self.setAutoFillBackground(False) self.setStyleSheet("background-color: rgb(240, 240, 240);") self.setObjectName("statusBar") + + +if __name__ == '__main__': + class TestWidget(QWidget): + def __init__(self): + super().__init__() + layout = QVBoxLayout() + self.setLayout(layout) + layout.addWidget(PMModernToolbar()) + layout.addWidget(QPushButton()) + + + app = QApplication(sys.argv) + + fname = None + + if len(sys.argv) > 1: + fname = sys.argv[1] + form = TestWidget() + form.show() + app.exec_() diff --git a/pyminer/ui/base/widgets/tablewidget.py b/pyminer/ui/base/widgets/tablewidget.py index df00688ab64679dfe6b06b48146773d7ce217661..8df95d53c149a1d05489d889fc54d154b045e7b3 100644 --- a/pyminer/ui/base/widgets/tablewidget.py +++ b/pyminer/ui/base/widgets/tablewidget.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import QCoreApplication class PMTableWidget(QTableWidget): - def __init__(self,parent): + def __init__(self, parent): super().__init__(parent) self.setStyleSheet("") @@ -13,6 +13,8 @@ class PMTableWidget(QTableWidget): self.setObjectName("tableWidget_dataset") self.setColumnCount(30) self.setRowCount(30) + + def setup_ui(self): item = QTableWidgetItem() self.setVerticalHeaderItem(0, item) item = QTableWidgetItem() diff --git a/pyminer/ui/base/widgets/treeviews.py b/pyminer/ui/base/widgets/treeviews.py index 4460053a1384ce44770acbd5458cce58e7aafe07..3505028d1effc8cbacb0acb49dd02b71ac7c9aeb 100644 --- a/pyminer/ui/base/widgets/treeviews.py +++ b/pyminer/ui/base/widgets/treeviews.py @@ -41,7 +41,7 @@ class PMFilesTreeview(QTreeView): # 文件管理器 # my_dir = QDir.rootPath() - my_dir = os.path.dirname(get_root_dir()) + my_dir = os.path.dirname('')#get_root_dir()) self.model = RewriteQFileSystemModel() self.model.setRootPath(my_dir) # self.setRootIndex(self.model.index(QDir.homePath()))