qt布局调用自定义函数_【Qt学习笔记】11.自定义控件 Customize the Widget

一、窗口绘制——基本方法

自定义窗口:

Qt里允许自定义窗口控件,使之满足特殊的要求:

1、可以修改其显示,自行绘制

2、可以呈现动态效果

3、可以添加事件,支持鼠标和键盘操作

自定义的控件可以直接在Qt Designer 中使用,可以直接加到父窗口里。

步骤:

1、新建一个类,继承于QWidget和QFrame,最好是继承于QFrame

2、重写以下函数 void CellMonitor::paintEvent( QPaintEvent* event )

3、使用这个类CircleWidget::CircleWidget(QWidget *parent)

: QFrame(parent)

{

}

CircleWidget::~CircleWidget()

{

}

void CircleWidget::paintEvent(QPaintEvent * event)

{

QPainter painter(this);

painter.setBrush(QBrush(QColor(0x00, 0xff, 0x00)));

painter.drawEllipse(QPoint(100, 100), 100, 100);

}

在Qt Designer里使用:

自定义的Widget可以和原生控件一样,直接在Qt Designer里拖放布局。

1、拖放一个父类控件,如QFrame

2、Promote to ...具体化为子类控件,即可完成

84f46bd07579531c11a31ede28dbf5eb.png

0a55bb262b98557f04edec4649c0fd81.png

二、窗口的绘制——几何图形的绘制

常用:

Line:直线

Rect:长方形(含正方形)

Ellipse:椭圆(含圆形)

其他:

Arc:圆弧

Chord:封闭圆弧

polygon:多边形

RGB颜色:

在绘图时经常要指定颜色,Qt里使用RGB颜色,即由Red、Green、Blue分量定义的颜色值。每个分量0 ~ 255

如:

QColor green(0, 0xFF, 0);

QColor white(0xFF, 0xFF, 0xFF);

QColor black(0, 0, 0);

QPen与QBrush:

在Qt中,有两种绘制参数

QPen:负责线条的颜色和风格

QBrush:负责填充的颜色和风格

比如说,当画一个圆时,由当前QPen来决定线条,由QBrush来决定填充(所围区域)

注意:

1、对于非闭合形状,如Line,是不填充的

2、要先设置好色彩 然后再画,不然画完才设置颜色显然是无意义的

常用的几个类型:

QPoint:描述了一个点的坐标

QSize:描述了宽度和高度

QRect:描述了一个矩形的坐标和大小

另外,QPointF、QRectF是对应的float版本

一般情况下,后绘制的窗口会覆盖先前绘制的窗口

三、窗口的绘制——动画的实现

图画的运动:

每秒钟重绘n次,对人眼来说,它看起来就是运动的

步骤:

1、创建一个定时器

2、调用QWidget::update(),通知界面重绘

注意:

update()函数只是通知界面重新绘制,会在事件循环中产生一个“绘制事件(paint event)”,在适合的时候才会重新绘制 (简单的说,就是生成了一个重绘信号,等待响应处理,而并不是立刻重绘)

四、窗口的绘制——文字的绘制

1、颜色:QPen

2、字体:QFont

字体包含以下参数:

family(字体):如 "Times" "宋体"

size(大小):如 10  16

weight(样式): 如 QFont:Normal, QFont::Bold QFont::Light 等......

italic(斜体):true false

3、位置与对齐

painter.drawText(0, 0, width, 40 ,//指定位置

Qt::AlignHCenter | Qt::AlignVCenter,    //指定对齐等属性

"hello world")

);

示例代码:QPainter painter(this);

QFont font("Times", 20, QFont::Light, true);

painter.setPen(QColor(0xFF, 0, 0));

painter.setFont(font);

painter.drawText(0, 0, width(), height(), Qt::AlignLeft | Qt::AlignTop, "Change World");

五、窗口的绘制——图片的绘制

Qt中使用QPixmap表示图片

图片的来源:

Qt里的图片有两种加载方式:

1、文件系统中的文件

使用绝对路径或相对路径来指定

2、资源中的文件

:/xxx/Resources/XX.jpg   (使用冒号开头)

绘制参数:

1、源矩形

可以绘制图像的全部,也可以只绘制其中一部分QRect source(0, 0, img_width, img_height);    //源矩形

2、目标矩形

可以填充到全部窗口,也可以只填充到一部分QRect target(0, 0, width/2, height/2);        //目标矩形

painter.drawPixmap(target, m_picture, source);    //绘制

注意事项:

QPixmap应该作为成员变量,只加载一次,这是因为:

1、加载成本很高(开销大)

2、也没有必要反复加载

