首页 Qt 学习之路 2 Qt 学习之路 2(24):Qt 绘制系统简介

Qt 学习之路 2(24):Qt 绘制系统简介

90 11.8K

Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainterQPainterDeviceQPaintEngine三个类。

QPainter用来执行绘制的操作;QPaintDevice是一个二维空间的抽象,这个二维空间允许QPainter在其上面进行绘制,也就是QPainter工作的空间;QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine类应用于QPainterQPaintDevice之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心QPaintEngine这个类的。我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了QPaintEngine类,这个类让不同的纸张、屏幕都能使用一种画笔。

下图给出了这三个类之间的层次结构(出自 Qt API 文档):

Qt 绘图系统

上面的示意图告诉我们,Qt 的绘图系统实际上是,使用QPainterQPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。

下面我们通过一个实例来介绍QPainter的使用:

//!!! Qt4/Qt5

class PaintedWidget : public QWidget
{
    Q_OBJECT
public:
    PaintedWidget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

注意我们重写了QWidgetpaintEvent()函数。这或许是我们在理解了 Qt 事件系统之后首次实际应用。接下来就是PaintedWidget的源代码:

//!!! Qt4/Qt5

PaintedWidget::PaintedWidget(QWidget *parent) :
    QWidget(parent)
{
    resize(800, 600);
    setWindowTitle(tr("Paint Demo"));
}

void PaintedWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawLine(80, 100, 650, 500);
    painter.setPen(Qt::red);
    painter.drawRect(10, 10, 100, 400);
    painter.setPen(QPen(Qt::green, 5));
    painter.setBrush(Qt::blue);
    painter.drawEllipse(50, 150, 400, 200);
}

在构造函数中,我们仅仅设置了窗口的大小和标题。而paintEvent()函数则是绘制的代码。首先,我们在栈上创建了一个QPainter对象,也就是说,每次运行paintEvent()函数的时候,都会重建这个QPainter对象。注意,这一点可能会引发某些细节问题:由于我们每次重建QPainter,因此第一次运行时所设置的画笔颜色、状态等,第二次再进入这个函数时就会全部丢失。有时候我们希望保存画笔状态,就必须自己保存数据,否则的话则需要将QPainter作为类的成员变量。paintEvent() 作为重绘函数,会在需要重绘时由 Qt 自动调用。“需要重绘”可能发生在很多地方,比如组件刚刚创建出来的时候就需要重绘;组件最大化、最小化的时候也需要重新绘制;组件由遮挡变成完全显示的时候也需要等等。

QPainter接收一个QPaintDevice指针作为参数。QPaintDevice有很多子类,比如QImage,以及QWidget。注意回忆一下,QPaintDevice可以理解成要在哪里去绘制,而现在我们希望画在这个组件,因此传入的是 this 指针。

QPainter有很多以 draw 开头的函数,用于各种图形的绘制,比如这里的drawLine()drawRect()以及drawEllipse()等。当绘制轮廓线时,使用QPainterpen()属性。比如,我们调用了painter.setPen(Qt::red)将 pen 设置为红色,则下面绘制的矩形具有红色的轮廓线。接下来,我们将 pen 修改为绿色,5 像素宽(painter.setPen(QPen(Qt::green, 5))),又设置了画刷为蓝色。这时候再调用 draw 函数,则是具有绿色 5 像素宽轮廓线、蓝色填充的椭圆。

运行一下我们的程序,可以看到最终效果:

绘制示例

我们会在后面的章节详细介绍画笔QPen和画刷QBrush的属性。

另外要说明一点,请注意我们的绘制顺序,首先是直线,然后是矩形,最后是椭圆。按照这样的绘制顺序,可以看到直线是第一个绘制,位于最下一层;矩形是第二个绘制,在中间一层;椭圆是最后绘制,在最上层。

