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_()方法

原文地址:https://www.cnblogs.com/bbird/p/12197433.html

时间: 2024-10-07 12:48:02

vnpy源码阅读学习(3):学习vnpy的界面的实现的相关文章

vnpy源码阅读学习(5):关于MainEngine的代码阅读

关于MainEngine的代码阅读 在入口文件中,我们看到了除了窗体界面的产生,还有关于MainEngine和EventEngin部分.今天来学习下MainEngine的代码. 首先在run代码中,我们看到以下的代码 main_engine.add_gateway(DeribitGateway) main_engine.add_app(OptionMasterApp) 从上述代码可以基本猜测所有的网管,设置,甚至策略引擎行情,都跟MainEngine有关系,MainEngine应该是一个主线,把

vnpy源码阅读学习(7):串在一起

串在一起 我们已经分析了UI.MainEngine.EventEngine.然后他们几个是如何发挥作用的呢?我总结了一张图: 我们来具体的看看UI部分是如何跟EventEngine穿插起来的 \examples\vn_trader\run.py def main(): """""" qapp = create_qapp() event_engine = EventEngine() main_engine = MainEngine(event_en

【 js 基础 】【 源码学习 】backbone 源码阅读(三)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.这篇文章主要讲 backbone.sync 中用到的 Rest 和 CRUD. 首先我们简单了解一下 REST: REST :

【 js 基础 】【 源码学习 】backbone 源码阅读(一)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.具体从以下几个方面入手:1.MVC 框架2.观察者模式 以及 控制反转 一.MVC 框架所谓 MVC 框架,包含三个部分,mod

【 js 基础 】【 源码学习 】backbone 源码阅读(二)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.这篇文章主要讲 控制反转. 一.控制反转 上篇文章有说到控制反转,但只是简略的举了个例子,在这里我们详细说一下这个知识点,它其实

为什么C/C++程序员都要阅读Redis源码之:Redis学习事件驱动设计

为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你源码编译安装过Nginx/Grpc/Thrift/Boost等开源产品,你会发现有很多依赖,而依赖本身又有依赖,十分痛苦.通常半天一天就耗进去了.由衷地羡慕 npm/maven/pip/composer/...这些包管理器.而Redis则给人惊喜,一行make了此残生. 除了安装过程简洁,代码也十分简洁.

多个Android项目源码-覆盖方方面面值得学习

Android PDF 阅读器 http://sourceforge.net/projects/andpdf/files/个人记账工具 OnMyMeans http://sourceforge.net/projects/onmymeans/developAndroid电池监控 Android Battery Dog http://sourceforge.net/projects/andbatdog/RSS阅读软件 Android RSS http://code.google.com/p/andr

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

【原】AFNetworking源码阅读(四)

[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDelegate类所实现的NSURLSession相关的代理方法,甚至连dataTask.uploadTask.downloadTask这几个基本概念也没说.这一篇就是为了集中消灭这些遗留问题. 2. AFURLSessionManagerTaskDelegate的代理方法 此处实现的仍然是NSURLS