• PyQt5_QScrollArea内容保存成图片


    本文提供两个例子,一个简单,一个复杂。基础没有那么好的同学可以看第一个,功能单一且明确;第二个例子较复杂,是根据一个主题绘制多个曲线,里面还有很多其他功能点(在本人以前博文中提过),基础不扎实的同学看了容易晕。

    目录

    效果:

    例子1

    ​例子2

     代码:

    例子1

     例子2

    使用

    例子1

    例子2 

     数据

    效果:

    例子1

     例子2

     

     代码:

    例子1

    1. import sys
    2. from PyQt5 import QtCore, QtGui, QtWidgets
    3. class ExampleWidget(QtWidgets.QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.temp_save = 0
    7. self.temp_mark = 0
    8. self.init_data()
    9. self.init_ui()
    10. self.fill_pic_widget()
    11. def init_data(self):
    12. self.pic_file_name_list = ['000', '001', '002', '003', '004', '005', '006', '007', '008', '009', '010', '011',
    13. '012', '013', '014', '015', '016', '017']
    14. pass
    15. def init_ui(self):
    16. self.setWindowTitle('QScrollArea内部生成图片存储')
    17. one_btn = QtWidgets.QPushButton('换内容')
    18. one_btn.clicked.connect(self.one_btn_clicked)
    19. two_btn = QtWidgets.QPushButton('ScrollArea内部生成图片存储')
    20. two_btn.clicked.connect(self.two_btn_clicked)
    21. self.pic_layout = QtWidgets.QGridLayout()
    22. self.scroll_area = QtWidgets.QScrollArea()
    23. # self.scroll_area.setWidgetResizable(True)
    24. self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    25. layout = QtWidgets.QVBoxLayout()
    26. layout.addWidget(one_btn)
    27. layout.addWidget(two_btn)
    28. layout.addWidget(self.scroll_area)
    29. self.setLayout(layout)
    30. pass
    31. def one_btn_clicked(self):
    32. self.temp_mark = 1
    33. self.fill_pic_widget()
    34. pass
    35. def two_btn_clicked(self):
    36. self.temp_save += 1
    37. path,_ = QtWidgets.QFileDialog.getSaveFileName(
    38. self,
    39. '选择图片存储路径',
    40. f"pic_{self.temp_save}",
    41. 'JPG(*.jpg)'
    42. )
    43. if not path:
    44. return
    45. widget = self.scroll_area.widget()
    46. pix = widget.grab()
    47. pix.save(path)
    48. pass
    49. def fill_pic_widget(self):
    50. '''放置图片'''
    51. # 清空控件
    52. while self.pic_layout.count():
    53. item = self.pic_layout.takeAt(0)
    54. widget = item.widget()
    55. if widget is not None:
    56. widget.deleteLater()
    57. pass
    58. pass
    59. sc_child_widget = self.scroll_area.takeWidget()
    60. if sc_child_widget is not None:
    61. sc_child_widget.deleteLater()
    62. pre_dir = r'D:/temp010/'
    63. row_i = -2
    64. if self.temp_mark == 1:
    65. self.pic_file_name_list.reverse()
    66. for pic_index, file_name in enumerate(self.pic_file_name_list):
    67. one_check = QtWidgets.QCheckBox(file_name)
    68. pixmap = QtGui.QPixmap(pre_dir + file_name + '.jpeg')
    69. one_piclabel = QtWidgets.QLabel()
    70. one_piclabel.setPixmap(pixmap)
    71. one_piclabel.setScaledContents(True)
    72. col_i = pic_index % 4
    73. if col_i == 0:
    74. row_i += 2
    75. print(file_name, row_i, col_i)
    76. self.pic_layout.addWidget(one_check, row_i, col_i, 1, 1)
    77. self.pic_layout.addWidget(one_piclabel, row_i + 1, col_i, 1, 1)
    78. pass
    79. print('self.pic_layout,', self.pic_layout.count())
    80. one_sc_child_widget = QtWidgets.QWidget()
    81. one_sc_child_widget.setLayout(self.pic_layout)
    82. self.scroll_area.setWidget(one_sc_child_widget)
    83. pass

     例子2

    1. import sys,math
    2. import pandas as pd
    3. from threading import Thread
    4. from PyQt5 import QtCore,QtWidgets,QtGui
    5. from PyQt5.QtCore import Qt
    6. from typing import Any,Dict,List
    7. import pyqtgraph as pg
    8. pg.setConfigOption('background', 'w')
    9. pg.setConfigOption('foreground', 'k')
    10. class RotateAxisItem(pg.AxisItem):
    11. def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
    12. p.setRenderHint(p.Antialiasing,False)
    13. p.setRenderHint(p.TextAntialiasing,True)
    14. ## draw long line along axis
    15. pen,p1,p2 = axisSpec
    16. p.setPen(pen)
    17. p.drawLine(p1,p2)
    18. p.translate(0.5,0) ## resolves some damn pixel ambiguity
    19. ## draw ticks
    20. for pen,p1,p2 in tickSpecs:
    21. p.setPen(pen)
    22. p.drawLine(p1,p2)
    23. ## draw all text
    24. # if self.tickFont is not None:
    25. # p.setFont(self.tickFont)
    26. p.setPen(self.pen())
    27. for rect,flags,text in textSpecs:
    28. # this is the important part
    29. p.save()
    30. p.translate(rect.x(),rect.y())
    31. p.rotate(-30)
    32. p.drawText(-rect.width(),rect.height(),rect.width(),rect.height(),flags,text)
    33. # restoring the painter is *required*!!!
    34. p.restore()
    35. class RotateAxisItemOneYear(pg.AxisItem):
    36. def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
    37. p.setRenderHint(p.Antialiasing,False)
    38. p.setRenderHint(p.TextAntialiasing,True)
    39. ## draw long line along axis
    40. pen,p1,p2 = axisSpec
    41. p.setPen(pen)
    42. p.drawLine(p1,p2)
    43. p.translate(0.5,-1) ## resolves some damn pixel ambiguity
    44. # p.translate(1,0) ## resolves some damn pixel ambiguity
    45. ## draw ticks
    46. for pen,p1,p2 in tickSpecs:
    47. p.setPen(pen)
    48. p.drawLine(p1,p2)
    49. ## draw all text
    50. # if self.tickFont is not None:
    51. # p.setFont(self.tickFont)
    52. font = QtGui.QFont()
    53. font.setPixelSize(9)
    54. p.setFont(font)
    55. p.setPen(self.pen())
    56. for rect,flags,text in textSpecs:
    57. # this is the important part
    58. p.save()
    59. p.translate(rect.x(),rect.y())
    60. p.rotate(-30)
    61. p.drawText(-rect.width()*0.6,rect.height(),rect.width(),rect.height(),flags,text)
    62. # restoring the painter is *required*!!!
    63. p.restore()

    RotateAxisItem: 自定义横坐标

    RotateAxisItemOneYear: 自定义横坐标,相比RotateAxisItem相比只是字号小,其实这两个可以合并为一个,大家自己探索哈。

    1. class PyQtGraphLineWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.line_type: int = 1
    5. self.fomc_p = None
    6. self.temp_thread = None
    7. self.init_data()
    8. self.init_ui()
    9. # self.register_event()
    10. def init_data(self):
    11. # 颜色值 https://www.sioe.cn/yingyong/yanse-rgb-16/
    12. self.color_one = (30,144,255)
    13. self.color_two = (255,140,0)
    14. self.p2 = None
    15. self.legend2 = None
    16. self.curve2 = None
    17. pass
    18. def init_ui(self):
    19. self.title_label = QtWidgets.QLabel('折线图')
    20. self.title_label.setAlignment(Qt.AlignCenter)
    21. self.title_label.setStyleSheet('QLabel{font-size:18px;font-weight:bold;}')
    22. xax = RotateAxisItem(orientation='bottom')
    23. xax.setHeight(h=80)
    24. self.pw = pg.PlotWidget(axisItems={'bottom': xax})
    25. self.pw.setMouseEnabled(x=True,y=False)
    26. # self.pw.enableAutoRange(x=False,y=True)
    27. self.pw.setAutoVisible(x=False,y=True)
    28. layout = QtWidgets.QVBoxLayout()
    29. layout.addWidget(self.title_label)
    30. layout.addWidget(self.pw)
    31. self.setLayout(layout)
    32. pass
    33. def set_data(self, data: Dict[str, Any]):
    34. '''单根y轴'''
    35. self.line_type = 1
    36. if data is None:
    37. self.pw.clear()
    38. return
    39. if self.p2 is not None:
    40. '''
    41. 如果前一次显示的是双y轴,
    42. 【暂且把self.pw叫做主视图,self.p2叫做右侧视图】
    43. 1. 要把主视图右侧坐标轴隐藏
    44. 2. 要把主视图右侧跟随变动的信号断开
    45. 3. 将右侧视图(ViewBox)删除
    46. 4. 将右侧视图(ViewBox)清空
    47. 5. 将右侧视图置空
    48. '''
    49. self.pw.hideAxis('right')
    50. self.vb.sigResized.disconnect(self.updateViews)
    51. self.pw.scene().removeItem(self.p2)
    52. self.p2.clear()
    53. self.legend2.clear()
    54. self.curve2.clear()
    55. self.vb.clear()
    56. self.p2 = None
    57. self.legend2 = None
    58. self.curve2 = None
    59. # 将上一次视图清空
    60. self.pw.clear()
    61. self.pw.addLegend()
    62. title_str = data['title_str']
    63. self.title_label.setText(title_str)
    64. xTick = [data['xTick00']]
    65. x = data['x']
    66. y = data['y']
    67. y_name = data['y_name']
    68. self.pw.setLabel('left', y_name)
    69. self.y_datas = y
    70. self.x_data = xTick
    71. self.x_Tick = data['xTick']
    72. self.y_name = y_name
    73. xax = self.pw.getAxis('bottom')
    74. xax.setTicks(xTick)
    75. self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
    76. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    77. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    78. self.label = pg.TextItem()
    79. self.pw.addItem(self.vLine, ignoreBounds=True)
    80. self.pw.addItem(self.hLine, ignoreBounds=True)
    81. self.pw.addItem(self.label, ignoreBounds=True)
    82. self.vb = self.pw.getViewBox()
    83. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    84. # 显示整条折线图
    85. self.pw.enableAutoRange()
    86. pass
    87. def set_data_2y(self, data: Dict[str, Any]):
    88. '''双y轴'''
    89. self.line_type = 2
    90. if data is None:
    91. self.pw.clear()
    92. return
    93. if self.p2 is not None:
    94. self.pw.hideAxis('right')
    95. self.vb.sigResized.disconnect(self.updateViews)
    96. self.pw.scene().removeItem(self.p2)
    97. self.p2.clear()
    98. self.legend2.clear()
    99. self.curve2.clear()
    100. self.vb.clear()
    101. self.p2 = None
    102. self.legend2 = None
    103. self.curve2 = None
    104. if self.fomc_p:
    105. self.pw.scene().removeItem(self.fomc_p)
    106. self.pw.clear()
    107. self.pw.addLegend()
    108. title_str = data['title_str']
    109. self.title_label.setText(title_str)
    110. xTick = [data['xTick00']]
    111. x = data['x']
    112. y = data['y']
    113. y_name = data['y_name']
    114. self.pw.setLabel('left', y_name)
    115. self.y_datas = y
    116. self.x_data = xTick
    117. self.x_Tick = data['xTick']
    118. self.y_name = y_name
    119. xax = self.pw.getAxis('bottom')
    120. xax.setTicks(xTick)
    121. self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
    122. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    123. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    124. self.label = pg.TextItem()
    125. self.pw.addItem(self.vLine, ignoreBounds=True)
    126. self.pw.addItem(self.hLine, ignoreBounds=True)
    127. self.pw.addItem(self.label, ignoreBounds=True)
    128. # 第二根y轴 start
    129. y2 = data['y2']
    130. y_name2 = data['y_name2']
    131. self.y_datas2 = y2
    132. self.pw.setLabel('right', y_name2)
    133. self.p2 = pg.ViewBox()
    134. self.p2.setMouseEnabled(x=True, y=False)
    135. self.p2.setAutoVisible(x=False, y=True)
    136. self.pw.scene().addItem(self.p2)
    137. self.pw.getAxis('right').linkToView(self.p2)
    138. self.p2.setXLink(self.pw)
    139. self.curve2 = pg.PlotCurveItem(x=x,y=y2, pen=pg.mkPen({'color': self.color_two, 'width': 4}), connect='finite')
    140. self.p2.addItem(self.curve2)
    141. self.legend2 = pg.LegendItem(offset=(0., 1.))
    142. self.legend2.setParentItem(self.p2)
    143. power = pg.PlotDataItem(antialias=True, pen=pg.mkPen({'color': self.color_two, 'width': 4}))
    144. self.legend2.addItem(power, y_name2)
    145. # 第二根y轴 end
    146. self.vb = self.pw.getViewBox()
    147. self.updateViews()
    148. self.vb.sigResized.connect(self.updateViews)
    149. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    150. self.pw.enableAutoRange()
    151. pass
    152. def updateViews(self):
    153. if self.p2:
    154. self.p2.setGeometry(self.pw.getViewBox().sceneBoundingRect())
    155. self.p2.linkedViewChanged(self.pw.getViewBox(), self.p2.XAxis)
    156. if self.fomc_p:
    157. self.fomc_p.setGeometry(self.pw.getViewBox().sceneBoundingRect())
    158. self.fomc_p.linkedViewChanged(self.pw.getViewBox(), self.fomc_p.XAxis)
    159. pass
    160. def mouseMoved(self,evt):
    161. pos = evt[0]
    162. if self.pw.sceneBoundingRect().contains(pos):
    163. mousePoint = self.vb.mapSceneToView(pos)
    164. index = int(mousePoint.x())
    165. if index >= 0 and index < len(self.y_datas):
    166. x_str = self.x_Tick[index][1]
    167. y_str_html = ''
    168. if self.line_type == 2:
    169. y_str2 = str(self.y_datas2[index])
    170. y_str_html += '&nbsp;'+y_str2
    171. y_str = str(self.y_datas[index])
    172. y_str_html += '&nbsp;' + y_str
    173. html_str = '<p style="color:black;font-size:18px;font-weight:bold;">&nbsp;' + x_str +'&nbsp;'+y_str_html+ '</p>'
    174. self.label.setHtml(html_str)
    175. self.label.setPos(mousePoint.x(), mousePoint.y())
    176. self.vLine.setPos(mousePoint.x())
    177. self.hLine.setPos(mousePoint.y())
    178. pass
    179. class PyQtGraphLineOneYearWidget(QtWidgets.QWidget):
    180. def __init__(self):
    181. super().__init__()
    182. self.line_type: int = 1
    183. self.fomc_p = None
    184. self.temp_thread = None
    185. self.init_data()
    186. self.init_ui()
    187. # self.register_event()
    188. def init_data(self):
    189. # 颜色值 https://www.sioe.cn/yingyong/yanse-rgb-16/
    190. self.color_one = (30,144,255)
    191. self.color_two = (255,140,0)
    192. self.p2 = None
    193. self.legend2 = None
    194. self.curve2 = None
    195. pass
    196. def init_ui(self):
    197. self.title_label = QtWidgets.QLabel('折线图')
    198. self.title_label.setAlignment(Qt.AlignCenter)
    199. self.title_label.setStyleSheet('QLabel{font-size:18px;font-weight:bold;}')
    200. xax = RotateAxisItemOneYear(orientation='bottom')
    201. xax.setHeight(h=50)
    202. self.pw = pg.PlotWidget(axisItems={'bottom': xax})
    203. self.pw.setMouseEnabled(x=True,y=False)
    204. # self.pw.enableAutoRange(x=False,y=True)
    205. self.pw.setAutoVisible(x=False,y=True)
    206. layout = QtWidgets.QVBoxLayout()
    207. layout.addWidget(self.title_label)
    208. layout.addWidget(self.pw)
    209. self.setLayout(layout)
    210. pass
    211. def set_data(self, data: Dict[str, Any]):
    212. '''单根y轴'''
    213. self.line_type = 1
    214. if data is None:
    215. self.pw.clear()
    216. return
    217. if self.p2 is not None:
    218. '''
    219. 如果前一次显示的是双y轴,
    220. 【暂且把self.pw叫做主视图,self.p2叫做右侧视图】
    221. 1. 要把主视图右侧坐标轴隐藏
    222. 2. 要把主视图右侧跟随变动的信号断开
    223. 3. 将右侧视图(ViewBox)删除
    224. 4. 将右侧视图(ViewBox)清空
    225. 5. 将右侧视图置空
    226. '''
    227. self.pw.hideAxis('right')
    228. self.vb.sigResized.disconnect(self.updateViews)
    229. self.pw.scene().removeItem(self.p2)
    230. self.p2.clear()
    231. self.legend2.clear()
    232. self.curve2.clear()
    233. self.vb.clear()
    234. self.p2 = None
    235. self.legend2 = None
    236. self.curve2 = None
    237. # 将上一次视图清空
    238. self.pw.clear()
    239. self.pw.addLegend()
    240. title_str = data['title_str']
    241. self.title_label.setText(title_str)
    242. xTick = [data['xTick00']]
    243. x = data['x']
    244. y = data['y']
    245. y_name = data['y_name']
    246. self.pw.setLabel('left', y_name)
    247. self.y_datas = y
    248. self.x_data = xTick
    249. self.x_Tick = data['xTick']
    250. self.y_name = y_name
    251. xax = self.pw.getAxis('bottom')
    252. xax.setTicks(xTick)
    253. self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
    254. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    255. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    256. self.label = pg.TextItem()
    257. self.pw.addItem(self.vLine, ignoreBounds=True)
    258. self.pw.addItem(self.hLine, ignoreBounds=True)
    259. self.pw.addItem(self.label, ignoreBounds=True)
    260. self.vb = self.pw.getViewBox()
    261. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    262. # 显示整条折线图
    263. self.pw.enableAutoRange()
    264. pass
    265. def set_data_2y(self, data: Dict[str, Any]):
    266. '''双y轴'''
    267. self.line_type = 2
    268. if data is None:
    269. self.pw.clear()
    270. return
    271. if self.p2 is not None:
    272. self.pw.hideAxis('right')
    273. self.vb.sigResized.disconnect(self.updateViews)
    274. self.pw.scene().removeItem(self.p2)
    275. self.p2.clear()
    276. self.legend2.clear()
    277. self.curve2.clear()
    278. self.vb.clear()
    279. self.p2 = None
    280. self.legend2 = None
    281. self.curve2 = None
    282. if self.fomc_p:
    283. self.pw.scene().removeItem(self.fomc_p)
    284. self.pw.clear()
    285. self.pw.addLegend()
    286. title_str = data['title_str']
    287. self.title_label.setText(title_str)
    288. xTick = [data['xTick00']]
    289. x = data['x']
    290. y = data['y']
    291. y_name = data['y_name']
    292. self.pw.setLabel('left', y_name)
    293. self.y_datas = y
    294. self.x_data = xTick
    295. self.x_Tick = data['xTick']
    296. self.y_name = y_name
    297. xax = self.pw.getAxis('bottom')
    298. xax.setTicks(xTick)
    299. self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
    300. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    301. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    302. self.label = pg.TextItem()
    303. self.pw.addItem(self.vLine, ignoreBounds=True)
    304. self.pw.addItem(self.hLine, ignoreBounds=True)
    305. self.pw.addItem(self.label, ignoreBounds=True)
    306. # 第二根y轴 start
    307. y2 = data['y2']
    308. y_name2 = data['y_name2']
    309. self.y_datas2 = y2
    310. self.pw.setLabel('right', y_name2)
    311. self.p2 = pg.ViewBox()
    312. self.p2.setMouseEnabled(x=True, y=False)
    313. self.p2.setAutoVisible(x=False, y=True)
    314. self.pw.scene().addItem(self.p2)
    315. self.pw.getAxis('right').linkToView(self.p2)
    316. self.p2.setXLink(self.pw)
    317. self.curve2 = pg.PlotCurveItem(x=x,y=y2, pen=pg.mkPen({'color': self.color_two, 'width': 4}), connect='finite')
    318. self.p2.addItem(self.curve2)
    319. self.legend2 = pg.LegendItem(offset=(0., 1.))
    320. self.legend2.setParentItem(self.p2)
    321. power = pg.PlotDataItem(antialias=True, pen=pg.mkPen({'color': self.color_two, 'width': 4}))
    322. self.legend2.addItem(power, y_name2)
    323. # 第二根y轴 end
    324. self.vb = self.pw.getViewBox()
    325. self.updateViews()
    326. self.vb.sigResized.connect(self.updateViews)
    327. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    328. self.pw.enableAutoRange()
    329. pass
    330. def updateViews(self):
    331. if self.p2:
    332. self.p2.setGeometry(self.pw.getViewBox().sceneBoundingRect())
    333. self.p2.linkedViewChanged(self.pw.getViewBox(), self.p2.XAxis)
    334. if self.fomc_p:
    335. self.fomc_p.setGeometry(self.pw.getViewBox().sceneBoundingRect())
    336. self.fomc_p.linkedViewChanged(self.pw.getViewBox(), self.fomc_p.XAxis)
    337. pass
    338. def mouseMoved(self,evt):
    339. pos = evt[0]
    340. if self.pw.sceneBoundingRect().contains(pos):
    341. mousePoint = self.vb.mapSceneToView(pos)
    342. index = int(mousePoint.x())
    343. if index >= 0 and index < len(self.y_datas):
    344. x_str = self.x_Tick[index][1]
    345. y_str_html = ''
    346. if self.line_type == 2:
    347. y_str2 = str(self.y_datas2[index])
    348. y_str_html += '&nbsp;'+y_str2
    349. y_str = str(self.y_datas[index])
    350. y_str_html += '&nbsp;' + y_str
    351. html_str = '<p style="color:black;font-size:18px;font-weight:bold;">&nbsp;' + x_str +'&nbsp;'+y_str_html+ '</p>'
    352. self.label.setHtml(html_str)
    353. self.label.setPos(mousePoint.x(), mousePoint.y())
    354. self.vLine.setPos(mousePoint.x())
    355. self.hLine.setPos(mousePoint.y())
    356. pass

    PyQtGraphLineWidget和PyQtGraphLineOneYearWidget也是可以合并为一个的,我只是犯懒,大家有兴趣的可以自己探索哈,这两个是用于显示折线的控件

    1. class ChildLineWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.init_data()
    5. self.init_ui()
    6. pass
    7. def init_data(self):
    8. pass
    9. def init_ui(self):
    10. self.setMinimumHeight(600)
    11. self.left_line_widget = PyQtGraphLineWidget()
    12. self.righttop_line_widget = PyQtGraphLineOneYearWidget()
    13. self.rightdown_table_widget = QtWidgets.QTableWidget()
    14. layout_right = QtWidgets.QVBoxLayout()
    15. layout_right.addWidget(self.righttop_line_widget,1)
    16. layout_right.addWidget(self.rightdown_table_widget,1)
    17. layout = QtWidgets.QHBoxLayout()
    18. layout.addWidget(self.left_line_widget,3)
    19. layout.addLayout(layout_right,1)
    20. self.setLayout(layout)
    21. pass
    22. def set_data(self,data:Dict[str,Any]):
    23. line_data = data['line_data']
    24. table_data = data['table_data']
    25. self.setting_line_data(line_data)
    26. self.setting_table_data(table_data)
    27. pass
    28. def setting_line_data(self,data:Dict[str,Any]):
    29. line_type = data['line_type']
    30. whole_line = data['whole_line']
    31. one_year_line = data['one_year_line']
    32. if line_type == 2:
    33. self.left_line_widget.set_data_2y(whole_line)
    34. self.righttop_line_widget.set_data_2y(one_year_line)
    35. else:
    36. self.left_line_widget.set_data(whole_line)
    37. self.righttop_line_widget.set_data(one_year_line)
    38. pass
    39. def setting_table_data(self,data:Dict[str,Any]):
    40. table_header = data['table_header']
    41. table_body = data['table_body']
    42. self.rightdown_table_widget.clearContents()
    43. self.rightdown_table_widget.setColumnCount(len(table_header))
    44. self.rightdown_table_widget.setHorizontalHeaderLabels(table_header)
    45. self.rightdown_table_widget.setRowCount(len(table_body))
    46. for r_i,r_v in enumerate(table_body):
    47. for c_i,c_v in enumerate(r_v):
    48. cell = QtWidgets.QTableWidgetItem(str(c_v))
    49. self.rightdown_table_widget.setItem(r_i,c_i,cell)
    50. pass
    51. self.rightdown_table_widget.resizeColumnsToContents()
    52. pass
    53. pass

    ChildLineWidget: 在滚动区中一个数据的完整显示控件,显示数据的全数据折线图,近期数据折现图和最新50个数据列表

    1. class MultiChildLineWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.init_data()
    5. self.init_ui()
    6. pass
    7. def init_data(self):
    8. self.title_str: str = '主题名'
    9. self.child_line_data_list: List[Any] = []
    10. pass
    11. def init_ui(self):
    12. self.layout_child = QtWidgets.QVBoxLayout()
    13. self.scroll_area = QtWidgets.QScrollArea()
    14. self.scroll_area.setWidgetResizable(True)
    15. self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    16. layout = QtWidgets.QVBoxLayout()
    17. layout.addWidget(self.scroll_area)
    18. self.setLayout(layout)
    19. pass
    20. def set_data(self,data:Dict[str,Any]):
    21. title_str = data['title']
    22. self.child_line_data_list = data['line_data_list']
    23. self.title_str = title_str
    24. self.fill_child_line_widget()
    25. pass
    26. def fill_child_line_widget(self):
    27. # 清空控件
    28. while self.layout_child.count():
    29. item = self.layout_child.takeAt(0)
    30. widget = item.widget()
    31. if widget is not None:
    32. widget.deleteLater()
    33. pass
    34. pass
    35. sc_child_widget = self.scroll_area.takeWidget()
    36. if sc_child_widget is not None:
    37. sc_child_widget.deleteLater()
    38. title_label = QtWidgets.QLabel(self.title_str)
    39. title_label.setAlignment(Qt.AlignCenter)
    40. title_label.setStyleSheet('QLabel{font-size:26px;font-weight:bold;color:blue}')
    41. self.layout_child.addWidget(title_label)
    42. self.layout_child.addSpacing(10)
    43. for one_item in self.child_line_data_list:
    44. one_widget = ChildLineWidget()
    45. one_widget.set_data(one_item)
    46. self.layout_child.addWidget(one_widget)
    47. self.layout_child.addSpacing(10)
    48. pass
    49. one_sc_child_widget = QtWidgets.QWidget()
    50. one_sc_child_widget.setLayout(self.layout_child)
    51. self.scroll_area.setWidget(one_sc_child_widget)
    52. pass
    53. def res_scroll_widget(self):
    54. return self.scroll_area.widget()
    55. pass

    MultiChildLineWidget: 放置多个最小数据单元控件ChildLineWidget的容器

    1. class ExampleScrollLineWidget(QtWidgets.QWidget):
    2. close_signal = QtCore.pyqtSignal(str)
    3. signal_themeline = QtCore.pyqtSignal(object)
    4. def __init__(self):
    5. super().__init__()
    6. self.caculate_thread = None
    7. self.init_data()
    8. self.init_ui()
    9. self.register_event()
    10. pass
    11. def init_data(self):
    12. self.pre_catalog_list:List[Any] = []
    13. self.pre_data_list:List[Any] = []
    14. self.pic_count:int = 0
    15. self.catalog_code_list: List[str] = ['economic_usanationdebt10y','economic_goldspotprice','economic_medianpehs300','economic_pigprice','economic_usdebt10sub1','economic_chinadebt10sub1']
    16. self.period_year_map: Dict[str,int] = {
    17. '日数据':252,
    18. '周数据':52,
    19. '月数据':12,
    20. '季度数据':8,
    21. '年数据':5
    22. }
    23. self.table_count: int = 50
    24. self.x_count: int = 20
    25. pass
    26. def init_ui(self):
    27. self.caculate_progress = QtWidgets.QProgressBar()
    28. self.caculate_status_label = QtWidgets.QLabel()
    29. layout_progress = QtWidgets.QHBoxLayout()
    30. layout_progress.addWidget(self.caculate_progress)
    31. layout_progress.addWidget(self.caculate_status_label)
    32. choise_btn = QtWidgets.QPushButton('选择数据')
    33. choise_btn.clicked.connect(self.choise_btn_clicked)
    34. create_pic_btn = QtWidgets.QPushButton('生成图片')
    35. create_pic_btn.clicked.connect(self.create_pic_btn_clicked)
    36. tip_label = QtWidgets.QLabel('主题名')
    37. self.theme_lineedit = QtWidgets.QLineEdit()
    38. layout_btn = QtWidgets.QHBoxLayout()
    39. layout_btn.addWidget(choise_btn)
    40. layout_btn.addWidget(create_pic_btn)
    41. layout_input = QtWidgets.QHBoxLayout()
    42. layout_input.addWidget(tip_label)
    43. layout_input.addWidget(self.theme_lineedit)
    44. self.muliti_widget = MultiChildLineWidget()
    45. layout = QtWidgets.QVBoxLayout()
    46. layout.addLayout(layout_progress)
    47. layout.addLayout(layout_btn)
    48. layout.addLayout(layout_input)
    49. layout.addWidget(self.muliti_widget)
    50. self.setLayout(layout)
    51. pass
    52. def register_event(self):
    53. self.signal_themeline.connect(self.process_themeline_event)
    54. pass
    55. def process_themeline_event(self,data:Dict[str,Any])->None:
    56. change_type = data['change_type']
    57. if change_type == 'caculate_result':
    58. res_data = data['data']
    59. title_str = self.theme_lineedit.text()
    60. print(title_str)
    61. title_str = title_str.strip()
    62. if len(title_str)<=0:
    63. title_str = '主题'
    64. pre_show_data = {
    65. 'title':title_str,
    66. 'line_data_list':res_data
    67. }
    68. self.muliti_widget.set_data(pre_show_data)
    69. self.progress_finished()
    70. self.caculate_thread = None
    71. pass
    72. def setting_data(self,data:Dict[str,Any]):
    73. self.pre_catalog_list = data['catalog']
    74. self.pre_data_list = data['data']
    75. pass
    76. def choise_btn_clicked(self):
    77. '''选择数据按钮点击'''
    78. self.start_caculate_thread('caculate',None)
    79. pass
    80. def create_pic_btn_clicked(self):
    81. '''生成图片按钮点击'''
    82. self.pic_count += 1
    83. path, _ = QtWidgets.QFileDialog.getSaveFileName(
    84. self,
    85. '选择图片存储路径',
    86. f"pic_{self.pic_count}",
    87. 'JPG(*.jpg)'
    88. )
    89. if not path:
    90. return
    91. widget = self.muliti_widget.res_scroll_widget()
    92. pix = widget.grab()
    93. pix.save(path)
    94. pass
    95. def init_caculate_thread(self):
    96. self.progress_init()
    97. # self.start_caculate_thread('init', None)
    98. pass
    99. def start_caculate_thread(self, mark_str: str, data: Dict[str, Any]):
    100. if self.caculate_thread:
    101. QtWidgets.QMessageBox.information(
    102. self,
    103. '提示',
    104. '有任务正在执行',
    105. QtWidgets.QMessageBox.Yes
    106. )
    107. return
    108. self.caculate_thread = Thread(
    109. target=self.running_caculate_thread,
    110. args=(
    111. mark_str,
    112. data,
    113. )
    114. )
    115. self.caculate_thread.start()
    116. self.progress_busy()
    117. pass
    118. def running_caculate_thread(self, mark_str: str, data: Dict[str, Any]):
    119. if mark_str == 'caculate':
    120. catalog_list = self.pre_catalog_list
    121. data_list = self.pre_data_list
    122. # 开始处理数据
    123. catalog_map = {}
    124. for item in catalog_list:
    125. catalog_code = item['economic_code']
    126. level = item['level']
    127. if level == 2:
    128. catalog_name = f"{item['economic_name']}【{item['economic_source']}】"
    129. else:
    130. catalog_name = item['economic_name']
    131. catalog_lineshow = item['economic_lineshow']
    132. if catalog_lineshow.get('y2') is not None:
    133. line_type = 2
    134. else:
    135. line_type = 1
    136. catalog_period = item['economic_period']
    137. if self.period_year_map.get(catalog_period) is None:
    138. last_one_year_count = 10
    139. else:
    140. last_one_year_count = self.period_year_map[catalog_period]
    141. catalog_map[catalog_code] = {
    142. 'catalog_name':catalog_name,
    143. 'catalog_lineshow':catalog_lineshow,
    144. 'line_type':line_type,
    145. 'catalog_period':catalog_period,
    146. 'last_one_year_count':last_one_year_count
    147. }
    148. res_show_list = []
    149. for item in data_list:
    150. catalog_code = item['catalog_code']
    151. catalog_obj = catalog_map[catalog_code]
    152. last_one_year_count = catalog_obj['last_one_year_count']
    153. catalog_name = catalog_obj['catalog_name']
    154. catalog_lineshow = catalog_obj['catalog_lineshow']
    155. line_type = catalog_obj['line_type']
    156. column_list = item['column_list']
    157. pd_column_list = item['pd_column_list']
    158. item_data_list = item['data_list']
    159. df = pd.DataFrame(columns=pd_column_list,data=item_data_list)
    160. df_one_year = df.iloc[-1*last_one_year_count:, :].copy()
    161. df_table = df.iloc[-1*self.table_count:,:].copy()
    162. if line_type == 2:
    163. # 双Y轴
    164. whole_line_data = self.res_twoY_line_data(df,catalog_lineshow)
    165. one_year_line_data = self.res_twoY_line_data(df_one_year,catalog_lineshow)
    166. pass
    167. else:
    168. # 一条曲线
    169. whole_line_data = self.res_one_line_data(df,catalog_lineshow)
    170. one_year_line_data = self.res_one_line_data(df_one_year,catalog_lineshow)
    171. pass
    172. whole_line_data['title_str'] = catalog_name
    173. one_year_line_data['title_str'] = f"{catalog_name}(近期)"
    174. one_line = {
    175. 'line_type':line_type,
    176. 'whole_line':whole_line_data,
    177. 'one_year_line':one_year_line_data
    178. }
    179. # 表格数据
    180. one_table = {
    181. 'table_header':column_list,
    182. 'table_body':df_table.values.tolist()
    183. }
    184. res_show_list.append({
    185. 'line_data':one_line,
    186. 'table_data':one_table
    187. })
    188. pass
    189. res_json = {}
    190. res_json['change_type'] = 'caculate_result'
    191. res_json['data'] = res_show_list
    192. self.signal_themeline.emit(res_json)
    193. pass
    194. pass
    195. def res_one_line_data(self,df:pd.DataFrame,catalog_lineshow:Dict[str,Any])->Dict[str,Any]:
    196. whole_line_data = {}
    197. df['count'] = range(len(df))
    198. df[catalog_lineshow['y1']] = df[catalog_lineshow['y1']].astype('float')
    199. x = df['count'].values.tolist()
    200. y = df[catalog_lineshow['y1']].values.tolist()
    201. xnames = df[catalog_lineshow['x']].values.tolist()
    202. xTicks = [(one_i, two_i) for one_i, two_i in zip(x, xnames)]
    203. xTicks00 = []
    204. if len(xTicks) > self.x_count:
    205. dur_count = math.floor(len(xTicks) / self.x_count)
    206. for i in range(0, len(xTicks), dur_count):
    207. xTicks00.append(xTicks[i])
    208. else:
    209. xTicks00 = xTicks
    210. y_name = catalog_lineshow['ylabel']
    211. whole_line_data['x'] = x
    212. whole_line_data['y'] = y
    213. whole_line_data['xTick00'] = xTicks00
    214. whole_line_data['y_name'] = y_name
    215. whole_line_data['xTick'] = xTicks
    216. return whole_line_data
    217. def res_twoY_line_data(self, df: pd.DataFrame, catalog_lineshow: Dict[str, Any]) -> Dict[str, Any]:
    218. whole_line_data = {}
    219. df['count'] = range(len(df))
    220. df[catalog_lineshow['y1']] = df[catalog_lineshow['y1']].astype('float')
    221. df[catalog_lineshow['y2']] = df[catalog_lineshow['y2']].astype('float')
    222. x = df['count'].values.tolist()
    223. y = df[catalog_lineshow['y1']].values.tolist()
    224. y2 = df[catalog_lineshow['y2']].values.tolist()
    225. xnames = df[catalog_lineshow['x']].values.tolist()
    226. xTicks = [(one_i, two_i) for one_i, two_i in zip(x, xnames)]
    227. xTicks00 = []
    228. if len(xTicks) > self.x_count:
    229. dur_count = math.floor(len(xTicks) / self.x_count)
    230. for i in range(0, len(xTicks), dur_count):
    231. xTicks00.append(xTicks[i])
    232. else:
    233. xTicks00 = xTicks
    234. y_name = catalog_lineshow['ylabel']
    235. y_name2 = catalog_lineshow['ylabel2']
    236. whole_line_data['x'] = x
    237. whole_line_data['y'] = y
    238. whole_line_data['y2'] = y2
    239. whole_line_data['xTick00'] = xTicks00
    240. whole_line_data['y_name'] = y_name
    241. whole_line_data['y_name2'] = y_name2
    242. whole_line_data['xTick'] = xTicks
    243. return whole_line_data
    244. def progress_init(self)->None:
    245. self.caculate_progress.setValue(0)
    246. self.caculate_status_label.setText('无任务')
    247. def progress_busy(self)->None:
    248. self.caculate_progress.setRange(0,0)
    249. self.caculate_status_label.setText('正在执行')
    250. def progress_finished(self)->None:
    251. self.caculate_progress.setRange(0,100)
    252. self.caculate_progress.setValue(100)
    253. self.caculate_status_label.setText('执行完毕')
    254. def closeEvent(self, QCloseEvent):
    255. # self.close_signal.emit('ThemeLineWidget')
    256. self.close()
    257. pass
    258. pass

    使用

    例子1

    1. if __name__ == '__main__':
    2. app = QtWidgets.QApplication(sys.argv)
    3. t_win = ExampleWidget()
    4. t_win.showMaximized()
    5. sys.exit(app.exec_())
    6. pass

     点击“ScrollArea内容生成图片存储”按钮

     选择要存储的目录,点击保存,在对应目录下就能看到对应图片

    例子2 

    1. if __name__ == '__main__':
    2. QtCore.QCoreApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    3. app = QtWidgets.QApplication(sys.argv)
    4. import json
    5. pre_dir = r'D:/temp006/'
    6. with open(pre_dir+'catalog_list.json','r',encoding='utf-8') as fr:
    7. catalog_list = json.load(fr)
    8. with open(pre_dir+'data_list.json','r',encoding='utf-8') as fr:
    9. data_list = json.load(fr)
    10. pre_data = {
    11. 'catalog':catalog_list,
    12. 'data':data_list
    13. }
    14. t_win = ExampleScrollLineWidget()
    15. t_win.showMaximized()
    16. t_win.setting_data(pre_data)
    17. sys.exit(app.exec_())
    18. pass

    点击“生成图片”按钮

     选择要保存的目录,单击“保存”,在对应的目录下可以看到图片

     

     数据

    文中使用到的数据:

    链接:https://pan.baidu.com/s/1NumTfqscqDAXLN0V_pHNRw 
    提取码:b5oh

     

  • 相关阅读:
    MyCat简介与安装
    RHEL 6和RHEL 7管理服务的区别
    如何使用 Puppeteer 避免机器人检测?
    VS 将 localhost访问改为ip访问
    记录--怎么写一个可以鼠标控制旋转的div?
    阿里云搭建wordpress,自己的第一个网站
    FFT点数、补零对结果的影响
    制造业各细分行业数据库(2000-2019年)
    【Unity3D】卷轴特效
    解决js代码中加入alert就成功执行请求接口,不加就不执行的问题
  • 原文地址:https://blog.csdn.net/m0_37967652/article/details/125508988