如果了解 OpenGL,肯定听说过这么一句话:OpenGL 是一个状态机。所谓状态机,就是说,OpenGL 保存的只是各种状态。比如,将画笔颜色设置成红色,那么,除非你重新设置另外的颜色,它的颜色会一直是红色。QPainter也是这样,它的状态不会自己恢复,除非你使用了各种设置函数。因此,如果在上面的代码中,我们在椭圆绘制之后再画一个矩形,它的样式还会是绿色 5 像素的轮廓线以及蓝色的填充,除非你显式地调用了设置函数进行状态的更新。这是大多数绘图系统的实现方式,包括 OpenGL、QPainter以及 Java2D。正因为QPainter是一个状态机,才会引出我们前面曾经介绍过的一个细节问题:由于paintEvent()是需要重复进入的,因此,需要注意第二次进入时,QPainter的状态是不是和第一次一致,否则的话可能会造成闪烁的现象。这个闪烁并不是由于双缓冲的问题,而是由于绘制状态的快速切换。

90 评论

锁骨断了 2012年10月30日 - 18:06

嗯 Qt的绘图系统强大而灵活,在最新的Qt5里面,这套绘图系统通过scenegraph,在底层Qpa插件提供的绘图区上绘制图形。同时通过Qpa插件实现不到10个虚函数就可以实现硬件加速,比起以前的QWS方便太多了

回复
DevBean 2012年10月30日 - 22:24

感谢指出!

回复
Abigale 2013年1月10日 - 17:39

我在用qt进行画图,是根据时间不断变化的,然后要把画出来的图像附加在一个视频上。也就是除了画图的部分,别的部分要是透明的,能不能劳驾楼主给点思路。我在用Qpainter来画图,可以实现我想要的功能吗?

回复
豆子 2013年1月10日 - 22:14

如果是单纯的图像是可以的,你可以使用 QPixmap 覆盖到视频上,只要坐标一致就可以了,不知道这样可不可以。

回复
Abigale 2013年1月11日 - 08:59

我去试试,我是画圆圈,视频和圆圈的坐标是一致的!可是你说使用QPixmap是什么意思呀?我是打算把 QPainter painter(this);中的this换成我要附加的那个视频的窗口!

回复
豆子 2013年1月11日 - 09:24

QPixmap 就是一个图像类,可以使用 QPainter 在上面绘制,并且 QPixmap 可以使用 fill() 函数进行底色填充,只要使用 Qt::transparent 就是透明色了,然后再使用 QPainter 进行绘制。不过具体还是看你的需求。

回复
Abigale 2013年1月11日 - 09:58

谢谢指导!我去试试~~

Abigale 2013年1月11日 - 13:31

QPixmap pixmap;
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.drawEllipse(24,24,20,20);
我把paintEvent重写成这样了,可是会报错!我哪里理解错了?

豆子 2013年1月11日 - 13:40

不要在 paintEvent 里面这样写。如果你要用 QPixmap 的话,直接放在构造函数里面就可以了。

Abigale 2013年1月14日 - 13:29

想请教下楼主,如何用qt来播放视频(mp4格式的)呀?现在把圈圈画好了,准备合并试试的时候,才发现我不知道如何加载视频!囧~~

回复
豆子 2013年1月14日 - 15:44

可以考虑使用 mplayer 进行播放,只要有解码器就可以了。

回复
Abigale 2013年1月14日 - 17:07

我用phonon来操作,但是无法显示视频!
QWidget *widget = new QWidget;
widget->setWindowTitle("Video Player");
widget->resize(400,400);

Phonon::VideoPlayer *player = new Phonon::VideoPlayer(Phonon::VideoCategory, widget);
player->load(Phonon::MediaSource("../Puppet.mp4"));

player->play();

widget->show();

回复
豆子 2013年1月15日 - 11:19

这个不大清楚,先测试下 phonon 是否正常工作,比如用一个肯定存在的解码器播放,然后再检查下有没有 mp4 解码器。

回复
Abigale 2013年1月15日 - 13:31

O(∩_∩)O谢谢我试过了,是因为格式的关系,如果是mpg格式的话就可以了!
可是,透明化的实现很有问题呀,我用你提到的map.fill(Qt::transparent)来做透明化处理,可是根本没有效果!
QRect re = Event->rect();
QPixmap map(re.size());
map.fill(this, re.topLeft());

QPainter p;
p.begin(&map);
p.setPen(QPen(Qt::blue,2,Qt::SolidLine));
p.draEllipse(0,0,20,20);
p.end();

