因为对c++一直处于差不多能看的懂代码,但写的话一头包,所以毅然采用pyqt编写一个股票行情软件。部分窗体截取如下:

等大体上快完工了,跑着跑着突然发现,界面卡顿的一笔。一看cpu,飚到了十几。瞅瞅人家的行情软件,那cpu使用都是稳定的在2以下。

行情一频繁就尿裤,难道是py太拉胯了?

于是立马使用cProfile:

python -m cProfile -s cumulative main.py

1.一号锅:拉胯的setStyleSheet

首先发现setStyleSheet这个函数耗时太久,单发调用居然要5ms,而股票嘛,有红有绿,这调用又非常频繁,故而这肯定是一个瓶颈。 网上搜索了一下,setStyleSheet会触发上级组件的重绘,所以性能上无比拉胯。

不用setStyleSheet修改文字颜色,那就用别的方案。有用palette的,我试了下,毫无效果,并且又注意到官方说不保证palette在所有平台上都一致,所以palette作废。

另外,还可以给QLabel设置html代替纯文本,于是简单撸了以下工具函数

def set_label_text_with_style(label, text, style):label.setText(f"<div style='{style}'>{text}</div>")

对代码进行全局替换。

再一跑,似乎cpu调用下降了,但还是8左右,也不能100%确定这到底有没有起到性能上的优化作用。

于是继续查看下cProfile的输出结果,终于发现了罪魁祸首:QListWidget

2.二号锅:无比拉胯的QListWidget

QListWidget这兄弟,如果数据变化不频繁的话,那还是很能罩的住场子的,譬如什么好友列表,音乐播放列表。但是碰到高速行情,就不行了。大量的ListItem的添加,删除,QListWidget的性能就开始跟不上了。

于是继续网上搜索,又说用QListView的,但是那文档实在是稀烂,而且有各种各样的api。我不就是显示个行情,几个数字而已,何必搞那么复杂。还是自己撸个类似的ListView。

3.自己撸个ListView

现在,我需要个显示效果和QListWidget一致,但是在高频行情下,能够光速刷新,毫不尿裤的listview。它对外暴露一个数据model,用户只需要往里面增删数据,即可同步刷新到界面ui。另外,还需要暴露一个delegate,列表项绘制的时候,就调用用户提供的绘图代码,对列表项进行绘制。

class FFList(QScrollArea):def __init__(self, model, parent=None):super().__init__(parent)self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)self.content_view = ContentView(model, self)self.setWidget(self.content_view)self.content_view.setFixedHeight(1)self.verticalScrollBar().setStyleSheet('''QScrollBar:vertical {width: 15px;}''')def resizeEvent(self, e):self.content_view.setFixedWidth(self.width())def scrollToBottom(self):scroll_bar = self.verticalScrollBar()scroll_bar.setValue(scroll_bar.maximum())

这便是自己撸的listview了,取名为FFList。它继承自QScrollArea, 这样滚动条的相关逻辑还是交由qt进行处理。另外,FFList只支持垂直滚动,水平滚动也是同理,不赘述。

class FFListModel:def __init__(self, delegate, max_count):self.items = []self.delegate = delegateself.max_count = max_countself.__content_view = Nonedef attach_content_view(self, content_view):self.__content_view = content_viewdef __notify_update(self):if self.__content_view is not None:self.__content_view.refresh_ui()def clear(self):self.items = []self.__notify_update()def add(self, *items):self.items.extend(items)if len(self.items) > self.max_count:self.items = self.items[len(self.items) - self.max_count:]self.__notify_update()def pop(self, count):self.items = self.items[count:]self.__notify_update()

这便是数据类型model了,当数据发生变化的时候,调用__notify_update对上层ui进行告知,进而更新ui显示。

class FFItemDelegate:def item_height(self):raise NotImplemented()def paint(self, qp, item, x, y, w, h):raise NotImplemented()

这是列表项绘制的代理类,需要用户实例化

