一、vs+qt自定义控件制作和出现“无法打开源文件”解决办法
QT创建自定义控件见此视频所示:第三天02_自定义控件__onekeybatch_哔哩哔哩_bilibili
vs也与此类似。
两者创建过程中都会遇到的一个问题:1.无法打开源文件“.h” 2.main文件和父窗口cpp文件都无法打开包括文件“.h”
解决方法:1.对于qt制作的一般没有此问题,若出现问题。
(1)在父窗口添加子窗口头文件。(2)默认编译完成的ui_XXX.h中显示的自定义控件类名称为#include<XXX>,显示无法识别,但主动修改成#include"XXX"可以识别。
2.vs+qt出现此问题,编译显示错误,点击取消后会进入一个ui_“父窗口名”.h的文件
(也可在外部引用依赖项找到)修改#include<XXX>为#include"XXX"
但是主动修改成#include"XXX"可以识别,请每次编译后都要手动修改。
解决方案:1.在qt中完成自定义控件界面设计,用vs打开qt项目进行编译,最终没有错误。
2.vs+qt进行页面设计是不要勾选全局。
二、QString字符串拼接
1、
2、 // 字符串拼接 QString().arg()
// %1, %2, %3 -- 占位符
三、鼠标事件
1.QMouseEven的button和buttons,要分清两者使用区别(瞬间和持续状态)
//鼠标进入
void MyLabel::enterEvent(QEnterEvent*)
{
setText("你好!!!!!!!!");
}
void MyLabel::leaveEvent(QEvent*)
{
setText("拜拜");
}
void MyLabel::mousePressEvent(QMouseEvent* ev)
{
QString btn;
if (ev->button() == Qt::LeftButton)
{
btn = "LeftButton";
}
else if (ev->button() == Qt::RightButton)
{
btn = "RightButton";
}
else if (ev->button() == Qt::MiddleButton)
{
btn = "MiddleButton";
}
QString str = QString("MousePree[%3]:(%1, %2)").arg(ev->x()).arg(ev->y()).arg(btn);
setText(str);
}
void MyLabel::mouseReleaseEvent(QMouseEvent* ev)
{
QString btn;
if (ev->button() == Qt::LeftButton)
{
btn = "LeftButton";
}
else if (ev->button() == Qt::RightButton)
{
btn = "RightButton";
}
else if (ev->button() == Qt::MiddleButton)
{
btn = "MiddleButton";
}
QString str = QString("MouseRelease[%3]:(%1, %2)").arg(ev->x()).arg(ev->y()).arg(btn);
setText(str);
}
void MyLabel::mouseMoveEvent(QMouseEvent* ev)
{
QString btn;
if (ev->buttons() & Qt::LeftButton)
{
btn = "LeftButton";
}
else if (ev->buttons() & Qt::RightButton)
{
btn = "RightButton";
}
else if (ev->buttons() & Qt::MiddleButton)
{
btn = "MiddleButton";
}
QString str = QString("MouseMove[%3]:(%1, %2)").arg(ev->x()).arg(ev->y()).arg(btn);
setText(str);
}
2.设置定时器
两种定时器#include<QTimerEvent>和#include <QTimer>
private:
int id;
int id1;
// 启动定时器
// 参数 1: 触发定时器的时间, 单位: ms
// 参数2: 使用默认值
// 返回值: 定时器ID
id = startTimer(100);
id1 = startTimer(3000);
//判断定时器后进行不同定时器操作,每触发一次定时器, 进入该函数中
void MyLabel::timerEvent(QTimerEvent* e)
{
QString str;
if (e->timerId() == id)
{
static int num = -100;
str = QString("%1: %2").arg("Time out: ").arg(num++);
if (num > 100)
{
// 关闭定时器
killTimer(id);
}
}
else if (e->timerId() == id1)
{
static int num1 = 10000;
str = QString("%1: %2").arg("Time out: ").arg(num1++);
if (num1 >= 10000 + 1000)
{
// 关闭定时器
killTimer(id1);
}
}
setText(str);
}
// 第二种定时器用法#include <QTimer> 利用信号进行操作,也比较简单
QTimer * timer = new QTimer(this);
timer->start(100);
connect(timer, &QTimer::timeout, this, [=]()
{
static int number = 0;
this->setText(QString::number(number++));
if (number > 50)
{
// 关闭定时器
timer->stop();
number = 0;
timer->start();
}
});
四、绘图
1.回调函数与虚函数
回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
模块A有一个函数foo,它向模块B传递foo的地址,然后在B里面发生某种事件时,通过A里面传过来的foo地址调用foo,通知A发生了什么事情,让A作出相应反应,那么foo就称为回调函数。下面是回调函数的简单实例:
#include <iostream>
using namespace std;
typedef int(*CallBack)(char *p);//声明CallBack 类型的函数指针
int A(char *p)
{
cout<<"A"<<endl;
cout<<p<<endl;
return 0;
}
int B(CallBack lpCall,char *p)
{
cout<<"B"<<endl;
cout<<p<<endl;
lpCall(p); //借助回调完成的功能,也就是A函数来处理的.
return 0;
}
void main()
{
char *p="hello!";
B(A,p);
}
一个虚函数调用 * 实际上是一个回调。
调用者在对象的虚函数表中查找相应的条目并调用它。这与回调函数的行为完全相同,只是成员函数指针的语法比较笨拙。虚函数将工作卸载到编译器,这使它们成为一个非常优雅的解决方案。
2.窗口刷新事件
窗口刷新:QWidget::update(), QWidget::repaint(),但一般使用update()可以最小化闪烁。
对于回调函数如果用户强制刷新会在刷新后被调用,可以用此性质使用虚函数刷新。
void paintEvent(QPaintEvent*);
connect(ui->ButtonMove, &QPushButton::clicked, this, [=]()
{
// 刷新窗口
update(); // 系统调用paintEvent 函数
});
void Widget::paintEvent(QPaintEvent*)
{
//创建画家类对象
QPainter p(this); //指定绘图设备
x += 5;
if(x>this->width())
{
x = 20;
}
p.drawPixmap(x, 100, QPixmap(":/Image/sunny.png"));
}
3. 绘图设备
大概采用以下几种绘图方式
// QWidget
// QPixmap QImage QPicture QBitmap(黑白图片)
// QBitmap 父类 QPixmap
// QPixmap -- 图片类, 主要用来显示, 它针对于显示器显示做了特殊优化, 依赖于平台的, 只能在主线程中使用(UI线程)
// QIamge -- 图片类 , 不依赖有平台, (图片传输 , 可以在多线程中对其进行操作)
指定绘图设备
// 指定绘图设备 1. 构造函数中(参数是绘图设备)// 2. begin(参数是绘图设备)// end();
但是采用begin的方式发现setpen函数不起作用
// 在QPixmap中画图
QPixmap pix(300, 300); // 纸的大小
pix.fill(Qt::red);
QPainter p(&pix);
p.setPen(QPen(Qt::green, 10));
p.drawRect(10, 10, 280, 280);
p.drawEllipse(150, 150, 50, 50);
pix.save("D:\\mypixmap.png");
// 指定绘图设备 1. 构造函数中(参数是绘图设备)
// 2. begin(参数是绘图设备)
// end();
// 在QImage中画图
QImage img(300, 300, QImage::Format_RGB32); // 纸的大小
img.fill(Qt::red);
p.begin(&img);
p.setPen(QPen(Qt::green, 10));
p.drawRect(10, 10, 280, 280);
p.drawEllipse(150, 150, 50, 50);
p.end();
img.save("D:\\myImage.png");
// 在QPicture中画图
// 1. 保存的是绘图步骤 -- 画家类
// 2. 不是图片, 二进制文件(save保存生成的文件)
// 3. 不依赖平台
QPicture pic; // 纸的大小
p.begin(&pic);
p.setPen(QPen(Qt::green, 10));
p.drawRect(10, 10, 280, 280);
p.drawEllipse(150, 150, 50, 50);
p.end();
pic.save("D:\\mypic.aaa");
上述代码只有。setPen QPixmap才起作用,QImage不起作用。
// 在QPixmap中画图
QPixmap pix(300, 300); // 纸的大小
pix.fill(Qt::red);
QPainter p(&pix);
p.setPen(QPen(Qt::green, 10));
p.drawRect(10, 10, 280, 280);
p.drawEllipse(150, 150, 50, 50);
pix.save("D:\\mypixmap.png");
// 指定绘图设备 1. 构造函数中(参数是绘图设备)
// 2. begin(参数是绘图设备)
// end();
// 在QImage中画图
QImage img(300, 300, QImage::Format_RGB32); // 纸的大小
img.fill(Qt::red);
QPainter painter(&img);
painter.setPen(QPen(Qt::green, 10));
painter.drawRect(10, 10, 280, 280);
painter.drawEllipse(150, 150, 50, 50);
img.save("D:\\myImage.png");
// 在QPicture中画图
// 1. 保存的是绘图步骤 -- 画家类
// 2. 不是图片, 二进制文件(save保存生成的文件)
// 3. 不依赖平台
QPicture pic; // 纸的大小
p.begin(&pic);
p.setPen(QPen(Qt::green, 10));
p.drawRect(10, 10, 280, 280);
p.drawEllipse(150, 150, 50, 50);
p.end();
pic.save("D:\\mypic.aaa");
上述代码QPixmap和QImage的setPen都起作用作用