p.begin(this);
p.drawPixmap(0,0,map);
p.end();
加载视频,然后整个画面就只能显示画的圈,看不到视频,透明化的效果看不到。

豆子 2013年1月16日 - 09:46

你这段代码是什么意思啊?QPixmap 的 fill() 并没有使用 Qt::transparent 填充啊

Abigale 2013年1月16日 - 09:51

QRect re = Event->rect();
QPixmap map(re.size());
map.fill(this, re.topLeft());
map.fill(Qt::transparent);
我是在这个位置写的透明化,因为前面提到了就没有打出来。

回复
豆子 2013年1月16日 - 16:00

那 fill(this, re.topLeft()) 的作用是什么呢?

回复
cwq 2013年1月25日 - 17:10

“因此,需要注意第二次进入时,QPainter 的状态是不是和第一次一致,否则的话可能会造成闪烁的现象。这个闪烁并不是由于双缓冲的问题,而是由于绘制状态的快速切换。”——请问一下,这里提到的“双缓冲问题”是什么意思?

回复
豆子 2013年1月27日 - 21:16

如果绘图系统没有进行双缓冲的话,同样会照成闪烁。此处的闪烁并不是双缓冲所带来的,而是由于状态切换。所谓双缓冲,是在进行绘制时,不直接绘制在屏幕上,而是先在内存中绘制完全,然后将内存中完整的图像一次性绘制出来,这么做的好处是不会由于复杂的绘制命令而带来闪烁。具体可以参考图形学的基本教程。

回复
fc88 2014年6月25日 - 14:45

后来才发现的闪烁不是双缓冲之类的问题。我经常是用局部重绘解决。但是为什么状态的的切换会影响界面的显示呢? 我觉得不是只有更新图像才会导致的画面的显示问题么?

回复
豆子 2014年6月26日 - 13:20

如果是 repaint 的问题,一般只要窗口有变化,比如大小变化之类,都会重新调用 repaint,并不一定是更新图像才会。

回复
hilbet 2013年6月19日 - 23:16

博主很多文章的代码都没给全,这让初学者很头痛。
这一章的例子在主函数的文件里是不是有特别声明啊,我将文中的代码分别写入painter.h和painter.cpp文件中然后主函数是:
#include "painter.h"
#include
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
PaintedWidget painter;
painter.show();
return app.exec();
}
编译后出来一个什么都没有的框,麻烦博主诊断一下

回复
hilbet 2013年6月20日 - 01:15

不知道为什么又正常了,真是不好意思,博主无视我吧

回复
恒古炎 2013年9月22日 - 19:22

我在使用QPaint::drawPixmap方法载入图片时,上次那个中文字符通不过的问题又出现了,我的编码是UTF-8,也调过了其它编码,但是其它编码更加不行 OTL 编译环境为Qt5 很疑惑偶数中文就通过了…… 请问这是为什么

回复
豆子 2013年9月22日 - 20:03

这个问题也挺奇怪的,不知道哪里就会出现,一般是编码问题,注意下 UTF-8 格式的话 BOM 有没有添加之类的

回复
恒古炎 2013年9月23日 - 13:11

QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));//在main函数QApplication实例化之前加上就搞定了 www 困扰多久的问题终于没了

回复
恒古炎 2013年9月23日 - 13:34

晕了 最后一个字是“一”才行 刚刚好编码对上了吗…… 结果中文问题实在搞不定,请问下BOM在Qt creater里怎么设置啊?

回复
豆子 2013年9月23日 - 15:26

选项的 Text Editor 里面的 Behavior 里面有关于 BOM 的设置,尝试始终添加或者始终删除看看。Windows 平台上的某些程序不能识别带有 BOM 的 UTF-8 文件,看看是不是这里的问题

回复
恒古炎 2013年9月23日 - 19:01

三种都试过了,还是一样的结果,偶数汉字可通过 奇数汉字就通不过。这问题VS上面会有吗?实在不行只能用VS了 OTL 最好能解决这个问题就好了

wbin 2013年9月24日 - 14:26

