学习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详细解释

我们来看看这部分的代码

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

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

  1. if "Windows" in platform.uname():
  2. ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
  3. app_name
  4. )

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

主窗体生成部分

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

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

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

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

  1. def init_ui(self):
  2. self.setWindowTitle(self.window_title) #设置标题
  3. self.init_dock()
  4. self.init_toolbar()
  5. self.init_menu()
  6. self.load_window_setting("custom")

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

init_dock

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

init_dock的方法中首先调用了create_dock

咱们来研究下create_dock的方法。

  1. def create_dock(
  2. self, widget_class: QtWidgets.QWidget, name: str, area: int
  3. ):
  4. widget = widget_class(self.main_engine, self.event_engine)
  5. dock = QtWidgets.QDockWidget(name)
  6. dock.setWidget(widget)
  7. dock.setObjectName(name)
  8. dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
  9. self.addDockWidget(area, dock)
  10. return widget, dock

我们基本上可以这样理解,就是实例化了一个自定义的Widget,然后放入docker中.

docker大概是这样的一个概念【浮动窗口】。

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

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

  1. from PyQt5.QtCore import *
  2. from PyQt5.QtWidgets import *
  3. class DockDemo(QMainWindow):
  4. def __init__(self):
  5. super().__init__()
  6. tradeWidget = TradeWidget()
  7. self.trade_docker = QDockWidget('交易窗口', self)
  8. self.trade_docker.setWidget(tradeWidget)
  9. self.trade_docker.setFeatures(self.trade_docker.DockWidgetFloatable | self.trade_docker.DockWidgetMovable)
  10. self.trade_docker.setObjectName("交易窗口")
  11. self.trade_docker.setFloating(False)
  12. self.addDockWidget(Qt.RightDockWidgetArea,self.trade_docker)
  13. tickWidget = TickMonitorWidget()
  14. self.tick_docker = QDockWidget('行情窗口', self)
  15. self.tick_docker.setWidget(tickWidget)
  16. self.tick_docker.setFloating(False)
  17. self.addDockWidget(Qt.LeftDockWidgetArea, self.tick_docker)
  18. self.show()
  19. class TradeWidget(QWidget):
  20. def __init__(self):
  21. super().__init__()
  22. self.initUI()
  23. def initUI(self):
  24. self.setWindowTitle("这里是交易窗口")
  25. button = QPushButton('交易按钮', self)
  26. button.move(10, 20)
  27. self.show()
  28. class TickMonitorWidget(QWidget):
  29. def __init__(self):
  30. super().__init__()
  31. self.initUI()
  32. def initUI(self):
  33. self.setWindowTitle("这里是行情窗口")
  34. button = QPushButton('行情', self)
  35. button.move(10, 20)
  36. self.show()
  37. app = QApplication([])
  38. dd = DockDemo()
  39. exit(app.exec_())

init_toolbar

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

init_menu()

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

  1. self.add_menu_action(
  2. help_menu,
  3. "查询合约",
  4. "contract.ico",
  5. partial(self.open_widget, ContractManager, "contract"),
  6. )
  7. def add_menu_action(
  8. self,
  9. menu: QtWidgets.QMenu,
  10. action_name: str,
  11. icon_name: str,
  12. func: Callable,
  13. ):
  14. """"""
  15. icon = QtGui.QIcon(get_icon_path(__file__, icon_name))
  16. action = QtWidgets.QAction(action_name, self)
  17. action.triggered.connect(func)
  18. action.setIcon(icon)
  19. menu.addAction(action)

保存windows布局和恢复布局

  1. def save_window_setting(self, name: str):
  2. """
  3. Save current window size and state by trader path and setting name.
  4. """
  5. settings = QtCore.QSettings(self.window_title, name)
  6. settings.setValue("state", self.saveState())
  7. settings.setValue("geometry", self.saveGeometry())
  8. def load_window_setting(self, name: str):
  9. """
  10. Load previous window size and state by trader path and setting name.
  11. """
  12. settings = QtCore.QSettings(self.window_title, name)
  13. state = settings.value("state")
  14. geometry = settings.value("geometry")
  15. if isinstance(state, QtCore.QByteArray):
  16. self.restoreState(state)
  17. self.restoreGeometry(geometry)
  18. def restore_window_setting(self):
  19. """
  20. Restore window to default setting.
  21. """
  22. self.load_window_setting("default")
  23. self.showMaximized()

打开一个定义的Wiget

  1. def open_widget(self, widget_class: QtWidgets.QWidget, name: str):
  2. """
  3. Open contract manager.
  4. """
  5. widget = self.widgets.get(name, None)
  6. if not widget:
  7. widget = widget_class(self.main_engine, self.event_engine)
  8. self.widgets[name] = widget
  9. if isinstance(widget, QtWidgets.QDialog):
  10. widget.exec_()
  11. else:
  12. widget.show()

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