六、鼠标的支持——基本概念

Qt中鼠标事件分为四种:

1、按下    Press

2、抬起    Release

3、移动    Move

4、双击    Double Click

用QMouseEventl类表示一个鼠标事件

x,y: 坐标

globalX, globalY: 全局坐标

button: 鼠标左键、右键、中间

鼠标事件继承与 QWidget,因此重写这4个事件的处理方法,就可以自定义控件支持鼠标操作void mouseDoubleClickEvent( QMouseEvent* event );

void mouseMoveEvent( QMouseEvent* event );

void mousePressEvent( QMouseEvent* event );

void mouseReleaseEvent( QMouseEvent* event );

小练习:

写一个程序,当鼠标按下时,画出鼠标移动的轨迹,直到松开为止

按下时: m_pressedFlag = true;

移动时: 记录轨迹

松开时: m_pressedFlag = false;

效果如下:

04c7bcab77f98fa8608c6f000fa2cb1c.png

实现代码:class MyWidget : public QFrame

{

Q_OBJECT

public:

MyWidget(QWidget *parent);

~MyWidget();

private:

void paintEvent( QPaintEvent* event );

//鼠标事件

void mouseDoubleClickEvent(QMouseEvent *event);

void mouseMoveEvent(QMouseEvent *event);

void mousePressEvent(QMouseEvent *event);

void mouseReleaseEvent(QMouseEvent *event);

private:

bool m_pressedFlag;

QVector m_points;//存放轨迹的集合

};

MyWidget::MyWidget(QWidget *parent)

: QFrame(parent)

, m_pressedFlag(false)

{

m_points.resize(1024);

}

MyWidget::~MyWidget()

{

}

void MyWidget::paintEvent(QPaintEvent* event)