void PaintedWidget::paintEvent(QPaintEvent *)
paintEvent实现时为什么参数中QPaintEvent* 不需要指定形参名,我把它改成void PaintedWidget::paintEvent(QPaintEvent *e),编译是会 警告:C4100: “e”: 未引用的形参。

回复
豆子 2013年9月24日 - 16:41

C++ 里面如果函数参数没有使用,则会有这样的警告。大多数编译器可以选择忽略这种警告,因为这种警告无伤大雅。另外的方法就是函数实现不写形参名字,这样编译器也不会有警告。所以不写形参名仅仅是为了规避编译器警告,没有其它意义。具体详见 C++ 基础

回复
wbin 2013年9月24日 - 16:46

那加上这个参数,是不是为了覆盖父类的paintEvent

回复
豆子 2013年9月24日 - 19:13

是的,C++ 的覆盖要求函数签名必须一致

回复
wbin 2013年9月24日 - 19:14

谢谢

回复
小陈 2013年12月20日 - 11:07

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPainter painter(this);
painter.begin(this);
painter.drawLine(80,100,650,500);
painter.setPen(Qt::red);
painter.drawRect(10, 10, 100, 400);
painter.setPen(QPen(Qt::green, 5));
painter.setBrush(Qt::blue);
painter.drawEllipse(50, 150, 400, 200);
painter.end();
}我是写在这 出现这种问题且没有图
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
QPainter::drawRects: Painter not active
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
QPainter::end: Painter not active, aborted

写在这void PaintedWidget::paintEvent(QPaintEvent *)
{

}
也没图不过没上面的那问题

回复
豆子 2013年12月21日 - 00:40

对于 QWidget,QPainter 只能用在 paintEvent() 函数中,所以你写在构造函数中是不可以的。另外,我将你的这段代码放到 paintEvent() 中是正常的,不知道你是不是哪里写错了,比如函数名不对等。

回复
vikione 2014年2月7日 - 02:53

博主请问这个例子中void PaintedWidget::paintEvent(QPaintEvent *)这个函数是何时被调用的,我的main函数如下(没有调用这个paintEvent事件函数,为什么程序能画出图来?):
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PaintedWidget w;
w.show();
return a.exec();
}

回复
豆子 2014年2月7日 - 13:56

paintEvent() 是重绘函数,会在需要重绘时自动调用。“需要重绘”可能发生在很多地方,比如组件刚刚创建出来的时候就需要重绘;组件最大化、最小化的时候也需要重回;组件由遮挡变成完全显示的时候也需要重回,等等。所以,尽管你没有手动调用这个函数,Qt 也是会自动调用的。

回复
Alien You 2017年5月5日 - 10:30

博主,我觉得您这一点应该在上面说明一下吧,不然真的不知道是怎么调用的

回复
scosdq 2014年2月7日 - 23:23

请问楼主给出的两段代码分别应该写在什么地方,是都放在源文件里就行还是有的需要放在头文件里?另外能不能麻烦楼主给下源文件中其余部分的代码哈,就是一系列#include和main函数~
刚接触QT,想通过些完整的例子来熟悉图形界面功能~

回复
豆子 2014年2月11日 - 16:18

按照 C++ 标准来就可以了。函数的实现放在 cpp 文件中,然后把函数声明复制到头文件中就可以。本系列中绝大部分代码都是完整的,不过还是需要进行类似的声明操作,仅是为了减少文章篇幅而已。

回复
lee 2014年3月10日 - 22:58

在mainwindow如何使用QPainter画图,求指点。 我在一个成员函数里面像下面这样写,但是报错。 QPainter painter(this->viewport());
QImage qimg;
qimg.load(fileName);
painter.drawImage(0, 20, qimg);

回复
lee 2014年3月10日 - 22:59

QmainWindow 打错了 求指教

回复
豆子 2014年3月11日 - 21:28

具体是什么错误?

回复
lllllzx 2014年3月14日 - 15:12

main.obj:-1: error: LNK2019: 无法解析的外部符号 "public: __cdecl PaintedWidget::PaintedWidget(class QWidget *)" (??0PaintedWidget@@QEAA@PEAVQWidget@@@Z),该符号在函数 main 中被引用
这个报错是怎么回事啊?
完全照着博主给的函数打的
=======================
#include "paintedwidget.h"
#include

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

