一、窗口绘制——基本方法
自定义窗口:
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 ...具体化为子类控件,即可完成
二、窗口的绘制——几何图形的绘制
常用:
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;
效果如下:
实现代码:
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<QPoint> 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 < m_points.size() - 1; ++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里面自己加的概念
小练习:
区域截图:加载一张图片,选中一个区域,当鼠标松开后,发射一个信号
做出下面的效果:
代码:
//////////////////////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;
效果:
代码:
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);
效果:
代码:
github:
https://github.com/HonestFox/Qt/tree/master/12_14ScreenShot
————————————
尾巴
尽量少造轮子,多读文档。
像Qt这种庞大的框架,往往有大量现成的轮子
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。