首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在没有Matplotlib或PyQtChart等库的Python中实现图表?

如何在没有Matplotlib或PyQtChart等库的Python中实现图表?
EN

Stack Overflow用户
提问于 2021-04-21 10:53:09
回答 1查看 1.4K关注 0票数 0

作为我学习的一部分,我开始了一个编程项目。我的项目将是一个数据可视化程序,必须至少能够可视化或“绘制”一个线状图,可能还有其他图表,如条形图或饼形图。但是,我不允许使用Python库来简化操作(例如Matplotlib、QtDesigner、NumPy或PyQtChart)。可视化应该用PyQt来完成。

这个项目对我来说似乎很难,因为我对编程很陌生。我想知道是否有人有类似的编程项目或技巧的经验,以帮助我接近这个项目。提前感谢!

另外,如果这不是StackOverflow的相关问题,请告诉我,我会把它记下来。

EN

回答 1

Stack Overflow用户

发布于 2021-04-21 12:25:34

考虑到这些约束,剩下的唯一解决方案是通过QPainter使用定制小部件绘图,或者使用图形视图框架,后者功能更强大,但也要复杂得多。

在下面的两个简单示例中,您可以看到两种方法之间的一些差异,这将显示相同的数据。

QWidget子类通常要简单得多,因为您可以直接绘制基本形状(包括椭圆、圆弧和馅饼)。您只需实现paintEvent (请记住,小部件上的QPainter只能在事件中发生!不能单独调用paintEvent() )在小部件上构造一个QPainter,然后开始绘图。

这种简单性的缺点是,如果您需要更高的模块化和更好的控制,它可能会导致更麻烦的修改。

Graphics View (这正是QtCharts所使用的)允许更高的可扩展性,因为它提供了一个面向对象的框架(使用QGraphicsItem子类),因此允许对现有数据的操作,因此您可以更容易地添加/删除条形码/pies/等等;不幸的是,它功能强大,很难相处,它需要长时间的学习和实验才能开始真正掌握它。

代码语言:javascript
复制
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_())

一些考虑因素:

  • 小部件可以有子部件;您可以为单个"bar“创建一个基本的小部件类,然后使用布局将它们添加到另一个小部件中;
  • 在上面的实现中,饼图要复杂一些;还请注意,QPainter使用1/16度的单位来表示角度;
  • 类似于为条形图创建QWidget类,您可以为图形视图创建一个QGraphicsRectItem子类,而不是像上面那样使用方便函数addRect()
  • 请记住,所有图形项最初都有0x0坐标,这还包括在(100,100)处创建一个矩形,这是一个位于0x0的矩形项,但相对于该位置,该矩形在100x100处显示;
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67194411

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档