PaintedWidget w;
w.show();

return a.exec();
}===========================
这是主函数

回复
lllllzx 2014年3月14日 - 15:24

clear MakeFile后好了。
建议博主在源代码里还是加上头文件比较好
感谢

回复
豆子 2014年3月17日 - 09:08

主要是头文件都比较简单,所以没有添加。以后注意下这个问题 ;-P

回复
太难学了 2014年4月18日 - 09:36

博主,我想要在图片(图像是用opencv的Mat存储的)画框框和时间,请问用什么比较好,(我用opencv画出来效果不是很好,特别是时间的显示上)

回复
豆子 2014年4月21日 - 11:03

OpenCV不是很了解,如果要显示图片的话,直接在 paintEvent 里面绘制不可以吗?

回复
小津 2014年6月1日 - 02:42

想问一下豆子,这篇教程建议前备知识除了C++还有那些?

回复
豆子 2014年6月3日 - 09:27

基本只需要 C++ 了,如果有其它框架的使用经验应该更好一些,毕竟有些术语是通用的。

回复
小津 2014年6月3日 - 09:37

谢谢,要好好学习了~

回复
雨后星辰从 2014年7月23日 - 17:35

你好,请问qt5的QQuickView对象怎样像QWidget对象那样设置主窗口背景透明呀?
例如QWidget对象的widget.setAttribute ( Qt::WA_TintedBackground )这样效果,以前qt4的时候QDeclarativeView也是集成QWidget的,这个就 没有疑问了,但是现在qt5就找不到怎么弄了

回复
豆子 2014年7月26日 - 09:26

Qt 5.1 的 QQuickView 增加了 setOpacity() 函数,可以设置透明色。另外,Qt 5.3 增加了 QQuickWidget 类,直接继承了 QWidget。如果能够升级 Qt 的话,可以选择这些解决方案试试。

回复
czcysmusic 2014年7月24日 - 17:01

你好!paintEvent是由QT框架负责调用的,那么在paintEvent函数中,在堆中创建QPainter *p = QPainter(this),那么以后每次调用paintEvent函数,都会重新创建新的QPainter指针,虽然有指明this,但是按照您之前的说法,得等到父对象关闭,这些内存才会得到释放,那这样的话,每调用一次paintEvent,就得在堆中新创建的话一个新的QPainter指针??是吗?

回复
豆子 2014年7月25日 - 23:32

按照 C++ 标准,应该是这个样子的。所以在 paintEvent() 函数里面,一般是使用栈上创建 QPainter,而不是堆上。

回复
kkLY 2015年3月7日 - 00:54

老师你好,
我初学qt 尝试一个显示地图上路径的程序
重写了用于显示地图对话框的paintEvent函数,但是运行时是一片空白
调试时发现他无限调用paintEvent函数还会出现找不到QPen.cpp等情况。。。。
这个问题对我来说有的无从下手,尝试了多个方法也没解决。
请问这个情况该怎么办?

附上paintEvent:
void paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawPixmap(0, 0, width(), height(), QPixmap(":/dmap/data/jpg.png"));

QPen pen(Qt::red, 3);
painter.setPen(pen);

for (int i = 0; i pos(), map.label_[line[i+1]+1]->pos());

QPoint p = map.label_[line[line.size() - 1]]->pos();
QPoint d1(10, -10), d2(10, 10);
painter.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin));
painter.drawLine(p, p + d1);
painter.drawLine(p, p + d2);
painter.drawLine(p, p - d1);
painter.drawLine(p, p - d2);

}

回复
kkLY 2015年3月7日 - 21:09

问题解决了 无视我吧_(:3」∠)_

回复
lumanman 2015年4月25日 - 17:22

楼主,你好!我用Qt做界面,需要在图片上叠加一些数字,然后把叠加了数字的图片显示出来,这个该怎么做呀?

回复
豆子 2015年4月27日 - 09:15

可以在调用QPainter的构造函数时传入图片指针,创建QPainter对象,然后进行绘制。

回复
干粮 2015年5月17日 - 13:01

楼主,你好!我才开始用QT,想写一个槽函数,用来画波形图,但是paintEvent()会在编译完就画出来,有什么办法解决?