需要注意的是,如果是widget直接调用show,如果是dialog需要调用的是exec_()方法

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

  1. vnpy源码阅读学习(1):准备工作

    vnpy源码阅读学习 目标 通过阅读vnpy,学习量化交易系统的一些设计思路和理念. 通过阅读vnpy学习python项目开发的一些技巧和范式 通过vnpy的设计,可以用python复现一个小型简单的 ...

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

    关于MainEngine的代码阅读 在入口文件中,我们看到了除了窗体界面的产生,还有关于MainEngine和EventEngin部分.今天来学习下MainEngine的代码. 首先在run代码中,我 ...

  3. vnpy源码阅读学习(8):关于app

    关于app 在入口程序中,我们看到了把 gateway,app, 各类的engine都添加到mainEngine中来.不难猜测gateway主要是处理跟外部的行情,接口各方面的代码,通过别人的文章也不 ...

  4. vnpy源码阅读学习(4):自己写一个类似vnpy的UI框架

    自己写一个类似vnpy的界面框架 概述 通过之前3次对vnpy的界面代码的研究,我们去模仿做一个vn.py的大框架.巩固一下PyQt5的学习. 这部分的代码相对来说没有难度和深度,基本上就是把PyQt ...

  5. vnpy源码阅读学习(9)回到OptionMaster

    回到OptionMaster 根据我们对APP调用的代码阅读,我们基本上知道了一个APP是如何被调用,那么我们回到OptionMaster学习下这个APP的实现. 看看结构 class OptionM ...

  6. vnpy源码阅读学习(2):学习PyQt5

    PyQt5的学习 花费了一个下午把PyQt5大概的学习了下.找了一个教程 PyQt5教程 跟着挨着把上面的案例做了一遍,大概知道PyQt5是如何生成窗体,以及控件的.基本上做到如果有需求要实现,查查手 ...

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

    串在一起 我们已经分析了UI.MainEngine.EventEngine.然后他们几个是如何发挥作用的呢?我总结了一张图: 我们来具体的看看UI部分是如何跟EventEngine穿插起来的 \exa ...

  8. Kubernetes 学习(九)Kubernetes 源码阅读之正式篇------核心组件之 Scheduler

    0. 前言 继续上一篇博客阅读 Kubernetes 源码,参照<k8s 源码阅读>首先学习 Kubernetes 的一些核心组件,首先是 kube-scheduler 本文严重参考原文: ...

  9. 搭建 Spring 源码阅读环境

    前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ...

随机推荐

  1. 2019-10-30-C#-dotnet-core-局域网组播方法

    title author date CreateTime categories C# dotnet core 局域网组播方法 lindexi 2019-10-30 9:0:48 +0800 2019- ...

  2. php Restful设计

    1.restful是基于资源的,面向资源架构风格(一个链接,一张图.一个文本等等) 2.restful的http协议 2.1 url: 2.1.1 port 服务端口,默认为80 2.1.2 path ...

  3. JAVA总结---序列化的三种方式

    序列化和反序列化 序列化:可以将对象转化成一个字节序列,便于存储. 反序列化:将序列化的字节序列还原 优点:可以实现对象的"持久性", 所谓持久性就是指对象的生命周期不取决于程序. ...

  4. Mac下SVN基本操作和常见错误

    一.基本操作 1  从服务器上下载代码 svn checkout http://xxx.xxx.xxx/xxx 2  获取最新的代码 svn update 3  提交代码 svn commit -m ...

  5. P1001 A+B+C Problem

    题目描述 输入三个整数 \(a,b,c\) ,计算它们的和并将结果输出. 输入格式 输入一行,三个整数 \(a,b,c\) ,每个整数之间以一个空格隔开 \((0 \le a,b,c \le 1000 ...

  6. NuGet 符号服务器

    在新的 VisualStudio 支持使用 NuGet 符号服务器,可以支持新的 Portable PDB 调试符号的库,本文告诉大家如何打包上传带符号的库和使用符号服务器 在 2018 的 11 月 ...

  7. IDEA开发 工具IC和IU的区别

    现在很多人都在用IDEA开发工具,那么下载安装时会有ideaIU和ideaIC两个版本,到底该怎么选择呢? 首先: ideaIU:U代表的是Ultimate,这个是官方旗舰版也就是商用版本,官方只提供 ...

  8. Linux 内核块 urb

    块 urb 被初始化非常象中断 urb. 做这个的函数是 usb_fill_bulk_urb, 它看来如此: void usb_fill_bulk_urb(struct urb *urb, struc ...

  9. python数据分析经常使用的库

    这个列表包含数据分析经常使用的Python库,供大家使用.1. 网络通用urllib -网络库(stdlib).requests -网络库.grab – 网络库(基于pycurl).pycurl – ...

  10. Visio文本相关操作

    三种方式:双击形状输入文本,插入文本框, 文本工具 文本块工具 选择后可以对文本进行移动旋转 如果要给文本加入边框 直接显示线条就可以了 因为都是文本框 添加特殊文本: 插入符号 插入域 比如当前时间 ...