{

QPainter painter(this);

int width = this->width();

int height = this->height();

//背景

painter.setBrush(QColor(0, 0, 0));//黑色

painter.drawRect(0, 0, width, height);

painter.setPen(QColor(255, 255, 255));

for (size_t i = 0; i 

{

QPoint& p1 = m_points[i];

QPoint& p2 = m_points[i + 1];

painter.drawLine(p1, p2);

}

}

//忽略双击事件

void MyWidget::mouseDoubleClickEvent(QMouseEvent *event)

{

QWidget::mouseDoubleClickEvent(event);

}

void MyWidget::mouseMoveEvent(QMouseEvent *event)

{

if (m_pressedFlag == true)

{

QPoint pos = event->pos();

//显示轨迹

m_points.push_back(pos);

update();//重绘

}

}

//鼠标按下

void MyWidget::mousePressEvent(QMouseEvent *event)

{

m_pressedFlag = true;

m_points.clear();

}

//鼠标抬起

void MyWidget::mouseReleaseEvent(QMouseEvent *event)

{

m_pressedFlag = false;

}

七、鼠标的支持——发射信号

事件与信号:

自定义控件的事件发生时,应该以信号的形式发送出去。

例如:对于一个按钮,当它被按下时(press, release),对外发射了一个clicked()信号

自定义信号的方式如下:class XXX

{

signals:

void SignalName( QPoint pos );

}

当事件发生时,emit SignalName( pos );

通过emit操作将信号发射出去。

注意:emit并非C++的语法,是Qt里面自己加的概念

小练习:

区域截图:加载一张图片,选中一个区域,当鼠标松开后,发射一个信号

做出下面的效果:

b87dcb66817f979bb5bb6b8bf243f932.png

代码://MyWidget.h///

class MyWidget : public QFrame

{

Q_OBJECT

public:

MyWidget(QWidget *parent);

~MyWidget();

private:

void paintEvent( QPaintEvent* event );

//鼠标支持

void mouseMoveEvent(QMouseEvent *event);

void mousePressEvent(QMouseEvent *event);

void mouseReleaseEvent(QMouseEvent *event);

private:

bool m_pressedFlag;

QPixmap m_picture;

QPoint m_curPos;//鼠标当前位置

QPoint m_begin;

QPoint m_end;

};

///MyWidget.cpp///

MyWidget::MyWidget(QWidget *parent)

: QFrame(parent)

,m_pressedFlag(false)

,m_begin(QPoint(0, 0))

,m_end(QPoint(0, 0))

{

m_picture.load("./Resources/MyPic.jpg");  //开销大,写在构造函数中,只加载一次,而不是写在paintEvent中,每次显示

}

MyWidget::~MyWidget()

{

}

void MyWidget::paintEvent(QPaintEvent* event)

{

QPainter painter(this);

int width = this->width();

int height = this->height();

//显示背景图片

painter.drawPixmap(0, 0, width, height, m_picture);

//画出区域

if (m_pressedFlag == true)

{

//画一个十字交叉线

painter.setPen(QColor(255, 0, 0));//红色的区域为十字交叉线

painter.drawLine(QPoint(0, m_curPos.y()), QPoint(width, m_curPos.y()));//水平

painter.drawLine(QPoint(m_curPos.x(), 0), QPoint(m_curPos.x(), height));

//画出选中的区域

QRect selected(m_begin, m_end);

painter.setPen(QColor(0, 0, 0));

//painter.setBrush(QColor(100, 100, 100));

painter.drawRect(selected);

}

}

//鼠标移动

void MyWidget::mouseMoveEvent(QMouseEvent *event)

{

if (m_pressedFlag = true)

{

m_curPos = event->pos();

m_end = m_curPos;

update();

}

}

//鼠标按下

void MyWidget::mousePressEvent(QMouseEvent *event)

{

m_pressedFlag = true;

m_begin = event->pos();

m_end = m_begin;

}

//鼠标抬起

void MyWidget::mouseReleaseEvent(QMouseEvent *event)

{

m_pressedFlag = false;

}

class Test11_7a_12_12 : public QMainWindow

{

Q_OBJECT

public:

Test11_7a_12_12(QWidget *parent = Q_NULLPTR);

private:

Ui::Test11_7a_12_12Class ui;

private slots:

int OnSelected(QRect area);

};

Test11_7a_12_12::Test11_7a_12_12(QWidget *parent)

: QMainWindow(parent)

{

ui.setupUi(this);

//关联自定义的控件发出的信号

assert(

connect(

ui.frame,

SIGNAL(selected(QRect)),

this,

SLOT(OnSelected(QRect))

)

);

}

int Test11_7a_12_12::OnSelected(QRect area)

{

return 0;

}

八、示例——正弦曲线

什么是曲线:

两种视角:

1、一个曲线是由无数个点组成的 ( 可以用drawPaint()方法 )

2、一个曲线是由无数个线段组成的。 ( 可以用drawLine()方法 )

这里采用第二种视角,也就是线段的画法,很多小线段首位连接在一起,形成一条“准”曲线

直角坐标系:

1、定义坐标原点

origin()

2、坐标转换

GUI: 左上角为原点,X向右增长,Y向下增长

数学坐标系:中心为原点,X向右增长,Y向上增长

代码:

github:https://github.com/HonestFox/Qt/tree/master/12_13MySin

(提示: QSpainBox)

九、示例——自定义进度条

由于Qt自带的QSlider有点简陋,这里自己实现一个MySlider,实现以下功能

高亮状态:

鼠标移入后,进入高亮状态

鼠标移出后,回到普通状态

触发事件: :void focused(bool yes)

鼠标点击:

当鼠标点击后,设置新的位置,并触发事件:void clicked(int progress)

位置显示:

给出提示框,动态显示当前位置

void tracking(int progress, QPoint pos)

(要实现这个功能,需要单独新建一个类)

getter / setter :

可以设置、获取当前值和范围

void setRange(int range);

void setProgress(int progress);

int range() const;

int progress() const;

效果:

dfe6555664a72cff07ca8827a45631fe.png

a492b0645ea74358902a98873acc1b0e.png

代码:

github:https://github.com/HonestFox/Qt/tree/master/12_14MySlider

十、实战——屏幕截图工具

需求:

1、隐藏自身窗口

2、自由选区

3、保存为jpg文件

实现:

1、截图

Qt中的  QPixmap::grabWindow( QApplication::desktop()->winId() );方法

2、自由选区

以一个全屏的对话框来展示截图

用鼠标来选区一个区域:记录开始点、结束点

3、特效:快门声

QSound m_shutter("xxxx.wav");

m_shutter.play();

4、延时

QTimer::singleShot(300, this, SLOT(onDelayedCapture()));

为什么要延时呢,因为当我们点击截图按钮,按道理是界面隐藏,紧接着打开一个记录屏幕的窗口的,

但是,界面的隐藏是一个过程,因此需要延时。

5、保存图片

QPixmap picture = ui.frame->pixmap();

picture.save(filename);

效果:

a9c7c0063aba0abc5bd7842166ab7813.png

059cb9eb3537b75e6766e6df83f90127.png

afc3ef15797ba07be7c2787d5e424fe1.png

57aa2fd167d43d8b5c8921f69c03e25e.png

代码:

github:

————————————

尾巴

尽量少造轮子,多读文档。

像Qt这种庞大的框架,往往有大量现成的轮子


版权声明:本文为weixin_36409041原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。