回复
豆子 2015年6月3日 - 09:48

paintEvent() 会在窗口一出现就被调用。因此你需要在里面判断,当有数据时进行绘制,没有的话就为空。当获取数据完毕时,调用 repaint() 函数重绘。

回复
lumanman 2015年6月3日 - 10:04

你好,那在paintEvent()函数中如何判断有没有数据呢?还有如果没有数据的话,怎么做才能使判断语句后面的窗口绘制语句不被执行呢?

回复
干粮 2015年6月3日 - 10:12

谢谢豆子!
我最后是调用updata()函数实现数据刷新的,一开始因为赋值为0,画出来也没啥东西,这样运行后数据有了才显示出来了!

回复
朱云 2015年7月28日 - 15:02

作为新手看的好辛苦哦

回复
tim 2016年6月1日 - 10:46

在paintEvent绘图我理解,但我自己构造一个函数,然后用QPainter画图,为什么就不显示,这个我死了想不通
class myWindows:public QWidget
{
public:void mypaint();//按键画一个图
}

回复
豆子 2016年6月3日 - 12:42

mypaint() 函数需要在 paintEvent() 中调用才可以,Qt 的绘制机制就是在 paintEvent() 完成的,很多 GUI 库都是这么实现的。具体原因未知,可能会有性能方面的考虑。

回复
花火 2016年11月15日 - 17:02

楼主你好,我想要绘制迷宫 并且绘制已经搜索出的路程,那我是不是可以分别在我的函数里写painter.drawLine(),然后再从QpaintEvent()中调用迷宫生成函数和迷宫寻路函数?如果可以这样,那我怎样在点击控件的时候选择调用哪个函数?谢谢楼主

回复
豆子 2016年11月19日 - 10:46

可以直接在 paintEvent() 中写这些绘制函数。同时需要设置一个标记位,点击按钮改变标记位,然后选择不同的绘制函数。由于这种绘图机制是一个状态机,所以只能用这种方式实现。具体类似于:

if (isDrawingMap) {
drawMap();
} else if (isDrawingElse) {
drawElse();
}

回复
浮游 2016年12月30日 - 10:48

豆子哥,绘图的程序是放在绘图事件中的,那当程序运行的时候会自动触发这个事件进行绘图吗,如果是这样,那默认的绘图事件的实现是不是就是不绘制任何东西

回复
豆子 2017年1月4日 - 09:11

可以说是的,不过默认绘图事件有可能会绘制组件原本的样式,比如灰色背景等。

回复
Alien You 2017年5月5日 - 10:51

博主,对于这句话“由于paintEvent()是需要重复进入的,因此,需要注意第二次进入时,QPainter的状态是不是和第一次一致,否则的话可能会造成闪烁的现象。这个闪烁并不是由于双缓冲的问题,而是由于绘制状态的快速切换”能不能写一个例子,我想试试,但是写的都程序奔溃了

回复
豆子 2017年5月6日 - 11:46

这个你写的程序为什么崩溃了?

回复
uXer 2017年5月7日 - 21:56

博主,我最近在写一个程序,绘图代码不在paintEvent()中,但是提示老是报错,虽说是重载呃错误,但是我好久没解决掉,下面给源码:(最后一行提示重载错误,但是解决不了,试了好多方法,求指教)
QImage *image = new QImage();
image->load(picturePath);
QImage *temp = new QImage(image->width(),image->height(),QImage::Format_RGB888);
QPainter *painter = new QPainter(image);
grayImg->toPixelFormat(QImage::Format_RGB888);
painter->drawImage(0,0,temp,0,0,0,0,Qt::AutoColor);

回复
豆子 2017年5月8日 - 23:07

drawImage() 函数需要的是 QImage &,不是 QImage *

回复
WeiXin 2017年12月25日 - 16:58

请问,我现在有100张图片,每张图片的像素点我提取出来了,想用这些像素点绘制一个三维的图片,怎么可以做到呢?

回复
豆子 2017年12月28日 - 21:37

这个需要了解计算机图形学的相关知识了,具体需要去看看图形学的相关内容,我也不是很清楚具体的实现细节。

