1 使用键盘按键
.h
class MainWindow : public QMainWindow
{
protected:
virtual void keyPressEvent(QKeyEvent *ev);
};
.cpp
void MainWindow::keyPressEvent(QKeyEvent *ev)
{
switch (ev->key())
{
case Qt::Key_F1:
std::cout << "111" << std::endl;
break;
}
QWidget::keyPressEvent(ev);
}
但有的时候按键不好使用,比如有的按键不能响应,则需要在构造函数中添加这句:
this->grabKeyboard();
在实际情况中我们可能会长按一个键,长按过程中它会自动连续触发“按下”、“松开”的动作,如果不想被多次触发,可以加判断:bool QKeyEvent::isAutoRepeat()
:
void MainWindow::keyPressEvent(QKeyEvent *ev)
{
if (ev->isAutoRepeat()) {
std::cout << "Repeat" << std::endl;
} else {
std::cout << "Not Repeat" << std::endl;
switch (ev->key())
{
case Qt::Key_A:
std::cout << "111" << std::endl;
break;
case Qt::Key_D:
std::cout << "222" << std::endl;
break;
}
}
QWidget::keyPressEvent(ev);
}
2 过滤事件
2.1 在label上画矩形
下面的例子是把事件过滤给label控件,在label上画矩形:
.h
class MainWindow : public QMainWindow
{
protected:
bool eventFilter(QObject *watched, QEvent *event);
private:
int x1=0, y1=0, x2=0, y2=0;
};
.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->label_2->installEventFilter(this); // 给控件安装事件过滤器
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == ui->label_2) // 判断观测对象是哪个控件
{
if (event->type() == QEvent::Paint){ // 判断事件类型
QPainter painter(ui->label_2);
painter.setPen(QPen(Qt::red, 2));
painter.drawRect(QRect(x1, y1, x2 - x1, y2 - y1));
}
else if(event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
// if (mouseEvent->button() == Qt::LeftButton) 判断是否是鼠标左键
// if (mouseEvent->buttons() & Qt::LeftButton) 判断是否包含鼠标左键
x1 = mouseEvent->pos().x();
y1 = mouseEvent->pos().y();
QCursor cursor;
cursor.setShape(Qt::ClosedHandCursor);
QApplication::setOverrideCursor(cursor);
}
else if(event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
x2 = mouseEvent->pos().x(); //鼠标相对于所在控件的位置
y2 = mouseEvent->pos().y();
update(); // 触发绘制事件
QApplication::restoreOverrideCursor();
}
else if(event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
x2 = mouseEvent->pos().x(); //鼠标相对于所在控件的位置
y2 = mouseEvent->pos().y();
update(); // 触发绘制事件
}
}
return QWidget::eventFilter(watched, event);
}
2.2 在label上画封闭图形
下面的例子是把事件过滤给label控件,在label上画封闭图形:
.h
class MainWindow : public QMainWindow
{
private:
QList<QPoint> point;
protected:
bool eventFilter(QObject *watched, QEvent *event);
}
.cpp
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == ui->label)
{
if (event->type() == QEvent::Paint) {
QPainter painter(ui->label);
painter.setRenderHint(QPainter::Antialiasing);
//painter.translate(20, 20); // 偏移距离
//painter.setBrush(Qt::red); // 填充色(纯色)
//painter.setBrush(QColor(255, 0, 0, 100)); // 填充色(半透明)
int p_num = point.size();
QPoint points[p_num];
for (int i = 0; i < p_num; i++) {
points[i] = point[i];
}
painter.drawPolygon(points, p_num);
}
else if (event->type() == QEvent::MouseButtonPress) {
point.clear();
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
point.append(mouseEvent->pos());
}
else if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
point.append(mouseEvent->pos());
update();
QApplication::restoreOverrideCursor();
}
else if (event->type() == QEvent::MouseMove) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
point.append(mouseEvent->pos());
update();
}
}
}
3 模拟按键点击事件
QPoint pos(x1,y1);
QMouseEvent event0(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(ui->label_2, &event0);
QPoint pos2(x2,y2);
QMouseEvent event1(QEvent::MouseButtonRelease, pos2, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(ui->label_2, &event1);
4 手动选择路径
QString DataPath;
// 选择文件夹
DataPath = QFileDialog::getExistingDirectory(this, tr("路径1:"), "/home/tianru/repeat-picture/");
// 选择文件
DataPath = QFileDialog::getOpenFileName(this, tr("选择视频:"), "/home/tianru/", tr("*.mp4"));
std::string path = DataPath.toStdString();
5 取出label里的图形
QPixmap pix;
pix = ui->label->grab(ui->label->rect());
QPixmap格式转换为Mat格式参考:opencv(c++)笔记.md
6 把Mat格式的图片显示在label上
cv::Mat img = cv::imread(path, 1); // 获取Mat格式图片
cv::cvtColor(img, img, CV_BGR2RGB);
QImage image((const uchar*)img.data,img.cols,img.rows,img.cols*img.channels(), QImage::Format_RGB888);
ui->label->setPixmap(QPixmap::fromImage(image));
ui->label->setScaledContents(true);
7 Qstring 和 string 互换
类型 | 方法 |
---|---|
int -> Qstring | QString a = QString::number(123); |
QString -> int | int i = atoi(qstr.toStdString().c_str()); |
Qstring -> string | string s = qstr.toStdString(); |
string -> QString | QString a = QString::fromStdString(s); |
8 qmake 使用方法
- 区分平台
qmake本身提供了在pro下判断平台的变量参数,如下:
macx {
message('this is macx.') # 打印信息
# 添加macx平台的内容
}
unix:!macx {
message('this is linux.') # 打印信息
# 添加linux平台的内容
}
win32 {
message('this is windows.') # 打印信息
# 添加windows平台的内容
}
执行qmake,编译输出里面会根据情况打印出上面的某条信息;
- 添加宏定义
DEFINES += TEST
# 判断宏定义是否存在
contains(DEFINES, TEST) {
message('存在')
} else {
message('不存在')
}
- 区分 debug 和 release 版本的库
CONFIG(debug, debug|release): {
# 添加内容
} else:CONFIG(release, debug|release): {
# 添加内容
}
- 路径中有空格加上双引号
LIBS += "-LD:\Program Files\opencv\opencv3.4.0\build\x64\vc14\lib"
9 读取硬件信息(windows)
在windows中读取cpu_id、disk_serial_number调用了wmic命令,读取mac地址用了qt中的QtNetwork相关函数,在使用QtNetwork头文件时一定要在pro文件中添加语句:QT += core gui network
,别忘记了!
代码如下:
machine_info.h
#include <QProcess>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QHostInfo>
#include <QSysInfo>
#include <QDesktopWidget>
#include <QFileInfoList>
#include <QDir>
#include <QLibrary>
#include <QTime>
#include <QtCore>
#include <iostream>
QString get_WMIC(const QString &cmd);
bool get_cpu_id(std::string & cpu_id);
bool get_disk_serial_number(std::string & serial_no);
bool get_mac_address(std::string & mac_address);
machine_info.cpp
#include "get_machine_info_win.h"
QString get_WMIC(const QString &cmd)
{
QProcess p;
p.start(cmd);
p.waitForFinished();
QString result = QString::fromLocal8Bit(p.readAllStandardOutput());
QStringList list = cmd.split(" ");
result = result.remove(list.last(), Qt::CaseInsensitive);
result = result.replace("\r", "");
result = result.replace("\n", "");
result = result.simplified();
return result;
}
bool get_cpu_id(std::string &cpu_id)
{
cpu_id = get_WMIC("wmic cpu get processorid").toStdString();
return true;
}
bool get_disk_serial_number(std::string &serial_no)
{
serial_no = get_WMIC("wmic diskdrive where index=0 get serialnumber").toStdString();
return true;
}
bool get_mac_address(std::string &mac_address)
{
QString strMac;
QList<QNetworkInterface> netList = QNetworkInterface::allInterfaces();
foreach(QNetworkInterface item, netList)
{
if((QNetworkInterface::IsUp & item.flags()) && (QNetworkInterface::IsRunning & item.flags()))
{
if(strMac.isEmpty() || strMac < item.hardwareAddress())
{
strMac = item.hardwareAddress();
}
}
}
mac_address = strMac.toStdString();
return true;
}
10 读取/设置控件显示信息
10.1 QTextEdit
// 读取数据
QString data = ui->textEdit->toPlainText();
// 写入数据
QString data = "123";
ui->textEdit->setPlainText(data);
10.2 QComboBox
// 读取信息
QString data = ui->comboBox->currentText()
// 写入数据
11 定时器 QTimer
使用方法见代码:
.h
class MainWindow
{
public:
MainWindow();
QTimer * time_display;
public slots:
void do_display();
}
.cpp
MainWindow::MainWindow() :
{
time_display = new QTimer(this);
connect(time_display, SIGNAL(timeout()), this, SLOT(do_display()));
time_display->start(500);//开启定时器,周期500ms
//time_display->stop();//关闭定时器
}
void MainWindow::do_display()
{
std::cout << "1 " << std::endl;
}
12 string和Qstring转换中文乱码
为了避免中文乱码问题,可以采用下面的方法转换:
-
Qstring -> string
std::string str; QString qstr; qstr = "你好" str = qstr.toLocal8Bit().data();
-
string -> Qstring
std::string str; QString qstr; str = "你好" qstr = QString::fromLocal8Bit(str.c_str());
13 QgridLayout布局控件
- 默认布局时会改变控件的尺寸和位置,如果不想改变,需要设置QgridLayout控件的相关属性,如:setSizeConstraint;
- 填充控件
QGridLayout * gridlayout = new QGradient(); gridlayout->addWidget(new QCheckBox("hello"), 0, 1, 2, 3);//0表示纵向的索引,1表示横向的索引,2表示纵向占据2格,3表示横向占据3格 QFrame * line = new QFrame();//创建线条 line->setFrameShape(QFrame::HLine);//设置线条方向 ui->gridLayout_attr->addWidget(line, 5, 0, 1, -1);//-1表示占据该行(或该列)所有格
- 遍历布局控件的各个子控件
QLayoutItem * item; for (int cc = ui->gridLayout->count()-1; cc >= 0; --cc) { QLayoutItem *it = ui->gridLayout->itemAt(cc); VideoList *orderHistory = qobject_cast<VideoList *>(it->widget()); // 这里的 VideoList 想要转换成的类型 if (orderHistory != 0) { //doing something for orderHistory qDebug() << orderHistory->m_checkbox->text(); // 对该类型的控件做操作 } }
- 遍历并清除指定控件
QLayoutItem * item; // 方法一 while ((item = ui->gridLayout_attr->itemAt(0)) != 0) { std::string name = item->widget()->metaObject()->className(); if (name == "QFrame") { // 按照什么条件筛选根据自己需求改 item->widget()->deleteLater(); item->widget()->setParent(NULL); } // 如果知道这个控件的类型,比如CCheckCombox,那么可以把item转换为该类型后再做操作 CCheckCombox * b = qobject_cast<CCheckCombox *>(item->widget()); if (b != 0) std::cout << b->count() << std::endl; } // 方法二 for (int i = 0; i < gridLayout->count(); ++i) { item = gridLayout->itemAt(i); std::string name = item->widget()->metaObject()->className(); if (name == "QFrame") { // 按照什么条件筛选根据自己需求改 item->widget()->deleteLater(); item->widget()->setParent(NULL); i--; } }
14 获取信号与槽中的信号发起者
在信号与槽的运用中,会出现多个信号指向一个槽函数,但是槽函数如何分辨是哪个控件发过来的信号呢,可以使用sender()函数获取当前信号的发起者,如:
QPushButton * button = new QPushButton("X");
button->setObjectName("aaaa");//设置对象名
QPushButton * button2 = new QPushButton("X");
button2->setObjectName("bbbb");//设置对象名
connect(button, SIGNAL(clicked()), this, SLOT(close_one_property()));
connect(button2, SIGNAL(clicked()), this, SLOT(close_one_property()));
槽函数:
// 需要先在头文件里声明槽函数
// ipublic slots:
// void slot_function();
void MainWindow::slot_function()
{
QObject * o = sender();
std::string n = o->objectName().toLocal8Bit().data();//获取对象名
std::cout << n << std::endl;
}
15 QCheckBox改变勾选框的图标
ui->checkBox_13->setStyleSheet("QCheckBox::indicator{width:15px; height:15px;}\
QCheckBox::indicator:unchecked{image:url(:/icon/uncheck.png);}\
QCheckBox::indicator:checked{image:url(:/icon/check2.png);}");
16 QPushButton改变图标
QIcon ico(":/icon/close.png");
ui->pushButton_7->setIcon(ico); // 设置图案
ui->pushButton_7->setIconSize(QSize(15,15));//设置尺寸
ui->pushButton_7->setFlat(true);//去掉边缘
17 设置窗口标题
MainWindow w;
w.setWindowTitle(QString::fromStdString("xxx"));
w.show();
18 Qt 获取目录
//fun2();
QString p = QCoreApplication::applicationDirPath();
std::cout << "当前程序所在目录:" << p.toStdString() << std::endl;
p = QDir::currentPath();
std::cout << "当前工作目录:" << p.toStdString() << std::endl;
p = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
std::cout << "用户目录:" << p.toStdString() << std::endl;
p = QDir::homePath();
std::cout << "用户目录:" << p.toStdString() << std::endl;
p = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
std::cout << "我的文档路径:" << p.toStdString() << std::endl;
p = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
std::cout << "桌面路径:" << p.toStdString() << std::endl;
p = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
std::cout << "程序数据存放路径:" << p.toStdString() << std::endl;
p = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
std::cout << "临时文件路径:" << p.toStdString() << std::endl;
19 信号与槽
- 发送信号
// 定义信号函数
signals:
void start_changed(); // 可以添加参数
// 发送信号
emit start_changed();
- 连接信号与槽
// 定义
QPushButton * m_button1;
private slots:
void button_1_clicked();
// 实现
m_button1 = new QPushButton(this);
connect(m_button1, SIGNAL(pressed()), this, SLOT(button_1_clicked())); // 连接信号与槽
// 编写槽函数
void SlidingBar::button_1_clicked()
{
// .............
}
20 给界面添加布局
在新创建的界面中,默认是没有布局的,布局一般用于窗体自适应大小,也就是说控件能根据窗体大小来改变自己的大小和位置。这里以添加垂直布局为例:
- 向ui界面中任意拖动一个控件
- 鼠标点击界面背景,再点击
工具 >> Form_Editor >> 打破布局
- 点击
工具 >> Form_Editor >> 垂直布局
21 获取窗体改变事件、鼠标事件
#include <QMouseEvent>
// 声明
protected:
virtual void resizeEvent(QResizeEvent *event) override;
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
// 实现
void SlidingBar::resizeEvent(QResizeEvent *event)
{
//qDebug() << this->geometry().width();
}
void SlidingBar::mousePressEvent(QMouseEvent *event)
{
}
void SlidingBar::mouseReleaseEvent(QMouseEvent *event)
{
}
void SlidingBar::mouseMoveEvent(QMouseEvent *event)
{
int pos = event->x();
//qDebug() << pos;
}
22 把 Mat 图像等比例显示在 QLabel 上
保持 Mat 图像比例不变,根据 Qlabel 尺寸计算出合适的值,让 Mat 图像最大化显示。
// ui->frame 是背景框
// frame 是Mat图像
// m_q_label 是改变尺寸后的fame
// m_q_label 需要做初始化,给他绑定一个父对象,和 ui->frame 一致,如:
// m_qt_frame = new QLabel();
// m_qt_frame->setParent(ui->centralWidget);
// m_qt_frame->setStyleSheet("background-color: rgb(0, 0, 0);");
// m_qt_frame->setGeometry(11, 11, 1, 1);
void MainWindow::resizeFrame(cv::Mat & frame)
{
int w = frame.cols;
int h = frame.rows;
// 改变图片的尺寸
int u_w = ui->frame->width();
int u_h = ui->frame->height();
int i_w = w;
int i_h = h;
if (i_w > u_w || i_h > u_h) {
if (u_w * i_h - u_h * i_w > 0) {
int i_h_temp = i_h;
i_h = u_h;
i_w = u_h * i_w / i_h_temp;
} else {
int i_w_temp = i_w;
i_w = u_w;
i_h = u_w * i_h / i_w_temp;
}
}
w = i_w;
h = i_h;
int x = ui->frame->x();
int y = ui->frame->y();
x += (u_w - w)/2;
y += (u_h - h)/2;
m_qt_frame->setGeometry(x, y, w, h);
cv::Mat img;
cv::resize(frame, img, cv::Size(w, h));
cv::cvtColor(img, img, CV_BGR2RGB);
QImage image((const uchar*)img.data, img.cols,
img.rows, img.cols*img.channels(),
QImage::Format_RGB888);
m_qt_frame->setPixmap(QPixmap::fromImage(image));
}
23 向 QTabWidget 添加 tab 和 button 等组件
// 假如界面已经创建了 QTabWidget : ui->tabWidget
QWidget * new_widget = new QWidget();
QGridLayout * new_layout = new QGridLayout();
QPushButton * button1 = new QPushButton(tr("1"));
QPushButton * button2 = new QPushButton(tr("2"));
QSpacerItem * spacer = new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); // 弹簧,第3个参数表示水平方向,第4个参数表示垂直方向
new_layout->addWidget(button1, 0, 0); // 布局中插入按键1
new_layout->addWidget(button2, 0, 1); // 布局中插入按键2
new_layout->addItem(spacer, 1, 0); // 布局中插入按键1
new_widget->setLayout(new_layout);
ui->tabWidget->insertTab(0, new_widget, "a");
//ui->tabWidget->removeTab(0);