QT周记大总结

初识UI

QT周记大总结_第1张图片

增加子组件

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。所有在水平布局内部的组件线性水平排布。

 QT周记大总结_第2张图片

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常用属性

QT周记大总结_第3张图片

 QT周记大总结_第4张图片

 ui指针工作流程

 QLabel 标签

QT周记大总结_第5张图片

 建议使用必应搜索下载图片。

#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作为抽象基类,规定了按钮的各种基本功能。

QT周记大总结_第6张图片

 QT周记大总结_第7张图片

 

 软件图标设计学习交流可以参考网站:阿里巴巴矢量图标库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;
}
…
}

 QLineEdit 单行文本编辑框

QT周记大总结_第8张图片

 QT周记大总结_第9张图片

 QComboBox 组合框

        与QRadioButton比较相似,用于选择一个单选选项,但是组合框比QRadioButton占用空间更小。

        在Designer中双击QComboBox可以编辑QComboBox中的选项(Item,Qt Creator中翻译为“项目”),常用属性如下:

 QT周记大总结_第10张图片

QT周记大总结_第11张图片

// 获取用户输入
    QString text = ui->lineEdit->text();
    // 如果用户不输入
    if(text == "")
        return;
    // 给组合框增加新选项
    ui->comboBox->insertItem(0,text);
    // 清空输入
    ui->lineEdit->clear();

 若干与数值和进度相关的组件

 QT周记大总结_第12张图片

 QT周记大总结_第13张图片

//赋初值
ui->horizontalscrollBar->setvalue (value) ;
ui->verticalscrollBar->setvalue (value) ;

字符串类 QString

QT周记大总结_第14张图片

QT周记大总结_第15张图片

 日期时间相关类

Qt中使用QDate类表示日期,使用QTime类表示时间,QDate和QTime类合并为QDateTime类,表示日期和时间。本次课程以QDateTime类为主,进行讲解和示例。

常用函数如下:

  • qint64 QDateTime::currentMSecsSinceEpoch() [static]
  • 获得当前的时间,返回一个64位的整数,此整数由当前时间减去1970年1月1日 凌晨00:00:00的毫秒数。
  • QDateTime    currentDateTime()
  • 返回基于当前时区的日期时间类对象
  • QDateTime    currentDateTimeUtc()
  • 返回基于格林威治时间的日期时间类对象
  • QString QDateTime::toString(const QString & format) const
  • 获得制定格式的日期和时间数据,数据来自于当前的操作系统,参数为格式

QT周记大总结_第16张图片

//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") << "号";
}

 定时器类 QTimer

常用函数如下:

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. 在弹出的窗口中,按照下图所示进行操作。

QT周记大总结_第17张图片 3. 在弹出的窗口中输入自定义类名,后点击“下一步”。

 QT周记大总结_第18张图片

 4. 在项目管理界面直接点击“完成”,可以看到Qt项目中存在自定义类的头文件和源文件。

QT周记大总结_第19张图片

 QList

.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;
}

QMap

.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是一种模态对话框,即弹出时必须优先处理。

        QMessageBox是QDialog的派生类,这些Qt内置的QDialog的派生类设计的目的是减少程序员的编程量,可以直接通过静态成员函数调用使用。这些静态成员函数的返回值往往是这个对话框的结果,例如QMessageBox的返回值是用户点击的按钮、QFontDialog的返回值是用户选择的字体,QColorDialog的返回值是用户选择的颜色......

        QMessageBox用于信息提示,预设有四种样式。

QT周记大总结_第20张图片

 

