zoukankan      html  css  js  c++  java
  • vnpy源码阅读学习(3):学习vnpy的界面的实现

    学习vnpy的界面的实现

    通过简单的学习了PyQt5的一些代码以后,我们基本上可以理解PyQt的一些用法,下面让我们来先研究下vnpy的UI部分的代码。

    首先回到上一节看到的run.py(/vnpy/example/trade/run.py)的关于UI部分的代码。

    生成QApplication部分

    qapp = create_qapp()

    我们跟踪得到 create_qapp() 方法是写在 "/vnpy/trader/ui/init.py"上面的。

    init.py主要是把一个文件夹变成一个包,方便包的引入和管理,方法写在__init__.py中可以会在引入的时候被直接调用,也就是说不需要在调用的时候通过xxx.method()的形式来调用。init.py详细解释

    我们来看看这部分的代码

    def excepthook(exctype, value, tb):
        ## 全局的一个异常处理的钩子,所有的异常都会被处理到这里来
       
    
    def create_qapp(app_name: str = "VN Trader"):
        
        sys.excepthook = excepthook
    
        # 让窗体可以适应高分辨率屏幕
        QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    
        qapp = QtWidgets.QApplication([])
        qapp.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
        font = QtGui.QFont(SETTINGS["font.family"], SETTINGS["font.size"])
        qapp.setFont(font)
        icon = QtGui.QIcon(get_icon_path(__file__, "vnpy.ico"))
        qapp.setWindowIcon(icon)
    
        if "Windows" in platform.uname():
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                app_name
            )
    
        return qapp
    
    
    class ExceptionDialog(QtWidgets.QDialog):
        """"""
        #这里是异常窗口的代码
    

    上面的这部分代码就是简单的生成一个QApplication代码,并且指定了全局的异常发生以后弹出异常窗体。需要注意以下代码:

    if "Windows" in platform.uname():
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                app_name
            )
    

    在Windows 7中,任务栏本身不适用于“应用程序Windows”,它适用于“应用程序用户模型”。例如,如果您运行了多个不同的应用程序实例,并且每个实例都有自己的图标,那么它们将全部分组到一个任务栏图标下。 Windows使用各种启发式方法来决定是否应该对不同的实例进行分组,在这种情况下,它决定将Pythonw.exe托管的所有内容分组到Pythonw.exe的图标下。 正确的解决方案是让Pythonw.exe告诉Windows它只是托管其他应用程序。也许未来的Python版本会这样做。或者,您可以添加一个注册表项来告诉Windows,Pythonw.exe本身只是一个主机而不是一个应用程序。有关AppUserModelIDs的信息,请参阅MSDN文档。 或者,您可以使用Python的Windows调用,明确告诉Windows此进程的正确AppUserModelID:

    主窗体生成部分

    让我们接着看看UI的主窗体生成部分的代码

    main_window = MainWindow(main_engine, event_engine)
    main_window.showMaximized()
    

    MainWindows的代码位置在 /vnpy/vnpy/trader/ui/mainwindow.py

    __init__()方法中就是对常见的self的属性赋值,没什么稀奇的。我们直接看initUI()部分的代码。

    def init_ui(self):
    
            self.setWindowTitle(self.window_title) #设置标题
            self.init_dock()
            self.init_toolbar()
            self.init_menu()
            self.load_window_setting("custom")
    

    我们一个一个看看这部分的函数和功能。

    init_dock

    def init_dock(self):
            """"""
            self.trading_widget, trading_dock = self.create_dock(TradingWidget, "交易", QtCore.Qt.LeftDockWidgetArea)
            tick_widget, tick_dock = self.create_dock(TickMonitor, "行情", QtCore.Qt.RightDockWidgetArea)
            #中间省略掉N多调用create_dock的方法
            self.tabifyDockWidget(active_dock, order_dock)
    
            self.save_window_setting("default")
    

    init_dock的方法中首先调用了create_dock
    咱们来研究下create_dock的方法。

    def create_dock(
            self, widget_class: QtWidgets.QWidget, name: str, area: int
        ):
            
            widget = widget_class(self.main_engine, self.event_engine)
    
            dock = QtWidgets.QDockWidget(name)
            dock.setWidget(widget)
            dock.setObjectName(name)
            dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
            self.addDockWidget(area, dock)
            return widget, dock
    

    我们基本上可以这样理解,就是实例化了一个自定义的Widget,然后放入docker中.
    docker大概是这样的一个概念【浮动窗口】。

    我搜索到了一篇详细的教程:PyQt5高级界面控件之QDockWidget(八)

    为了联系docker我简单的写了一段代码:

    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    
    
    class DockDemo(QMainWindow):
    
        def __init__(self):
            super().__init__()
    
            tradeWidget = TradeWidget()
            self.trade_docker = QDockWidget('交易窗口', self)
            self.trade_docker.setWidget(tradeWidget)
            self.trade_docker.setFeatures(self.trade_docker.DockWidgetFloatable | self.trade_docker.DockWidgetMovable)
            self.trade_docker.setObjectName("交易窗口")
            self.trade_docker.setFloating(False)
    
            self.addDockWidget(Qt.RightDockWidgetArea,self.trade_docker)
            tickWidget = TickMonitorWidget()
            self.tick_docker = QDockWidget('行情窗口', self)
            self.tick_docker.setWidget(tickWidget)
            self.tick_docker.setFloating(False)
            self.addDockWidget(Qt.LeftDockWidgetArea, self.tick_docker)
            self.show()
    
    
    class TradeWidget(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
            self.setWindowTitle("这里是交易窗口")
            button = QPushButton('交易按钮', self)
            button.move(10, 20)
            self.show()
    
    
    class TickMonitorWidget(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
            self.setWindowTitle("这里是行情窗口")
            button = QPushButton('行情', self)
            button.move(10, 20)
            self.show()
    
    
    app = QApplication([])
    dd = DockDemo()
    exit(app.exec_())
    
    

    init_toolbar

    这个没什么好说的,就是初始化工具栏

    init_menu()

    这个方法是生成菜单,里面有一个有趣的方法把菜单和插槽连接起来

    self.add_menu_action(
                help_menu,
                "查询合约",
                "contract.ico",
                partial(self.open_widget, ContractManager, "contract"),
            )
    def add_menu_action(
            self,
            menu: QtWidgets.QMenu,
            action_name: str,
            icon_name: str,
            func: Callable,
        ):
            """"""
            icon = QtGui.QIcon(get_icon_path(__file__, icon_name))
    
            action = QtWidgets.QAction(action_name, self)
            action.triggered.connect(func)
            action.setIcon(icon)
    
            menu.addAction(action)
    

    保存windows布局和恢复布局

    def save_window_setting(self, name: str):
            """
            Save current window size and state by trader path and setting name.
            """
            settings = QtCore.QSettings(self.window_title, name)
            settings.setValue("state", self.saveState())
            settings.setValue("geometry", self.saveGeometry())
    
    
    def load_window_setting(self, name: str):
            """
            Load previous window size and state by trader path and setting name.
            """
            settings = QtCore.QSettings(self.window_title, name)
            state = settings.value("state")
            geometry = settings.value("geometry")
    
            if isinstance(state, QtCore.QByteArray):
                self.restoreState(state)
                self.restoreGeometry(geometry)
    
    def restore_window_setting(self):
            """
            Restore window to default setting.
            """
            self.load_window_setting("default")
            self.showMaximized()
    

    打开一个定义的Wiget

    def open_widget(self, widget_class: QtWidgets.QWidget, name: str):
            """
            Open contract manager.
            """
            widget = self.widgets.get(name, None)
            if not widget:
                widget = widget_class(self.main_engine, self.event_engine)
                self.widgets[name] = widget
    
            if isinstance(widget, QtWidgets.QDialog):
                widget.exec_()
            else:
                widget.show()
    

    这个简单,就是当菜单和工具栏调用打开一个功能窗口的时候,首先查找这个窗口是否在wigets方法里面。如果不的话,实例化,添加进去。然后打开.
    需要注意的是,如果是widget直接调用show,如果是dialog需要调用的是exec_()方法

  • 相关阅读:
    MD5双重加密设计
    ComBox(自定义封装)LimitToList属性和做到移走光标不是下拉项清空输入
    强制下线功能
    广播
    动态添加碎片
    RecyclerView
    Listview的运行效率
    Listview
    通知栏
    补间动画
  • 原文地址:https://www.cnblogs.com/bbird/p/12197433.html
Copyright © 2011-2022 走看看