class ContentView(QWidget):def __init__(self, model, ff_list):super().__init__(ff_list)self.ff_list = ff_listself.model = modelself.model.attach_content_view(self)def refresh_ui(self):item_height = self.model.delegate.item_height()self.setFixedHeight(len(self.model.items) * item_height)# print(len(self.model.items) * item_height)self.update()def paintEvent(self, e):item_height = self.model.delegate.item_height()item_width = self.width()v_y0 = self.ff_list.verticalScrollBar().value()v_y1 = v_y0 + self.ff_list.viewport().height()i0 = v_y0 // item_heighti1 = v_y1 // item_heightif i1 >= len(self.model.items):i1 = len(self.model.items) - 1qp = QPainter()qp.begin(self)for i in range(i0, i1 + 1):self.model.delegate.paint(qp, self.model.items[i], 0, i * item_height, item_width, item_height)qp.end()

ContentView是最重要的一个类,他是FFList,即QScrollArea的centralWidget。每当数据变动时,都会调用refresh_ui, 重新设置高度(这一步会改动滚动条)

每当窗口需要重绘时,qt会调用paintEvent,这个函数的实现是自制的FFList性能上拉不拉胯的关键。

首先,计算v_y0,这就是当前滚动条滚到哪的这个位置。v_y1是v_y0加上视口高度,所以v_y0到v_y1之间的内容,就是当前滚动区域可视的高度区间(因为这只是垂直滚动,故而水平的不用管)。

接下来对可视区间内的列表项进行绘制,它会直接调用用户提供的绘制代码。

如果我们不计算v_y0, v_y1,而是对所有列表项都进行绘制,那么对不可见的列表项进行绘制,就是性能上的极大浪费了。

4.FFList的使用

使用上就比较简单了。列表组件由原先继承QListWidget改为FFList,并初始化model:

class OrderInternalView(FFList):def __init__(self, parent=None):self.model = FFListModel(OrderItemDelegate(), 200)super().__init__(self.model, parent)

当有新的行情的时候,将其转化为数据类,然后将其添加到model里:

self.model.add(*items)

另外,需要实现绘制接口,大致如下(根据业务需求自绘):

class OrderItemDelegate(FFItemDelegate):def item_height(self):return 20def paint(self, qp, item, x, y, w, h):if item.seq == 0:qp.setPen(lightGrayPen)time_text = to_time_label_str(item.timestamp)else:qp.setPen(goldPen)time_text = str(item.seq + 1)qp.drawText(x, y, 0.26 * w, h, Qt.AlignRight, time_text)x += 0.26 * wqp.setPen(QPen(QColor(get_color(item.price, item.prev_close))))qp.drawText(x, y, 0.28 * w, h, Qt.AlignRight, "%.2f" % item.price)x += 0.28 * wvol_pen = QPen(QColor(get_color(item.type, item.side)))if item.vol > 500:qp.setPen(purplePen)else:qp.setPen(vol_pen)qp.drawText(x, y, 0.2 * w, h, Qt.AlignRight, "%.0f" % item.vol)x += 0.2 * wqp.setPen(vol_pen)qp.drawText(x, y, 0.14 * w, h, Qt.AlignRight, to_symbol(item.type, item.side))

5.优化结果

使用自定义的FFList后,cpu使用稳定在2%以下,虽然说仍然略为拉胯,但已经算是可用了。其他的优化便是细枝末节的了,在不影响使用的前提下就不进行进一步的优化了。