回复
小房子 2018年3月20日 - 14:17

豆子个,久仰大名!!!
我在一个widget中使使用paintevent函数绘图,初始化实现一次绘图!
程序运行过程中,用户需要对画图进行更改,
比如把初始化图形中某一个三角形改为圆形,
但是widget的绘图需要在paintevent中进行,我目前想到再次调用paintevent函数,用状态机原理吗?不同状态下执行paintevent的不同部分?还有没有其他方法啊!

回复
豆子 2018年3月25日 - 09:37

没有其他方法,一般就是使用类似状态机的设计,设置标记位等。

回复
学生大琪 2018年8月3日 - 11:58

18年的留言欸~看到这里为啥我觉得...没想到豆子还活着这样的感觉。。。哈哈哈哈,anyway 很感谢,对我帮助很大的一个文档整理。虽然我是一个技术小白,但是不知为何有一种莫名的自信。谢谢啦,会好好利用这个文档的。感谢。

回复
wark 2019年5月17日 - 12:15

手动调用update或者repaint也会自动调用paintevent吗?

回复
豆子 2019年5月21日 - 13:27

会的

回复
stevin 2019年8月16日 - 15:52

豆子大神,你好:
我有一个需求:需要在播放的视频中实时绘制人脸框,即人脸框的位置和大小会随着播放时间不断变化,所以我需要在每次执行paintEvent()时先清除屏幕上之前绘制的人脸框然后再重新绘制新的人脸框。请问,我怎么才能在每次执行paintEvent()时先清除屏幕上之前绘制的人脸框呢?我现在每次调用repaint()方法通知paintEvent()进行绘制时,先前绘制的人脸框一直都在,会不断累积。

回复
豆子 2019年8月16日 - 19:41

这个可以考虑在第二次绘制之前,设置为异或模式先将上次的人脸框绘制,然后再进行第二次绘制。参考“橡皮筋技术”的原理,感觉应该可以实现

回复
stevin 2019年8月17日 - 12:55

您好,感谢回复,还有一个问题想请教一下:当我把页面设置为透明背景后,执行scene->removeItem无效。现在我不知道怎么解决这个问题,下面是我的代码描述。

我自定义了一个QGraphicsView类,并在构造函数中添加如下代码
//设置背景透明:
setAttribute(Qt::WA_TranslucentBackground, true);
//创建场景
QGraphicsScene *scene = new QGraphicsScene(this);
scene->setSceneRect(0,0,1920,1080);
//创建视图项并添加到组
QGraphicsItemGroup *group = new QGraphicsItemGroup();
QGraphicsRectItem *item = new QGraphicsRectItem(0,0,20,20);
item->setBrush(Qt::green);
group->addToGroup(item);
//将组添加到场景
scene->addItem(group);
setScene(scene);

然后我写了一个定时器,3秒后执行如下代码:
scene->removeItem(group);

结果是屏幕上面无任何变化,如果把上面的setAttribute(Qt::WA_TranslucentBackground, true);注释掉就不会有问题,请问是什么原因呢,期待您的回复,谢谢了

回复
yu 2020年8月4日 - 11:32

现在评论还有人理我么,同样是只有空白窗口。怎么回事啊?

回复
yu 2020年8月4日 - 11:32

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PaintedWidget pw;
pw.show();

return a.exec();
}

回复
holiday 2020年10月28日 - 11:39

博主还在看吗?想请教一下,怎么动态逐点绘制波形图,目前我的代码一运行就是直接是图形,设置了timer,update但是貌似没起到作用。功能代码写在了paintevent里面了。请指点,万分感谢。

回复
豆子 2020年10月31日 - 21:19

你的 paintEvent() 的绘制需要由一个变量控制显示哪些点,例如用于显示点的数据结构添加一个 visible 属性,每次调用更改一个,只绘制 visible 为 true 的点,类似这样的实现可能是可以的。

回复
Lucas 2021年4月21日 - 17:19

豆子老师,我想请教个问题,就是QSystemTrayIcon在deepin 15.11,x11桌面环境上geometry()返回(0,0,-1,-1),windows,统信这些系统是可以返回tray的geometry的,请教一下怎么解决

回复

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2