如何在没有Matplotlib或PyQtChart等库的情况下在Python中实现图表?
作为学习的一部分,我正在开始一个编程项目。我的项目将是一个数据可视化程序,必须能够至少可视化或“绘制”一个折线图和可能的其他图表,如条形图或饼图。但是,我不允许使用使这变得容易的Python库(如Matplotlib、QtDesigner、NumPy或PyQtChart)。应使用PyQt进行可视化 这个项目对我来说真的很难,因为我对编程相当陌生。我想知道是否有人有类似编程项目的经验或技巧来帮助我完成这个项目。提前谢谢如何在没有Matplotlib或PyQtChart等库的情况下在Python中实现图表?,python,pyqt,data-visualization,Python,Pyqt,Data Visualization,作为学习的一部分,我正在开始一个编程项目。我的项目将是一个数据可视化程序,必须能够至少可视化或“绘制”一个折线图和可能的其他图表,如条形图或饼图。但是,我不允许使用使这变得容易的Python库(如Matplotlib、QtDesigner、NumPy或PyQtChart)。应使用PyQt进行可视化 这个项目对我来说真的很难,因为我对编程相当陌生。我想知道是否有人有类似编程项目的经验或技巧来帮助我完成这个项目。提前谢谢 另外,如果这不是StackOverflow的相关问题,请告诉我,我会记下来。考
另外,如果这不是StackOverflow的相关问题,请告诉我,我会记下来。考虑到这些限制,剩下的唯一解决方案是使用自定义小部件绘制,或者使用,这更强大,但也更复杂 在下面两个简单的示例中,您可以看到这两种方法之间的一些差异,这将显示相同的数据 QWidget子类化通常要简单得多,因为您可以直接绘制基本形状(包括椭圆、圆弧和饼)。您只需实现
paintEvent
(记住,小部件上的QPainter只能发生在绘制事件中!您不能自己调用paintEvent())在小部件上构造QPainter,然后开始绘制
这种简单性的缺点是,如果您需要更高的模块性和更好的控制,那么修改起来可能会更麻烦。图形视图(这正是QtCharts使用的)允许更高的扩展性,因为它提供了一个面向对象的框架(使用QGraphicsItem子类),因此允许操作现有数据,因此您可以更轻松地添加/删除条形图/饼图/等;不幸的是,它既强大又难以相处,需要长期的研究和实验才能真正掌握它
from PyQt5 import QtCore, QtGui, QtWidgets
from random import random, randrange
class ChartWidget(QtWidgets.QWidget):
def __init__(self, data):
super().__init__()
self.data = data
self.setMinimumSize(480, 320)
def paintEvent(self, event):
if not self.data:
return
columnSpace = self.width() / len(self.data)
columnWidth = columnSpace * .6
qp = QtGui.QPainter(self)
labelHeight = self.fontMetrics().height()
columnHeight = self.height() - labelHeight
for i, (value, color) in enumerate(self.data):
height = columnHeight * value
x = i * columnSpace
bar = QtCore.QRect(x, columnHeight - height, columnWidth, height)
qp.setBrush(color)
qp.drawRect(bar)
labelRect = QtCore.QRect(x, columnHeight, columnSpace, labelHeight)
qp.drawText(labelRect, QtCore.Qt.AlignLeft, 'Value {}'.format(i + 1))
class ChartView(QtWidgets.QGraphicsView):
def __init__(self, data):
super().__init__()
scene = QtWidgets.QGraphicsScene()
self.setScene(scene)
self.setRenderHint(QtGui.QPainter.Antialiasing)
pen = QtGui.QPen(QtCore.Qt.black, 1)
pen.setCosmetic(True)
for i, (value, color) in enumerate(data):
# for simplicity, I created rectangles from the *bottom*
bar = scene.addRect(QtCore.QRectF(0, 0, 600, -value * 1000))
bar.setX(i * 1000)
bar.setPen(pen)
bar.setBrush(color)
label = scene.addSimpleText('Value {}'.format(i + 1))
label.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresTransformations)
label.setX(i * 1000)
def resizeEvent(self, event):
super().resizeEvent(event)
self.fitInView(self.sceneRect().adjusted(-10, -10, 10, 10))
import sys
app = QtWidgets.QApplication(sys.argv)
data = []
for i in range(randrange(5, 10)):
data.append((
random(),
QtGui.QColor(randrange(255), randrange(255), randrange(255))))
test = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(test)
layout.addWidget(ChartWidget(data))
layout.addWidget(ChartView(data))
test.show()
sys.exit(app.exec_())
一些考虑:
- 小部件可以有孩子;您可以为单个“条”创建一个基本的小部件类,然后将它们添加到具有布局的另一个小部件中李>
- 在上面的实现中,饼图要复杂一些;还要注意,QPainter使用1/16度的单位表示角度李>
- 与为条形图创建QWidget类类似,您可以为graphics view创建QGraphicsRecItem子类,而不必像上面那样使用方便的函数
李>addRect()
- 请记住,所有图形项最初都有0x0坐标,这还包括在(100100)处创建一个矩形,该矩形项位于0x0处,但显示相对于该位置100x100处的矩形李>