pyqt股票行情软件性能优化 差点又让python背了锅相关推荐

  1. Day02| 第四期-阿里巴巴股票行情分析(一)

                        01    前言                    2020年高考的第一天到来了,不晓得你的心情如何,我想我们始终忘不了的是对追梦过程中的努力,希望长大以后 ...

  2. 从Redis+Lua到Goroutine,日均10亿次的股票行情计算实践

    http://www.10tiao.com/html/730/201609/2652941778/1.html 2016-09-14 10:28 陶瑞甫 股票行情数据是一种典型的时序数据(Time-s ...

  3. 大数据应用实践1:基于开源架构的股票行情分析与预测

    股票市场行情分析与预测一直是数据分析领域里面的重头戏,确切地说IT行业的每一次重大发展的幕后推动者以及新产品(特别是高端产品)的最先尝试者都包含金融行业,特别是证券交易市场,它符合大数据的四大特征:交 ...

  4. Html5版本的全套股票行情图开源了,附带实现技术简介

    Html5版本的全套股票行情图开源了,附带实现技术简介 - 玉开 - 博客园 Html5版本的全套股票行情图开源了,附带实现技术简介 请使用支持html5的浏览器查看,推荐使用google chrom ...

  5. python绘制k线图的步骤_Python使用PyQtGraph绘制股票行情K线图

    PyQtGraph是Python平台上一种功能强大的2D/3D绘图库,相对于matplotlib库,由于其在内部实现方式上,使用了高速计算的numpy信号处理库以及Qt的GraphicsView框架, ...

  6. java 股票 代码_Java中利用散列表实现股票行情的查询_java

    ---- 在java中,提供了一个散列表类Hashtable,利用该类,我们可以按照特定的方式来存储数据,从而达到快速检索的目的.本文以查询股票的收盘数据为例,详细地说明java中散列表的使用方法. ...

  7. python 行情数据,拼多多股票:Python获取股票行情数据的一种方法

    Python获取股票行情数据的一种方法 拼多多股票 本号帮大家找了一个可免费获取股票行情数据的接口. Tushare社区目前主要维护新版本:tushare pro,数据更稳定拼多多股票质量更高,可获取 ...

  8. 微信公众号最佳实践 ( 7.5 )股票行情及分析

    股票行情及分析 我们使用simple_html_dom.php来抓取所需要的内容 PHP Simple HTML DOM Parser是PHP下目前最优秀的HTML元素解析器,作者是中国台湾的S.C ...

  9. Stockfolio 1.5 特别版 Mac 实时股票行情炒股软件

    这是一款macOS平台的实时股票行情软件,可以帮助我们在mac电脑上查看实时的股票行情 原帖地址:http://www.macfans.org/thread-22452-1-1.html

最新文章

  1. gopro 8 black wifi摄像头 导入 obs 直播 解决方案
  2. IMXRT 分散加载文件 修改OCRAM,DTCM、ITCM大小
  3. C语言二叉树曲折级顺序遍历(附完整源码)
  4. 直接设置Activity的背景颜色
  5. kubernetes1.8.4安装指南 -- 2. ssh免密登录
  6. Silverlight中使用动画的技巧
  7. Kafka学习-入门
  8. 第一次连接mysql失败_MySQL 远程连接失败
  9. 26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树
  10. (49)Verilog HDL SPI接收设计
  11. layui时间选择30分钟为单位_如何集中注意力,不妨试试番茄工作法 | 五色时间管理法...
  12. Python+常用模块(2).md
  13. Visual Studio 2015 前端开发工作流
  14. 珍大户《认知世界的经济学》学习笔记 -- 第17课 影响消费者的因素 -- 色彩、冷暖、秩序
  15. LaTeX插入图片时,图总是置顶的解决办法
  16. 第一节计算机课开场白,老师第一节课的开场白
  17. 手机写python爬虫_我用手机写了一个Python爬虫,爬下了《凡人修仙传》……
  18. mac tortoisesvn客户端_tortoisesvn mac版下载
  19. MYSQL 面试常见问题汇总
  20. 目标检测算法之CVPR 2019 Guided Anchoring

热门文章

  1. 从一篇AMA揭幕单慢雾安全技术
  2. 基于深度学习lstm_基于LSTM的深度恶意软件分析
  3. ArcGIS Desktop 10.5 打开遇到严重的应用程序错误的解决办法
  4. 科达录播服务器修改ip,科达视讯平台API使用说明
  5. Python查询手机号码所在地区
  6. 小程序搜索词条单个轮播停留展示、信息栏多个词条循环滚动展示
  7. 分析手机拍照发展史,OPPO扮演了不可或缺的角色
  8. Oracle的学习路径与方法讨论
  9. 基于OHCI的USB主机 —— UFI读状态代码
  10. 打印机无法打印PDF格式的电子发票问题