private:
// 成员变量
QPushButton * btn1;
btn1 = new QPushButton("关闭",this);
btn1->move(100,100);
……
#define QPushButton_STYTLE (QString("\
/*按钮普通态*/\
QPushButton\
{\
font-family:Microsoft Yahei;\
/*字体大小为20点*/\
font-size:20pt;\
/*字体颜色为白色*/\
color:white;\
/*背景颜色*/\
background-color:rgb(14 , 150 , 254);\
/*边框圆角半径为8像素*/\
border-radius:8px;\
}\
/*按钮悬停态*/\
QPushButton:hover\
{\
/*背景颜色*/\
background-color:rgb(100 , 137 , 255);\
}\
/*按钮按下态*/\
QPushButton:pressed\
{\
/*背景颜色*/\
background-color:rgb(14 , 135 , 10);\
/*左内边距为3像素,让按下时字向右移动3像素*/\
padding-left:3px;\
/*上内边距为3像素,让按下时字向下移动3像素*/\
padding-top:3px;\
}"))
emit mySignal(); // 发射自定义信号函数,信号函数“没有调用也没有函数体”
在Designer中,可以点击键盘的F4进入信号槽编辑模式,在此模式下发射者和接收者可以通过拖拽的方式连接,发射者在箭头的箭尾,接收者在箭头的指向,随后弹出信号与槽函数选择的窗口。
// int → QString QString text = QString::number(count); |
点击F3可以退出此模式,要查看这种模式下连接的信号槽,除了重新点击F4进入模式外,可以直接点击信号和槽编辑器。同时,信号和槽编辑器的表格也可以编辑四个参数。
布局 Layout
水平布局的类名是QHBoxLayout,在Designer中名称是Horizontal Layout。所有在水平布局内部的组件线性水平排布。
private:
QVBoxLayout* layout;
QPushButton* btn1;
QPushButton* btn2;
layout = new QVBoxLayout(this);
btn1 = new QPushButton("按钮1",this);
btn2 = new QPushButton("按钮2",this);
layout->addWidget(btn1);
layout->addWidget(btn2);
qwidget常用属性
ui指针工作流程
建议使用必应搜索下载图片。
#include // 颜色类
#include // 调色板类
#include // 图片类
#include // 大小尺寸类
// 创建一个颜色对象
QColor color(255,0,128);
// 创建一个调色板对象
QPalette pa;
// 设置颜色
// 参数1:颜色角色
// 参数2:颜色对象
pa.setColor(QPalette::WindowText,color);
// 获得文字显示的QLabel对象
ui->label_text->setPalette(pa);
// 创建一个图片对象,注意文件路径从资源文件中复制出来
QPixmap map(":/new/prefix1/gqj.jpg");
// 大小对象
QSize size(300,280);
// 缩放
// 参数1:大小
// 参数2:缩放规则
// 返回值:处理后的图片对象
map = map.scaled(size,Qt::KeepAspectRatioByExpanding);
// 给组件设置图片显示效果
ui->label_pic->setPixmap(map);
所有的按钮类都继承自QAbstractButton,QAbstractButton作为抽象基类,规定了按钮的各种基本功能。
软件图标设计学习交流可以参考网站:阿里巴巴矢量图标库https://www.iconfont.cn/plus
复选按钮
private:
Ui::Dialog *ui;
QButtonGroup *group;
group = new QButtonGroup(this);
// 添加按钮对象到按钮组,并设置一个不重复的正数编号
// 参数1:按钮对象
// 参数2:编号
group->addButton(ui->checkBox_c,1);
group->addButton(ui->checkBox_cpp,2);
group->addButton(ui->checkBox_cs,3);
connect(group,SIGNAL(buttonToggled(int,bool)),this,SLOT(btnToggledSlot(int,bool)));
void Dialog::btnToggledSlot(int id,bool checked)
{
if(id == 1)
{
qDebug() << "C" << checked;
}else if(id == 2)
{
qDebug() << "C++" << checked;
}
…
}
与QRadioButton比较相似,用于选择一个单选选项,但是组合框比QRadioButton占用空间更小。
在Designer中双击QComboBox可以编辑QComboBox中的选项(Item,Qt Creator中翻译为“项目”),常用属性如下:
// 获取用户输入
QString text = ui->lineEdit->text();
// 如果用户不输入
if(text == "")
return;
// 给组合框增加新选项
ui->comboBox->insertItem(0,text);
// 清空输入
ui->lineEdit->clear();
//赋初值
ui->horizontalscrollBar->setvalue (value) ;
ui->verticalscrollBar->setvalue (value) ;
Qt中使用QDate类表示日期,使用QTime类表示时间,QDate和QTime类合并为QDateTime类,表示日期和时间。本次课程以QDateTime类为主,进行讲解和示例。
常用函数如下:
//dialog.h
private slots:
// 与翻页信号连接的槽函数
void pageSlot(int,int);
// 每次选择日子的槽函数
void selectionSlot();
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
qint64 start = QDateTime::currentMSecsSinceEpoch();
ui->setupUi(this);
for(int i = 0; i<20000; i++)
for(int y = 0; y<20000; y++);
qint64 time = QDateTime::currentMSecsSinceEpoch();
qDebug() << time-start << "3332"; //打个时间戳
// 设置随机数种子
qsrand(time);
// 1-10随机数
int rand = qrand() % 10 + 1;
qDebug() << rand;
// 获得一个基于当前时间和日期的对象
QDateTime dt = QDateTime::currentDateTime();
// 时间和日期格式化
// yyyy/yy表示年,MM表示月,dd表示月,ddd表示星期
// hh表示小时,mm表示分钟,ss表示秒 t表示区域
QString text = dt.toString("yyyy-MM-dd ddd hh:mm:ss t");
qDebug() << text;
connect(ui->calendarWidget,SIGNAL(currentPageChanged(int,int)),
this,SLOT(pageSlot(int,int)));
connect(ui->calendarWidget,SIGNAL(selectionChanged()),
this,SLOT(selectionSlot()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::pageSlot(int year, int month)
{
qDebug() << "翻页了" << year << "年" << month << "月";
}
void Dialog::selectionSlot()
{
// 获取当前的日期
QDate date = ui->calendarWidget->selectedDate();
// 格式化
qDebug() << "选择了" << date.toString("dd") << "号";
}
常用函数如下:
void QTimer::start() [slot]
启动定时器,如果调用此函数时定时器已经启动,则会停止当前的运行,并重新启动。
void QTimer::stop() [slot]
停止定时器
void QTimer::timeout() [signal]
触发时发射的信号函数
单次的
//dialog.h
private:
Ui::Dialog *ui;
QTimer* timer;
private slots:
// 点击按钮的槽函数
void btnClickedSlot();
// 与timeout信号连接的槽函数
void timeoutSlot();
//dialog.cpp //单次的
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 创建定时器对象
timer = new QTimer(this);
// 设置为一次性
timer->setSingleShot(true);
// 设置触发时间为5s
timer->setInterval(5000);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
connect(timer,SIGNAL(timeout()),
this,SLOT(timeoutSlot()));
}
Dialog::~Dialog()
{
delete timer;
delete ui;
}
void Dialog::btnClickedSlot()
{
// 启动定时器
timer->start();
}
void Dialog::timeoutSlot()
{
// 到点了输出一句话
qDebug() << "Hello";
}
周期的
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 槽函数也是成员函数,可以手动调用
timeoutSlot();
timer = new QTimer(this);
timer->setSingleShot(false); // 周期性
timer->setInterval(100); // 间隔时间
// 连接信号槽
connect(timer,SIGNAL(timeout()),this,SLOT(timeoutSlot()));
timer->start(); // 启动
}
void Dialog::timeoutSlot()
{
// 获取当前时间并显示
QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
ui->lcdNumber->display(time);
}
Dialog::~Dialog()
{
// 先停止定时器
if(timer->isActive())
timer->stop();
delete timer;
delete ui;
}
创建自定义C++类
创建的步骤如下所示:
1. 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件”。
2. 在弹出的窗口中,按照下图所示进行操作。
4. 在项目管理界面直接点击“完成”,可以看到Qt项目中存在自定义类的头文件和源文件。
.cpp //.h记得加头文件就OK
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 创建一个QList对象
QList class22071;
// 准备一些学生对象
Student s1("张三",18,"物联网");
Student s2("李四",19,"电子信息");
Student s3("王五",20,"计算机");
Student s4("赵六",21,"软件工程");
Student s5("钱七",22,"网络工程");
// 添加元素
class22071.append(s1); // 向后追加
class22071.push_back(s2); // 向后追加
class22071.prepend(s3); // 向前追加
class22071.push_front(s4); // 向前追加
class22071 << s1 << s2 << s3; // 连续追加
// 取出单个元素
qDebug() << class22071[0].getName()
<< class22071.at(1).getName() << 1001;
// 插入元素
// 参数1:插入的位置
// 参数2:插入的元素
class22071.insert(1,s5);
// STL 迭代器
for(QList::const_iterator iter = class22071.begin();
iter != class22071.end();iter++)
{
Student s = *iter;
qDebug() << s.getName() << s.getAge() << s.getMajor();
}
qDebug() << "------------------------";
// Java迭代器
// 只读:QListIterator
// 读写:QMutableListIterator
QListIterator iter(class22071);
while(iter.hasNext()) // 判断迭代器指针后面还有没有元素
{
Student s = iter.next(); // 向后移动迭代器指针,并取出元素对象
qDebug() << s.getName() << s.getAge() << s.getMajor();
}
qDebug() << "------------------------";
QList list;
list << 34 << 56 << 123 << 34 << 56;
list.removeAll(34); // 移除所有34
list.removeFirst(); // 移除第一个元素
list.removeLast(); // 移除最后一个元素
list << 111 << 111 << 111;
list.removeOne(111); // 移除第一个111
list.removeAt(1); // 移除第二个元素
// 修改元素
// 参数1:修改的元素位置
// 参数2:新元素值
list.replace(0,666);
qDebug() << list;
}
Dialog::~Dialog()
{
delete ui;
}
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 创建一个QMap对象
QMap map;
// 添加数据
map["姓名"] = "张三";
// 参数1:键
// 参数2:值
map.insert("年龄","100岁");
// 修改数据
map["姓名"] = "张三丰";
// 取出之前判断元素有没有
if(map.contains("姓名"))
{
qDebug() << map["姓名"] << 1000; // 取出
}
// 这种方式可以不判断元素在不在,因为如果元素不存在,会返回预设的默认值
// 参数1:键
// 参数2:默认值
qDebug() << map.value("地址","默认值")<<1001;
qDebug() << map.value("年龄","默认值")<< 1002;
qDebug() << map << 1003;
int result = map.remove("姓名");
qDebug() << "删除结果:" << result;
qDebug() << map;
map["身高"] = "1米";
map["体重"] = "100斤";
// STL遍历
for(QMap::const_iterator iter = map.begin();
iter != map.end();iter++)
{
// 取出键和值
qDebug() << iter.key() << iter.value();
}
qDebug() << "--------------";
// Java遍历
// 只读迭代器 QMapIterator
// 读写迭代器 QMutableIterator
QMapIterator iter(map);
while(iter.hasNext())
{
iter.next(); // 向后移动
// 取出键和值
qDebug() << iter.key() << iter.value();
}
}
Dialog::~Dialog()
{
delete ui;
}
QMessageBox是一种模态对话框,即弹出时必须优先处理。
QMessageBox是QDialog的派生类,这些Qt内置的QDialog的派生类设计的目的是减少程序员的编程量,可以直接通过静态成员函数调用使用。这些静态成员函数的返回值往往是这个对话框的结果,例如QMessageBox的返回值是用户点击的按钮、QFontDialog的返回值是用户选择的字体,QColorDialog的返回值是用户选择的颜色......
QMessageBox用于信息提示,预设有四种样式。
参数1:所依赖的窗口对象
参数2:窗口标题
参数3:消息内容
返回值:用户点击的按钮类型
private:
Ui::Dialog *ui;
QButtonGroup* group;
// 创建并展示一个自定义QMessageBox对象
void customMessageDialog();
QMessageBox* box = NULL;
QPushButton* btn1;
QPushButton* btn2;
QPushButton* btn3;
private slots:
// 与void buttonClicked(int id)连接
void btnsClickedSlot(int);
…...
group = new QButtonGroup(this);
group->addButton(ui->pushButton_ques,1);
group->addButton(ui->pushButton_info,2);
group->addButton(ui->pushButton_warn,3);
group->addButton(ui->pushButton_crit,4);
group->addButton(ui->pushButton_cust,5);
connect(group,SIGNAL(buttonClicked(int)),this,SLOT(btnsClickedSlot(int)));
void Dialog::customMessageDialog()
{
// 创建一个QMessageBox对象
box = new QMessageBox(this);
// 设置标题
box->setWindowTitle("自定义QMessageBox");
// 图片对象
QPixmap map(":/new/prefix1/camera.png");
// 设置图片
box->setIconPixmap(map);
// 设置消息
box->setText("这是一个我自己设计的QMessageBox");
btn1 = new QPushButton("是",box);
btn2 = new QPushButton("不是",box);
btn3 = new QPushButton("滚蛋",box);
// 把按钮放置到QMessageBox的规定位置
box->addButton(btn1,QMessageBox::YesRole);
box->addButton(btn2,QMessageBox::NoRole);
box->addButton(btn3,QMessageBox::DestructiveRole);
// 显示对话框
box->show();
}
void Dialog::btnsClickedSlot(int id)
{
if(id == 1)
{
QMessageBox::StandardButton result = QMessageBox::question(this,"问题","你今早吃了吗?");
int y = this->y();
if(result == QMessageBox::Yes)
{
move(this->x(),y-10);
}else if(result == QMessageBox::No)
{
QWidget类是所有组件和窗口的基类,其对象创建时,如果传入parent参数就是其它窗口的子组件,如果不传入参数就是一个独立的窗口。一般不使用QWidget作为窗口或组件类型。
QWidget也规定了一些窗口类通用的函数:
设置窗口的标题
void setWindowTitle(const QString &)
设置窗口状态
void QWidget::setWindowState(Qt::WindowStates windowState)
设置窗口标记
void setWindowFlags(Qt::WindowFlags type)
窗口类型更多地会选用QDialog和QMainWindow两种类型, QMainWindow通常会作为应用程序的主窗口,而QDialog通常作为应用程序的子窗口。
QMainWindow适合作为中大型应用程序的主窗口的原因是其包含菜单栏、工具栏、中心组件和状态栏。
菜单的功能是折叠应用程序的功能选项,建议使用Designer添加相关选项,每个可以选择或点击的选项都是一个QAction对象。
在Designer的Action编辑器区域,双击对应的QAction可以编辑其细节。
使用下面的信号可以建立点击QAction的信号槽
void QAction::triggered(bool checked = false) [signal]
工具栏中每个按钮QToolButton实际上都是对应的QAction。在Designer中可以很方便地把QAction直接拖拽到工具栏即可。
此区域相当于之前的QDialog,直接通过布局设计效果即可。
用于显示应用程序使用过程中的相关信息。
基本使用可以通过两个槽函数完成:
void QStatusBar::showMessage(const QString & message, int timeout = 0) [slot]
参数1:展示消息的内容
参数2:消息持续时长,单位毫秒,如果使用默认值0,则表示一直显示直到清空
void QStatusBar::clearMessage() [slot]
.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置窗口标题
setWindowTitle("我的窗口");
// 设置全屏显示
// setWindowState(Qt::WindowFullScreen);
// 设置最上层无边框
// setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
// 建立QAction的点击的信号槽
connect(ui->actionNew,SIGNAL(triggered()),
this,SLOT(newTriggeredSlot()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::newTriggeredSlot()
{
QMessageBox::information(this,"通知","您点击了创建动作!");
}
parent除了可以表示某个组件在某窗口中的关系以外,还表示对象销毁的依赖关系,通常把作为parent参数的对象成为父对象,新创建的对象称为子对象,每个父对象内部存储子对象列表,只有父对象销毁时,才销毁所有的子对象。
parent参数是一种有效的防止内存泄漏的方法,缺点就是会导致子对象内存的延迟释放。
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void btnYesClickSlot();
void btnNoClickSlot();
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton_yes,SIGNAL(clicked()),
this,SLOT(btnYesClickSlot()));
connect(ui->pushButton_no,SIGNAL(clicked()),
this,SLOT(btnNoClickSlot()));
}
Dialog::~Dialog()
{
qDebug() << "析构函数";
delete ui;
}
void Dialog::btnYesClickSlot()
{
// 传递parent参数,再创建一个Dialog对象
Dialog* d = new Dialog(this);
d->show();
}
void Dialog::btnNoClickSlot()
{
// 不传递parent参数,再创建一个Dialog对象
Dialog* d = new Dialog;
d->show();
}
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
// 引入MyDialog的头文件
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void btnClickedSlot();
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
}
void Dialog::btnClickedSlot()
{
// 创建MyDialog对象
MyDialog* md = new MyDialog(this);
md->show();
}
Dialog::~Dialog()
{
delete ui;
}
窗口A启动窗口B,滑动窗口A中的滑块,窗口B中的滑块跟着滑动。
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
// 引入MyDialog的头文件
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
MyDialog* md = NULL;
private slots:
void btnClickedSlot();
// 监听滑块进度值的槽函数
void valueChangedSlot(int);
};
#endif // DIALOG_H
Dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
connect(ui->horizontalScrollBar,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));
}
void Dialog::btnClickedSlot()
{
// 获取当前滑块的位置
int value = ui->horizontalScrollBar->value();
// 创建MyDialog对象
md = new MyDialog(value,this);
md->show();
}
void Dialog::valueChangedSlot(int value)
{
if(md != NULL)
// 设置给窗口2
md->setScrollBarValue(value);
}
Dialog::~Dialog()
{
delete ui;
}
Mydialog .h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include
namespace Ui {
class MyDialog;
}
class MyDialog : public QDialog
{
Q_OBJECT
public:
explicit MyDialog(int value,QWidget *parent = 0);
~MyDialog();
// 写一个成员函数,给主窗口调用来设置滑块的位置
void setScrollBarValue(int);
private:
Ui::MyDialog *ui;
};
#endif // MYDIALOG_H
Mydialog .cpp
#include "mydialog.h"
#include "ui_mydialog.h"
MyDialog::MyDialog(int value,QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
// 设置滑块的初始位置
ui->horizontalScrollBar->setValue(value);
}
MyDialog::~MyDialog()
{
delete ui;
}
void MyDialog::setScrollBarValue(int value)
{
// 设置value值给组件
ui->horizontalScrollBar->setValue(value);
}
【例子】窗口A启动窗口B,滑动窗口B中的滑块,窗口A中的滑块跟着滑动。
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
// 引入MyDialog的头文件
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
MyDialog* md = NULL;
private slots:
void btnClickedSlot();
// 监听滑块进度值的槽函数
void valueChangedSlot(int);
// 接收MyDialog类中自定义信号
void valueSignalSlot(int);
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
connect(ui->horizontalScrollBar,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));
}
void Dialog::btnClickedSlot()
{
// 获取当前滑块的位置
int value = ui->horizontalScrollBar->value();
// 创建MyDialog对象
md = new MyDialog(value,this);
// 注意只有在发射者和接收者都创建完成时才能连接信号槽
connect(md,SIGNAL(valueSignal(int)),
this,SLOT(valueSignalSlot(int)));
md->show();
// 屏蔽按钮
ui->pushButton->setEnabled(false);
}
void Dialog::valueChangedSlot(int value)
{
if(md != NULL)
// 设置给窗口2
md->setScrollBarValue(value);
}
void Dialog::valueSignalSlot(int value)
{
// 设置给组件显示
ui->horizontalScrollBar->setValue(value);
}
Dialog::~Dialog()
{
delete ui;
}
mydialog.h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include
namespace Ui {
class MyDialog;
}
class MyDialog : public QDialog
{
Q_OBJECT
public:
explicit MyDialog(int value,QWidget *parent = 0);
~MyDialog();
// 写一个成员函数,给主窗口调用来设置滑块的位置
void setScrollBarValue(int);
private:
Ui::MyDialog *ui;
private slots:
// 滑块值变化的槽函数
void valueChangedSlot(int);
signals:
// 发射给窗口A
void valueSignal(int);
};
#endif // MYDIALOG_H
mydialog.cpp
#include "mydialog.h"
#include "ui_mydialog.h"
MyDialog::MyDialog(int value,QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
// 设置滑块的初始位置
ui->horizontalScrollBar->setValue(value);
connect(ui->horizontalScrollBar,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));
}
MyDialog::~MyDialog()
{
delete ui;
}
void MyDialog::setScrollBarValue(int value)
{
// 设置value值给组件
ui->horizontalScrollBar->setValue(value);
}
void MyDialog::valueChangedSlot(int value)
{
//发射自定义信号到窗口A
emit valueSignal(value);
}
Qt规定的事件有很多种,每一种事件都有对应的事件函数:
void QWidget::paintEvent(QPaintEvent * event) [virtual protected]
绘制
void QWidget::resizeEvent(QResizeEvent * event) [virtual protected]
大小改变
void QWidget::mousePressEvent(QMouseEvent * event) [virtual protected]
鼠标按压
void QWidget::mouseReleaseEvent(QMouseEvent * event) [virtual protected]
鼠标释放
void QWidget::mouseDoubleClickEvent(QMouseEvent * event) [virtual protected]
鼠标双击
void QWidget::mouseMoveEvent(QMouseEvent * event) [virtual protected]
鼠标移动
void QWidget::moveEvent(QMoveEvent * event) [virtual protected]
移动
void QWidget::keyPressEvent(QKeyEvent * event) [virtual protected]
按键按压
void QWidget::keyReleaseEvent(QKeyEvent * event) [virtual protected]
按键释放
void QWidget::focusInEvent(QFocusEvent * event) [virtual protected]
获取焦点
void QWidget::focusOutEvent(QFocusEvent * event) [virtual protected]
失去焦点
void QWidget::closeEvent(QCloseEvent * event) [virtual protected]
关闭
void QWidget::enterEvent(QEvent * event) [virtual protected]
进入
void QWidget::leaveEvent(QEvent * event) [virtual protected]
离开
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
// 画笔类
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
protected:
// 声明事件函数,虚函数具有传递性
void paintEvent(QPaintEvent *);
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
// 定义事件函数
void Dialog::paintEvent(QPaintEvent *)
{
qDebug() << x() << "," << y();
qDebug() << width() << "x" << height();
QPixmap map(":/new/prefix1/banner.jpg");
// 画笔类构造函数 QPainter(QPaintDevice * device)
QPainter painter(this); // 参数是画布
// 绘制图片
painter.drawPixmap(0,0,width(),height(),map);
}
void Dialog::keyReleaseEvent(QKeyEvent *event)
{
// 判断按键是哪一个
int code = event->key();
if(code == Qt::Key_A)
{
int value = ui->progressBar->value();
if(value != 0)
ui->progressBar->setValue(--value);
}else if(code == Qt::Key_D)
{
int value = ui->progressBar->value();
if(value != 100)
ui->progressBar->setValue(++value);
}
}
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
protected:
// 声明事件过滤器函数
bool eventFilter(QObject * watched, QEvent * event);
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 给要检测的子组件注册事件过滤器
// 当前的窗口去监控自组件的事件
ui->lineEdit_1->installEventFilter(this);
}
Dialog::~Dialog()
{
delete ui;
}
/**
* @brief Dialog::eventFilter
* @param watched 当前触发事件函数的对象
* @param event 当前的事件类型
* @return
*/
bool Dialog::eventFilter(QObject *watched, QEvent *event)
{
// 如果是蓝色的输入框,且是获取焦点
if(watched == ui->lineEdit_1 &&
event->type() == QEvent::FocusIn)
{
qDebug() << "蓝色输入框获取焦点!";
}else if(watched == ui->lineEdit_1 &&
event->type() == QEvent::FocusOut)
{
qDebug() << "蓝色输入框失去焦点!";
}
// 继续原来的传递逻辑
//注意是QDialog,不是Dialog
return QDialog::eventFilter(watched,event);
}
弹出打开单文件的对话框
保存单文件的对话框
参数1:依赖父对象
参数2:弹窗标题
参数3:基于哪个文件目录打开对话框,默认值为当前工作目录
参数4:文件格式过滤器
返回值:选择的文件路径|要保存的文件路径
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *group;
// 获取要打开的文件的路径
void getOpenFilePath();
// 获取要保存的文件路径
void getSaveFilePath();
private slots:
// 按钮组点击发射的槽函数
void buttonClickedSlot(int);
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
group = new QButtonGroup(this);
group->addButton(ui->pushButton_open,1);
group->addButton(ui->pushButton_save,2);
group->addButton(ui->pushButton_copy,3);
connect(group,SIGNAL(buttonClicked(int)),
this,SLOT(buttonClickedSlot(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::getOpenFilePath()
{
// 弹窗并选择要打开文件
QString path = QFileDialog::getOpenFileName(this,"打开文件","E:/Temp/",
"所有类型(*.*);;Qt(*.pro *.user *.h *.cpp *.ui)");
// 检测用户是否未选择
if(path == "")
{
QMessageBox::warning(this,"提示","请选择要打开的文件!");
return;
}
// 路径展示到QTextBrowser组件上
ui->textBrowser_open->append(path);
}
void Dialog::getSaveFilePath()
{
// 弹窗并选择要保存文件
QString path = QFileDialog::getSaveFileName(this,"另存为","E:/Temp/",
"所有类型(*.*);;Qt(*.pro *.user *.h *.cpp *.ui)");
// 检测用户是否未选择
if(path == "")
{
QMessageBox::warning(this,"提示","请设置要保存的文件!");
return;
}
// 路径展示到QTextBrowser组件上
ui->textBrowser_save->append(path);
}
void Dialog::buttonClickedSlot(int id)
{
if(id == 1)
{
getOpenFilePath();
}else if(id == 2)
{
getSaveFilePath();
}else if(id == 3)
{
}
}
// 创建文件信息类对象,如果送入一个不存在的文件路径,也可以创建
QFileInfo info(path);
QString text = "文件是否存在:";
if(info.exists())
{
text.append("是");
}else
{
text.append("否");
}
ui->textBrowser_open->append(text);
text = info.created().toString("yyyy-MM-dd hh:mm:ss");
text.prepend("创建日期:");
ui->textBrowser_open->append(text);
if(info.isReadable())
{
ui->textBrowser_open->append("可读");
}else{
ui->textBrowser_open->append("不可读");
}
text = "文件大小:";
qint64 size = info.size();
QString sizeText = QString::number(size);
text.append(sizeText).append("字节");
ui->textBrowser_open->append(text);
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *group;
// 获取要打开的文件的路径
void getOpenFilePath();
// 获取要保存的文件路径
void getSaveFilePath();
// 读取并写出文件,即拷贝
void copy();
QString readPath; // 打开文件的路径
QString writePath; // 写出文件的路径
private slots:
// 按钮组点击发射的槽函数
void buttonClickedSlot(int);
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
group = new QButtonGroup(this);
group->addButton(ui->pushButton_open,1);
group->addButton(ui->pushButton_save,2);
group->addButton(ui->pushButton_copy,3);
connect(group,SIGNAL(buttonClicked(int)),
this,SLOT(buttonClickedSlot(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::getOpenFilePath()
{
// 弹窗并选择要打开文件
QString path = QFileDialog::getOpenFileName(this,"打开文件","E:/Temp/",
"所有类型(*.*);;Qt(*.pro *.user *.h *.cpp *.ui)");
// 检测用户是否未选择
if(path == "")
{
QMessageBox::warning(this,"提示","请选择要打开的文件!");
return;
}
// 路径展示到QTextBrowser组件上
ui->textBrowser_open->append(path);
// 保存路径
readPath = path;
// 创建文件信息类对象,如果送入一个不存在的文件路径,也可以创建
QFileInfo info(path);
QString text = "文件是否存在:";
if(info.exists())
{
text.append("是");
}else
{
text.append("否");
}
ui->textBrowser_open->append(text);
text = info.created().toString("yyyy-MM-dd hh:mm:ss");
text.prepend("创建日期:");
ui->textBrowser_open->append(text);
if(info.isReadable())
{
ui->textBrowser_open->append("可读");
}else{
ui->textBrowser_open->append("不可读");
}
text = "文件大小:";
qint64 size = info.size();
QString sizeText = QString::number(size);
text.append(sizeText).append("字节");
ui->textBrowser_open->append(text);
}
void Dialog::getSaveFilePath()
{
// 弹窗并选择要保存文件
QString path = QFileDialog::getSaveFileName(this,"另存为","E:/Temp/",
"所有类型(*.*);;Qt(*.pro *.user *.h *.cpp *.ui)");
// 检测用户是否未选择
if(path == "")
{
QMessageBox::warning(this,"提示","请设置要保存的文件!");
return;
}
// 路径展示到QTextBrowser组件上
ui->textBrowser_save->append(path);
// 保存路径
writePath = path;
}
void Dialog::copy()
{
// 如果没有已经选择好的读写路径,给与提示
if(readPath=="" || writePath == "")
{
QMessageBox::warning(this,"提示","请选择要读写的文件路径!");
return;
}
// 屏蔽按钮
ui->pushButton_copy->setEnabled(false);
ui->pushButton_copy->setText("正在拷贝");
// QFile对象的创建可以送入一个不存在的路径
QFileInfo info(readPath);
int filesize;
filesize = info.size();
qDebug() << filesize;
QFile readFile(readPath); // 用于读取文件
QFile writeFile(writePath); // 用于写出文件
// 打开IO流
readFile.open(QIODevice::ReadOnly); // 只读模式
writeFile.open(QIODevice::WriteOnly); // 只写模式
// 字节数组用于循环拷贝数据
QByteArray buffer;
// 循环读写
int fNum = 0;
double temp;
while(!readFile.atEnd()) // 如果没有读取到输入流尾
{
fNum = fNum + 512;
temp = (double)fNum/filesize;
// 一次读取最多1kb的数据
buffer = readFile.read(512);
// 写出数据,返回值是实际写出的数据量
writeFile.write(buffer);
ui->progressBar->setValue(temp*100);
}
// 关闭IO
readFile.close();
writeFile.flush();
writeFile.close();
// 通知用户
QMessageBox::information(this,"通知",
writePath.prepend("拷贝完成,新文件位于"));
// 回复按钮
ui->pushButton_copy->setEnabled(true);
ui->pushButton_copy->setText("开始拷贝");
}
void Dialog::buttonClickedSlot(int id)
{
if(id == 1)
{
getOpenFilePath();
}else if(id == 2)
{
getSaveFilePath();
}else if(id == 3)
{
copy();
}
}
Qt的线程类是QThread。
.h
#include "mythread.h"
private slots:
void btnTimeClickedSlot();
void btnUiClickedSlot();
.cpp
connect(ui->pushButton_time,SIGNAL(clicked()),
this,SLOT(btnTimeClickedSlot()));
connect(ui->pushButton_ui,SIGNAL(clicked()),
this,SLOT(btnUiClickedSlot()));
void Dialog::btnTimeClickedSlot()
{
// 创建自定义子线程类对象
MyThread *mt = new MyThread(this);
// 调用start函数启动子线程
mt->start();
}
void Dialog::btnUiClickedSlot()
{
QMessageBox::information(this,"UI操作","哈哈哈哈哈哈!");
}
.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
protected:
// 声明函数
void run();
signals:
public slots:
};
#endif // MYTHREAD_H
.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
// 定义函数
void MyThread::run()
{
qDebug() << "子线程耗时操作开始";
// 给子线程增加一个耗时操作
QThread::msleep(10000);
qDebug() << "子线程耗时操作结束";
}
.h
private slots:
void btnTimeClickedSlot();
void btnUiClickedSlot();
.cpp
connect(ui->pushButton_time,SIGNAL(clicked()),
this,SLOT(btnTimeClickedSlot()));
connect(ui->pushButton_ui,SIGNAL(clicked()),
this,SLOT(btnUiClickedSlot()));
void Dialog::btnTimeClickedSlot()
{
// 创建自定义子线程类对象
MyThread *mt = new MyThread(this);
// 调用start函数启动子线程
mt->start();
}
void Dialog::btnTimeClickedSlot()
{
// 创建自定义子线程类对象
MyThread *mt = new MyThread(this);
// 调用start函数启动子线程
mt->start();
}
void Dialog::btnUiClickedSlot()
{
QMessageBox::information(this,"UI操作","哈哈哈哈哈哈!");
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
protected:
// 声明函数
void run();
signals:
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
// 定义函数
void MyThread::run()
{
qDebug() << "子线程耗时操作开始";
// 给子线程增加一个耗时操作
QThread::msleep(10000);
qDebug() << "子线程耗时操作结束";
}
设定的方式有两种:
优先级的设定仅仅表示在某一时间点,CPU会更加偏重执行某个线程,而不是一定执行某个线程,仅用于辅助功能的使用。
.h
#include "mythread.h"
private slots:
void btnClickedSlot();
.cpp
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
void Dialog::btnClickedSlot()
{
// 获得各自的优先级
int indexA = ui->comboBox_a->currentIndex();
int indexB = ui->comboBox_b->currentIndex();
// 线程对象
MyThread* mtA = new MyThread("A",this);
MyThread* mtB = new MyThread("B",this);
mtA->start();
mtB->start();
if(indexA == 0)
{
// 设定低优先级给线程A
mtA->setPriority(QThread::IdlePriority);
}else{
// 设定高优先级给线程A
mtA->setPriority(QThread::InheritPriority);
}
if(indexB == 0)
{
// 设定低优先级给线程B
mtB->setPriority(QThread::IdlePriority);
}else{
// 设定高优先级给线程B
mtB->setPriority(QThread::InheritPriority);
}
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QString name,QObject *parent = 0);
private:
QString name;
protected:
void run();
signals:
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QString name,QObject *parent) :
QThread(parent),name(name) // 构造初始化列表
{
}
void MyThread::run()
{
for(int i = 0;i<100;i++)
{
qDebug() << name << i;
// msleep(50);
}
}
通常子线程无需手动关闭,因为run函数执行完成后,子线程自动关闭。
如果想让正在运行的线程停止,可以使用void QThread::terminate() [slot]函数来强行终止线程执行,考虑到子线程主要执行耗时操作,最常见的耗时操作是IO操作,IO操作伴随着开启和关闭,因此终止线程可能会导致,IO操作未正常结束。
另外,也可以使用标志位的方式让线程比较温和地停止。
.h
private:
Ui::Dialog *ui;
private slots:
void btnClickedSlot(); // 点击按钮的槽函数
void valueSlotA(int); // 第一个进度条的槽函数
void valueSlotB(int); // 第二个进度条的槽函数
.cpp
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
void Dialog::btnClickedSlot()
{
// 获得各自的优先级
int indexA = ui->comboBox_a->currentIndex();
int indexB = ui->comboBox_b->currentIndex();
// 线程对象
MyThread* mtA = new MyThread("A",this);
MyThread* mtB = new MyThread("B",this);
// 连接主子线程之间通信的信号槽
connect(mtA,SIGNAL(valueSignal(int)),
this,SLOT(valueSlotA(int)));
connect(mtB,SIGNAL(valueSignal(int)),
this,SLOT(valueSlotB(int)));
// 启动线程
mtA->start();
mtB->start();
if(indexA == 0)
{
// 设定低优先级给线程A
mtA->setPriority(QThread::IdlePriority);
}else{
// 设定高优先级给线程A
mtA->setPriority(QThread::InheritPriority);
}
if(indexB == 0)
{
// 设定低优先级给线程B
mtB->setPriority(QThread::IdlePriority);
}else{
// 设定高优先级给线程B
mtB->setPriority(QThread::InheritPriority);
}
}
void Dialog::valueSlotA(int value)
{
ui->progressBar_a->setValue(value);
}
void Dialog::valueSlotB(int value)
{
ui->progressBar_b->setValue(value);
}
mythread.h
signals:
// 发射给主线程进度值
void valueSignal(int);
mythread.cpp //注意结构体
#include "mythread.h"
MyThread::MyThread(QString name,QObject *parent) :
QThread(parent),name(name) // 构造初始化列表
{
}
void MyThread::run()
{
for(int i = 0;i<100;i++)
{
qDebug() << name << i;
msleep(50);
// 发射信号
emit valueSignal(i+1);
}
}
软件下载链接:https://pan.baidu.com/s/1R1GGFUudAvmF9_8JAQmf6g
提取码:hqyj
QSqlDatabase表示一个数据库的连接,在Qt中要使用数据库功能必须在.pro文件中引入sql模块,注意点击保存生效。
无论操作何种数据库,程序员只需要与Qt对接即可。
连接各种数据库的API基本相同,但是要有连接的数据库的驱动程序,Qt5之后的版本内置SQLite数据库,因此无需配置驱动程序可以直接使用。
SQLite数据库以.db或.db3格式的文件存储,其驱动名称为QSQLITE
在SQLite数据库中表示设置数据库存储文件的名称,但是在其它数据库中有其它功能
void QSqlDatabase::setDatabaseName(const QString & name)
连接的关键代码如下:
void Dialog::connectDB()
{
// 获得数据库连接的对象
db = QSqlDatabase::addDatabase("QSQLITE");
// 设置数据库存储的文件名
db.setDatabaseName("country_management.db");
// 对于其它版本或其它数据库还可能需要其它函数
// 例如:setUserName、setPassword
// 打开数据库连接
if(db.open())
{
qDebug() << "数据库连接打开成功!";
}else
{
// 拿到上一次出错的信息
QSqlError info = db.lastError();
// 拿到错误信息文本
QString text = info.text();
text.prepend("数据库连接错误!");
// 展示
QMessageBox::critical(this,"错误",text);
}
}
启动程序连接成功后,在构建目录下可以看到已经创建的数据库文件。
数据库连接最好用完关闭。
Dialog::~Dialog()
{
// 如果数据库打开,则关闭
if(db.isOpen())
db.close();
delete ui;
}
void Dialog::createTable()
{
// 创建数据库操作类对象
QSqlQuery sq;
// 编写建表语句
QString sql = "CREATE TABLE country(id INTEGER PRIMARY KEY,name TEXT,continent TEXT,power INTEGER);";
// 执行SQL语句
if(sq.exec(sql))
{
qDebug() << "建表成功!";
}else
{
// 拿到上一次出错的信息文本
QString text = sq.lastError().text();
text.prepend("建表失败!");
qDebug() << text;
}
}
需要注意只有打开连接成功了,才有执行建表语句的必要。建表成功后,可以去构建目录下使用SQLiteSpy验证是否成功。
按照之前的做法,用户输入的数据与基础的SQL语句需要拼接完成最终的执行,但是不建议这样做:
Qt提供了SQL语句预处理和数据绑定的方式来处理上述问题,有两种预处理的方式:
插入数据的关键代码如下:
void Dialog::insertData()
{
QString name = ui->lineEdit->text();
// 如果不输入国家名称,引导用户输入
if(name == "")
{
QMessageBox::warning(this,"提示","请输入国家名称!");
return;
}
int id = ui->spinBox->value();
QString continent = ui->comboBox->currentText();
int power = ui->horizontalSlider->value();
QSqlQuery sq;
// 编写预处理SQL语句
QString sql = "INSERT INTO country VALUES(?,?,?,?);";
// 预处理SQL语句
sq.prepare(sql);
// 绑定数据,参数直接送入占位符要替换的数据
// 使用Oracle占位符?必须按照顺序来替换绑定数据
sq.addBindValue(id);
sq.addBindValue(name);
sq.addBindValue(continent);
sq.addBindValue(power);
// 执行SQL语句
if(sq.exec())
{
qDebug() << "插入成功!";
// TODO 刷新查询结果
}else
{
// 拿到上一次出错的信息文本
QString text = sq.lastError().text();
text.prepend("插入失败!");
QMessageBox::warning(this,"提示",text);
}
}
void Dialog::deleteData()
{
// 拿到序号
int value = ui->spinBox->value();
QSqlQuery sq;
// 预处理SQL语句,在此使用ODBC写法
QString sql = "DELETE FROM country WHERE id=:id";
// 预处理
sq.prepare(sql);
// 绑定数据,ODBC写法可以打乱顺序‘
// 参数1:列名
// 参数2:要绑定的数据
sq.bindValue(":id",value);
if(sq.exec())
{
qDebug() << "删除操作成功!";
// TODO 刷新查询结果
}else
{
// 拿到上一次出错的信息文本
QString text = sq.lastError().text();
text.prepend("删除操作失败!");
QMessageBox::warning(this,"提示",text);
}
}
需要注意的是,sq.exec()函数的返回值仅仅是这条执行的SQL语句是否执行成功,至于上面代码中的数据在不在,真正是否删除此数据,sq.exec()函数的返回值无法判断。
因此需要在删除之前先判断一下此数据是否存在,这是一个WHERE子句的查询。
修改数据的关键代码如下:
void Dialog::updateData()
{
// 拿到序号
int id = ui->spinBox->value();
// TODO 判断要更新的数据是否存在
QString name = ui->lineEdit->text();
// 如果不输入国家名称,引导用户输入
if(name == "")
{
QMessageBox::warning(this,"提示","请输入国家名称!");
return;
}
QString continent = ui->comboBox->currentText();
int power = ui->horizontalSlider->value();
QSqlQuery sq;
QString sql = "UPDATE country SET name=?,continent=?,power=? WHERE id=?";
sq.prepare(sql);
// 注意按照顺序
sq.addBindValue(name);
sq.addBindValue(continent);
sq.addBindValue(power);
sq.addBindValue(id);
if(sq.exec())
{
qDebug() << "更新操作成功!";
// TODO 刷新查询结果
}else
{
// 拿到上一次出错的信息文本
QString text = sq.lastError().text();
text.prepend("更新操作失败!");
QMessageBox::warning(this,"提示",text);
}
}
基本上就是插入数据和删除数据的综合,之前两个操作存在的问题更新操作也存在。
在标准SQL语句中可以使用LIKE关键字来进行模糊查询,使用两个占位符:
一个下划线表示匹配任意一个字符。
一个百分号表示匹配任意多个字符。
例如,查询所有包含“新”的国家的SQL语句:
SELECT * FROM country WHERE name LIKE '%新%'; |
查询所有“国”字结尾的国家的SQL语句:
SELECT * FROM country WHERE name LIKE '%国'; |
查询第二个字是"度"的国家的SQL语句:
SELECT * FROM country WHERE name LIKE '_度%'; |
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
#include
#include
#include
#include // 数据库操作
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup* group;
QSqlDatabase db; // 数据库连接对象
// 连接数据库
void connectDB();
// 创建表
void createTable();
// 插入数据
void insertData();
// 删除数据
void deleteData();
// 修改数据
void updateData();
// 关键字查询
void likeSelectData();
// 查询所有数据
void allSelectData();
// 判断某个记录在不在
bool hasData(int id);
private slots:
// 点击按钮的槽函数
void btnsClickedSlot(int);
};
#endif // DIALOG_H
Qt的Socket网络编程支持TCP和UDP,考虑上位机的实际使用场景,本次学习以TCP连接为例。
IP地址用于在同一个网络中,找到通信的网络硬件设备;端口号用于在一个网络设备中找到对应的通信软件进程。端口号的选用要在2000以上,避开一些常见的数字,例如8888、6666等。
本次网络编程主要使用两个类:
服务器管理类,重点是管理。
TCP连接类,重点是连接。
Server接口:
QTcpServer::QTcpServer(QObject * parent = 0)
构造函数
bool QTcpServer::listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
开启监听服务,参数1是监听哪个IP地址(默认值为都监听),参数2为运行的端口号,返回值为监听是否成功
void QTcpServer::newConnection() [signal]
新连接来了发射的信号
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
返回与客户端建立连接的对象,这个对象属于服务器连接管理类的子对象,跟随管理类对象一起销毁。
QHostAddress QAbstractSocket::peerAddress() const
获得对面连接(客户端)的IP地址
quint16 QAbstractSocket::peerPort() const
获得对面连接(客户端)的端口号
void QIODevice::readyRead() [signal]
接收消息的信号槽
QString QTextStream::readAll()
一次性把所有数据读出,返回值是读到的数据,如果不清楚数据量建议使用readLine
Client接口:
QTcpSocket::QTcpSocket(QObject * parent = 0)
构造函数
连接到主机
参数1:主机IP地址
参数2:主机端口号
参数3:读写模式,默认为可读可写
void QAbstractSocket::connected() [signal]
连接成功发射的信号
void QAbstractSocket::disconnected() [signal]
连接失败发射的信号
QTextStream::QTextStream(QIODevice * device)
创建一个文本流对象的构造函数,参数为Qt中支持IO操作的对象。
QTextStream & QTextStream::operator<<(const QString & string)
发送文本内容,注意使用QString而不是char*,此函数支持链式调用
服务器
.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
#include
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
// 服务器连接管理类
QTcpServer *server;
// 只保存最新的连接对象
QTcpSocket *socket = NULL;
void printDateTime(); // 在公屏上打印当前日期和时间
private slots:
// 新连接来了的 信号槽
void newConnectionSlot();
// 连接失败或断线
void disconnectedSlot();
// 读取消息
void readReadSlot();
};
#endif // DIALOG_H
.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 设置处于最前台
setWindowFlags(Qt::WindowStaysOnTopHint);
// 设置窗口标题
setWindowTitle("服务器");
// 创建一个服务器连接管理类
server = new QTcpServer(this);
// 在8887端口开启监听服务
bool result = server->listen(QHostAddress::Any,8887);
if(!result)
{
QMessageBox::critical(this,"错误","监听服务开启失败!");
return;
}
printDateTime();
ui->textBrowser->append("监听服务开启成功,端口号8887");
// 建立服务器检测到新连接来的信号槽
connect(server,SIGNAL(newConnection()),
this,SLOT(newConnectionSlot()));
}
Dialog::~Dialog()
{
// 如果监听服务已经开启,则关闭服务
if(server->isListening())
server->close();
delete ui;
}
void Dialog::printDateTime()
{
QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
ui->textBrowser->append(dt);
}
void Dialog::newConnectionSlot()
{
// 如果之前已经有了连接,踢掉
if(socket != NULL && socket->isOpen())
{
socket->close();
// 断开检测信号槽
disconnect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
// 断开读取消息的信号槽
disconnect(socket,SIGNAL(readyRead()),
this,SLOT(readReadSlot()));
}
// 保存最新的连接对象
socket = server->nextPendingConnection();
// 给客户端回句话问候一下
QTextStream ts(socket);
QString text = QDateTime::currentDateTime().toString("hh:mm:ss").prepend("已经接受了你的连接,连接时间:");
ts << text;
// 建立断连的信号槽连接
connect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
// 建立读取消息的信号槽连接
connect(socket,SIGNAL(readyRead()),
this,SLOT(readReadSlot()));
// 获得客户端的IP地址和端口号
QString ip = socket->peerAddress().toString();
quint16 port = socket->peerPort();
QString portText = QString::number(port);
// 拼接
ip.prepend("新连接来了!").append(":").append(portText);
printDateTime();
ui->textBrowser->append(ip);
}
void Dialog::disconnectedSlot()
{
// 获得客户端的IP地址和端口号
QString ip = socket->peerAddress().toString();
quint16 port = socket->peerPort();
QString portText = QString::number(port);
// 拼接
ip.prepend("连接已断开!").append(":").append(portText);
printDateTime();
ui->textBrowser->append(ip);
}
void Dialog::readReadSlot()
{
// 创建文本流对象
QTextStream ts(socket);
QString text = ts.readAll();
printDateTime();
ui->textBrowser->append(text);
}
用户
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include
#include
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QTcpSocket *client = NULL; // 客户端连接对象
private slots:
// 连接按钮
void btnConnClickedSlot();
// 发送按钮
void btnSendClickedSlot();
// 连接成功
void connectedSlot();
// 连接失败或断线
void disconnectedSlot();
// 读取消息
void readyReadySlot();
};
#endif // DIALOG_H
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 设置处于最前台
setWindowFlags(Qt::WindowStaysOnTopHint);
// 设置窗口标题
setWindowTitle("客户端");
connect(ui->pushButton_conn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButton_send,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot()));
}
Dialog::~Dialog()
{
// 如果还有连接存在,关闭连接
if(client != NULL && client->isOpen())
{
// 断开Socket断连的信号槽
disconnect(client,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
client->close();
}
delete ui;
}
void Dialog::btnConnClickedSlot()
{
// 先获取用户输入(默认格式正确)
QString ip = ui->lineEdit_ip->text();
int port = ui->spinBox->value();
// 创建客户端连接对象
client = new QTcpSocket(this);
// 【注意】网络状态信号槽的连接
connect(client,SIGNAL(connected()),
this,SLOT(connectedSlot()));
connect(client,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
// 发起连接
client->connectToHost(ip,port);
}
void Dialog::btnSendClickedSlot()
{
// 拿到昵称
QString name = ui->lineEdit_user->text();
if(name == "")
{
QMessageBox::warning(this,"提示","请输入昵称!");
return;
}
// 拿到消息
QString msg = ui->lineEdit_msg->text();
if(msg == "")
{
QMessageBox::warning(this,"提示","请输入要发送的消息!");
return;
}
// 创建文本流对象
QTextStream ts(client);
name.append(":").append(msg);
// 发送消息
ts << name;
// 清空输入,给下次输入使用
ui->lineEdit_msg->clear();
}
void Dialog::connectedSlot()
{
// 建立读消息的信号槽
connect(client,SIGNAL(readyRead()),
this,SLOT(readyReadySlot()));
// 屏蔽连接按钮
ui->pushButton_conn->setEnabled(false);
ui->pushButton_conn->setText("已连接");
// 开启发送按钮
ui->pushButton_send->setEnabled(true);
}
void Dialog::disconnectedSlot()
{
// 断开读取消息的信号槽
disconnect(client,SIGNAL(readyRead()),
this,SLOT(readyReadySlot()));
// 开启连接按钮
ui->pushButton_conn->setEnabled(true);
ui->pushButton_conn->setText("连接");
// 屏蔽发送按钮
ui->pushButton_send->setEnabled(false);
QMessageBox::warning(this,"提示","连接失败!");
}
void Dialog::readyReadySlot()
{
QTextStream ts(client);
QString text = ts.readAll();
QMessageBox::information(this,"接收到服务器的消息",text);
}
1.下载或设计一款应用程序图标,分辨率最低64x64,最高256x256
2. 把图片文件转换为图标格式,windows的图标格式是.ico
PNG转ICO, 在线转换器 - 转换视频, 音乐, 图像, PDF - Office-Converter.com
Convertio — 文件转换器
3. 把.ico文件放置到工作目录下。
4. 在Qt Creator中,选中项目名称,鼠标右键,点击“添加新文件”。
5. 在弹出的窗口中,按照下图所示进行操作。
6. 在弹出的窗口中,输入图标配置文件的名称(注意扩展名.rc必须写)后,点击“下一步”。
7. 在项目管理界面,直接点击“完成”
8. 在创建出的.rc文件中输入,双引号中的内容是图标文件的名称。
9. 在项目配置文件.pro中,添加一行代码,右边的文件是图标配置文件。
RC_FILE += icon_config.rc
10. 运行项目,观察图标是否设置成功。
Qt项目有两种构建模式:Debug和Release模式
默认的模式是Debug模式,可以在左下角切换为Release模式。
Debug模式下,生成的可执行文件中包含调试信息,运行速度比较慢,体积比较大,适合程序员进行开发调试,例如在Debug模式的构建目录下,进入到debug文件夹,可执行文件的体积较大。
Release模式下,生成的程序不含调试信息,并且会做出特别的优化,因为可执行文件的体积小,运行速度快,但是不适合开发调试,适合发布给用户使用,例如在Release模式的构建目录下,进入到release文件夹,可执行文件的体积较小。
双击可执行文件,可以发现只有可执行文件项目无法运行。
但是上面的项目需要用到数据库文件,所以还需要在这个目录中放置数据库文件,即exe可执行文件所在的目录就相当于之前的构建目录与工作目录。
获取匹配的动态库:
输入命令:
windeployqt.exe E:\Project\\debug\dict.exe
//系统文件 //文件目录————可执行文件
本次授课使用打包工具为isetup,安装文件下载链接:https://pan.baidu.com/s/18LGc4isU00UdbQGrCStsgg
提取码:hqyj
软件安装过程一路下一步即可,软件启动后的界面如下所示。
1. 选择创建新脚本后,点击“OK”。
2. 在新弹出的窗口中,点击“Next”
3. 配置相关参数后,点击“Next”
4. 配置安装参数后,点击“Next”
5. 首先选择要打包程序的可执行文件,然后点击“Add folder”,在弹出的窗口中选中打包程序的根目录,注意要包含子目录。最后点击“Next”
6. 在弹出的窗口中,配置开始菜单和图标的选项后,点击“Next”
7. 在弹出的页面中,直接点击“Next”跳过安装许可协议线相关的配置。
8. 在弹出的窗口中选中安装包的语言后,点击“Next”。
9. 在弹出的窗口中,配置安装相关参数后,点击“Next”
10. 在弹出的窗口中,直接点击“Next”
11. 在弹出的窗口中,点击“Next”进入编译界面。
12. 根据是否需要来决定是否存储安装包编译脚本。
13. 等待编译完成即可。