参数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)
        {

QMainWindow 主窗口类

QT周记大总结_第21张图片

QWidget类是所有组件和窗口的基类,其对象创建时,如果传入parent参数就是其它窗口的子组件,如果不传入参数就是一个独立的窗口。一般不使用QWidget作为窗口或组件类型。

QWidget也规定了一些窗口类通用的函数:

        设置窗口的标题

void       setWindowTitle(const QString &)

        设置窗口状态

void QWidget::setWindowState(Qt::WindowStates windowState)

QT周记大总结_第22张图片

        设置窗口标记

void       setWindowFlags(Qt::WindowFlags type)

 QT周记大总结_第23张图片

窗口类型更多地会选用QDialog和QMainWindow两种类型, QMainWindow通常会作为应用程序的主窗口,而QDialog通常作为应用程序的子窗口。

QMainWindow适合作为中大型应用程序的主窗口的原因是其包含菜单栏、工具栏、中心组件和状态栏。

QT周记大总结_第24张图片

QT周记大总结_第25张图片 菜单栏 QMenuBar

 菜单的功能是折叠应用程序的功能选项,建议使用Designer添加相关选项,每个可以选择或点击的选项都是一个QAction对象。

QT周记大总结_第26张图片

在Designer的Action编辑器区域,双击对应的QAction可以编辑其细节。

QT周记大总结_第27张图片

使用下面的信号可以建立点击QAction的信号槽

void QAction::triggered(bool checked = false) [signal]

 

工具栏 QToolBar

工具栏中每个按钮QToolButton实际上都是对应的QAction。在Designer中可以很方便地把QAction直接拖拽到工具栏即可。

QT周记大总结_第28张图片

中心组件 QWidget

此区域相当于之前的QDialog,直接通过布局设计效果即可。

QT周记大总结_第29张图片

QT周记大总结_第30张图片

状态栏 QStatusBar

用于显示应用程序使用过程中的相关信息。

基本使用可以通过两个槽函数完成:

  1. 展示消息

void QStatusBar::showMessage(const QString & message, int timeout = 0) [slot]

参数1:展示消息的内容

参数2:消息持续时长,单位毫秒,如果使用默认值0,则表示一直显示直到清空

  1. 清空消息

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参数的对象成为父对象,新创建的对象称为子对象,每个父对象内部存储子对象列表,只有父对象销毁时,才销毁所有的子对象。

        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();
}

自定义Qt窗口类

QT周记大总结_第31张图片

 QT周记大总结_第32张图片

QT周记大总结_第33张图片

 QT周记大总结_第34张图片

.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周记大总结_第35张图片

QT周记大总结_第36张图片

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);
}

文件对话框 QFileDialog

弹出打开单文件的对话框

保存单文件的对话框

参数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

// 创建文件信息类对象,如果送入一个不存在的文件路径,也可以创建
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);

文件读写类 QFile

.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();
    }
}

UI操作与耗时操作

QT周记大总结_第37张图片

 多线程

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() << "子线程耗时操作结束";
}

线程的优先级

QT周记大总结_第38张图片

 

设定的方式有两种:

  1. 在启动的start函数中设定,默认值是最高优先级QThread::InheritPriority
  2. 启动后调用void QThread::setPriority(Priority priority)函数,也可以通过Priority QThread::priority() const来获取线程的优先级。

优先级的设定仅仅表示在某一时间点,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);
    }
}

辅助工具SQLiteSpy

QT周记大总结_第39张图片

QT周记大总结_第40张图片

        软件下载链接:https://pan.baidu.com/s/1R1GGFUudAvmF9_8JAQmf6g 

        提取码:hqyj

数据连接类 QSqlDatabase

Qt数据库基础

        QSqlDatabase表示一个数据库的连接,在Qt中要使用数据库功能必须在.pro文件中引入sql模块,注意点击保存生效。

QT周记大总结_第41张图片QT周记大总结_第42张图片

QT周记大总结_第43张图片

        无论操作何种数据库,程序员只需要与Qt对接即可。

QT周记大总结_第44张图片 连接数据库

        连接各种数据库的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);
    }
}

启动程序连接成功后,在构建目录下可以看到已经创建的数据库文件。

QT周记大总结_第45张图片

QT周记大总结_第46张图片

 数据库连接最好用完关闭。

Dialog::~Dialog()
{
    // 如果数据库打开,则关闭
    if(db.isOpen())
        db.close();
    delete ui;
}

 数据库操作类 QSqlQuery

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验证是否成功。

QT周记大总结_第47张图片

 

插入数据

按照之前的做法,用户输入的数据与基础的SQL语句需要拼接完成最终的执行,但是不建议这样做:

  1. 比较繁琐,非常容易出错
  2. 可能会引起SQL注入的安全问题

Qt提供了SQL语句预处理和数据绑定的方式来处理上述问题,有两种预处理的方式:

  1. Oracle:使用?作为参数占位符
  2. ODBC:使用:列名作为参数占位符

插入数据的关键代码如下:

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关键字来进行模糊查询,使用两个占位符:

  1. _

一个下划线表示匹配任意一个字符。

  1. %

一个百分号表示匹配任意多个字符。

