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);
+
+
+
+
+
+ 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()))