例如,查询所有包含“新”的国家的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等。

QT周记大总结_第48张图片

 本次网络编程主要使用两个类:

  1. QTcpServer                         //服务器

服务器管理类,重点是管理。

  1. QTcpSocket                         //用户端

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*,此函数支持链式调用

QT周记大总结_第49张图片

服务器

.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

 QT周记大总结_第50张图片

 

#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);
}

QT周记大总结_第51张图片

设置图标

1.下载或设计一款应用程序图标,分辨率最低64x64,最高256x256

2. 把图片文件转换为图标格式,windows的图标格式是.ico

PNG转ICO, 在线转换器 - 转换视频, 音乐, 图像, PDF - Office-Converter.com

Convertio — 文件转换器

3. 把.ico文件放置到工作目录下。

4. 在Qt Creator中,选中项目名称,鼠标右键,点击“添加新文件”。

5. 在弹出的窗口中,按照下图所示进行操作。

QT周记大总结_第52张图片 6. 在弹出的窗口中,输入图标配置文件的名称(注意扩展名.rc必须写)后,点击“下一步”。

QT周记大总结_第53张图片 

7. 在项目管理界面,直接点击“完成”

8. 在创建出的.rc文件中输入,双引号中的内容是图标文件的名称。

QT周记大总结_第54张图片 

9. 在项目配置文件.pro中,添加一行代码,右边的文件是图标配置文件。

RC_FILE += icon_config.rc

10. 运行项目,观察图标是否设置成功。

 

Debug模式与Release模式

Qt项目有两种构建模式:Debug和Release模式

默认的模式是Debug模式,可以在左下角切换为Release模式。

Debug模式下,生成的可执行文件中包含调试信息,运行速度比较慢,体积比较大,适合程序员进行开发调试,例如在Debug模式的构建目录下,进入到debug文件夹,可执行文件的体积较大。

Release模式下,生成的程序不含调试信息,并且会做出特别的优化,因为可执行文件的体积小,运行速度快,但是不适合开发调试,适合发布给用户使用,例如在Release模式的构建目录下,进入到release文件夹,可执行文件的体积较小。

双击可执行文件,可以发现只有可执行文件项目无法运行。

QT周记大总结_第55张图片

动态链接库

但是上面的项目需要用到数据库文件,所以还需要在这个目录中放置数据库文件,即exe可执行文件所在的目录就相当于之前的构建目录与工作目录。

获取匹配的动态库:

QT周记大总结_第56张图片

输入命令:

windeployqt.exe  E:\Project\\debug\dict.exe

//系统文件     //文件目录————可执行文件

打包 

本次授课使用打包工具为isetupQT周记大总结_第57张图片,安装文件下载链接:https://pan.baidu.com/s/18LGc4isU00UdbQGrCStsgg 

提取码:hqyj

软件安装过程一路下一步即可,软件启动后的界面如下所示。

QT周记大总结_第58张图片下面是打包的步骤:

1. 选择创建新脚本后,点击“OK”。

QT周记大总结_第59张图片

2. 在新弹出的窗口中,点击“Next”

QT周记大总结_第60张图片

 3. 配置相关参数后,点击“Next”

QT周记大总结_第61张图片

 4. 配置安装参数后,点击“Next”

QT周记大总结_第62张图片

 5. 首先选择要打包程序的可执行文件,然后点击“Add folder”,在弹出的窗口中选中打包程序的根目录,注意要包含子目录。最后点击“Next”

QT周记大总结_第63张图片

6. 在弹出的窗口中,配置开始菜单和图标的选项后,点击“Next”

QT周记大总结_第64张图片

7. 在弹出的页面中,直接点击“Next”跳过安装许可协议线相关的配置。

QT周记大总结_第65张图片 

8. 在弹出的窗口中选中安装包的语言后,点击“Next”。

 9. 在弹出的窗口中,配置安装相关参数后,点击“Next”

QT周记大总结_第66张图片

 10. 在弹出的窗口中,直接点击“Next”

QT周记大总结_第67张图片

 11. 在弹出的窗口中,点击“Next”进入编译界面。

QT周记大总结_第68张图片

 12. 根据是否需要来决定是否存储安装包编译脚本。

QT周记大总结_第69张图片

13. 等待编译完成即可。

QT周记大总结_第70张图片

你可能感兴趣的:(ui)