Qt学习笔记

stvsl 2022-6-22 17,960 6/22

1. 软件相关

  1. qt助手(将会被自动安装,直接打开即可)

    archlinuxKDE环境下下只需要使用yay -S qtcreator进行安装即可

    查看是否安装成功

    qmake -V
  2. 工具链

    qmake(qt构建器)

    qtdesigner(快速界面设计器)

    uic(qt转换器,将界面设计器设计的文件转换成C++头文件)

    rcc(qt资源编译器,将媒体资源编译成C++源文件形式,方便静态编译)

    moc(qt元对象编译器,将qt扩展代码特性转换成标准C++代码)

    qtcreator(qt开发核心IDE)

2. 第一个程序创建

  1. helloword源码

    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
       //创建qt应用程序对象
       QApplication app(argc,argv);
       //创建标签控件
       QLabel label("Hello! QT!");
       //显示标签控件
       label.show();
       //让应用程序进入事件循环
       return app.exec(); 
    }
  2. 配置文件生成

    在项目文件夹内执行此命令生成工程配置文件

    qmake -project
  3. 向配置文件内添加指定的所需模块

    QT += widgets
  4. 使用qmake构建make文件

    qmake
  5. 使用make进行编译

    make
  6. 运行

    最终编译结果请查看./P1/study/helloword/中的内容,点击helloword无后缀文件即可运行

  7. 衍生扩展(一个按钮一个标签)

    1. 源码

      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
        QApplication app(argc,argv);
        QLabel label("我是标签");
        Label.show();
        QPushButton button("我是按钮");
        button.show();
        return app.exec();
      }
    2. 运行结果:两个窗口,最终编译结果请查看./P1/study/Button/中的内容

  8. 中文编码问题的解决方案

    QT内部使用UTF-16文件编码格式(Windows一般使用GBK编码格式,Linux一般使用UTF-8) (但此处无需担心此问题,qt5编译器会对其进行自动转换) ,但在windows环境下需要进行编码格式转换

    需要进行转化时程序内部转换写法:

    //引入编码转换器
    #include 
    //创建编码对象
    QTextCodec* coder = QTextCodec::codecForName("gbk");
    //将要显示的gbk编码字符串转换成utf-16编码
    QString string = oder->toUnicode("GBK等等的其它格式的字符串");

3. 父窗口(Parent)

在qt中,默认每个控件都会生成一个窗口,这与GUI程序逻辑和结构不符合,因此,在qt中定义了父窗口的概念,并依靠此进行统一的界面管理

  • 创建控件时,可以指定其停靠在某个父窗口上面,以此来绑定一组控件的排列布局

  • QWidget及其子类的对象可以作为其他控件的父窗口

  • 常用的父窗口类有以下三个(一般按照实际开发场景进行选择和使用)

    1. QWidget(标准窗口)
    2. QMainWindow(主窗口,是QWidget的直接子类,一般作为大型程序的入口)
    3. QDialog(对话框,是QWidget的直接子类,一般在特别小的程序上面使用)
  • 父窗口的析构函数结束时会自动销毁所有的内部控件对象,因此即使子窗口不是使用new操作动态创建的也可以不显式执行delete操作,且不必担心内存泄漏的问题,只需要保证父窗口被正确销毁即可

  • 设置窗口以及控件的位置和大小的函数

    //设置位置
    void move (int x,int y);
    //设置大小
    void resize (int w,int h);
  • 三种窗口的缺省效果

    1. Qwiget

      源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       QWidget parent;
       parent.show();
       return app.exec();
      }

      运行结果:一个标准空白窗口,最终编译结果请查看./P1/study/P1/Parent/QWidgetest/中的内容

    2. QMainWindow

      源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       QMainWindow parent;
       parent.show();
       return app.exec();
      }

      运行结果:一个较小的空白窗口,最终编译结果请查看./P1/study/P1/Parent/MainWindowtest/中的内容

    3. QDialog

      源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       QDialog parent;
       parent.show();
       return app.exec();
      }

      运行结果:一个居中空白窗口,最终编译结果请查看./P1/study/P1/Parent/QDialogtest/中的内容

      设置其位置和大小:

      修改后的源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       QDialog parent;
       //设置位置
       parent.move(50,50);
       //设置窗口大小
       parent.resize(320,240);
       parent.show();
       return app.exec();
      }

      最终编译结果请查看./P1/Parent/ChangedQDialogtest/中的内容

    4. 在父窗口上面停靠控件

      源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       //创建对话框窗体
       QDialog parent;
       //设置位置
       parent.move(50,50);
       //设置窗口大小
       parent.resize(320,240);
       //创建标签控件,并设置标签显示的内容和停靠对象(传递指针)
       QLabel label("我是标签",&parent);
       //设置标签位置
       label.move(20,40);
       //创建按钮控件,并设置按钮显示的内容和停靠对象(传递指针)
       QPushButton button("我是按钮",&parent);
       //设置按钮位置
       button.move(20,100);
       //设置按钮大小
       button.resize(80,80);
       //显示父窗口(内部绑定的控件对象不需要手动显示)
       parent.show();
       return app.exec();
      }

      运行结果请查看./P1/Parent/ChangedQDialogtest/中的内容

    5. 父窗口中的对象销毁

      源码:

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main(int argc, char *argv[])
      {
       QApplication app(argc,argv);
       //创建对话框窗体
       QDialog parent;
       //设置位置
       parent.move(50,50);
       //设置窗口大小
       parent.resize(320,240);
       //创建标签控件(栈内存中),并设置标签显示的内容和停靠对象(传递指针)
       QLabel label("我是标签",&parent);
       //设置标签位置
       label.move(20,40);
       //创建按钮控件(栈内存中),并设置按钮显示的内容和停靠对象(传递指针)
       QPushButton button("我是按钮",&parent);
       //设置按钮位置
       button.move(20,100);
       //设置按钮大小
       button.resize(80,80);
       //在堆内存中申请创建按钮对象,并设置按钮显示的内容和停靠对象(传递指针)
       QPushButton* button2 = new QPushButton("我是按钮",&parent);
       //设置按钮位置
       button2->move(170,100);
       //设置按钮大小
       button2->resize(80,60);
       //显示父窗口(内部绑定的控件对象不需要手动显示)
       parent.show();
       return app.exec();
      }

      运行结果请查看./P1/Parent/ObjectControl/中的内容

      • 请注意! 栈内存中的对象会随着父窗口的自动销毁而自动销毁,而使用new操作在堆内存中申请的成员也不需要手动销毁,只要指定了父窗口指针就不需要手动delect,在父窗口结束时也会自动被销毁,以免发生内存溢出,当然也可以手动delete,以此可以以最小的代价完成与界面动态更新相关的更多功能

4. 信号与槽机制

  • 信号和槽机制是QT自行定义的一种通信机制,用来实现对象之间的信号交互,当用户或系统触发了一个动作,导致某个控件的状态发生了改变,该控件就会发送一个信号,即自动调用执行其类中原有的一个函数--槽函数(此函数允许携带参数),槽和普通的数据成员没有太多区别,访问属性可以在是公有,保护,私有的,允许重载和覆盖,参数类型任意,同时可以像普通函数一样被调用
  • 槽函数和普通数据成员函数区别不在于语法特性,只是功能区别,槽函数体现的是对特性信号的处理,可以将槽函数和其它对象的信号建立链接,当信号被发出时,槽函数将被自动触发和执行,进而完成特定的功能
  1. 信号的定义格式

    class XX:public QObject{
    Q_OBJECT    //moc编译器宏标记,由于此代码并不是标准C++代码,因此不能被直接进入C++编译器进行编译,需要qt的moc(元对象编译器)对此部分进行处理,将其自动翻译成标准C++代码
    signals:  //访问控制限定符
    void signal_func(...); //信号函数
    };
    /* 请注意! 信号函数只需要声明,不能写定义!!!*/
  2. 槽的定义格式

    class XX:public QObject{
    Q_OBJECT
    public slots: //访问控制限定符
    void slot_func(...){ //槽函数
    ...
    }
    };
  3. 建立信号和槽的连接

    QT使用的连接方法是一个被封装在顶层基类类内部的一个静态函数:connect函数

    函数格式:

    QObject::connect(const QObject* sender,const char* signal,const QObject* reciver,const char* method);
    //翻译
    QObject::connect(信号发送对象指针,想要发送的信号函数,信号接收对象的指针,收到信号要执行的槽函数);
    /* 注意!!!
    信号函数可以使用SIGNAL(...)宏进行类型转换 省略号表示要转换的信号函数
    槽函数可以使用SLOT(...) 宏进行类型转换 省略号表示要转换的槽函数
    指针允许向上造型
    */
  4. 源码案例:点击按钮关闭标签

    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
       QApplication app(argc,argv);
       QDialog parent;
       parent.resize(320,240);
       QLabel label("我是标签",&parent);
       label.move(50,40);
       QPushButton button("我是按钮",&parent);
       button.move(50,100);
       QPushButton button2("退出",&parent);
       button2.move(150,100);
       parent.show();
       //点击按钮关闭标签
       QObject::connect(&button,SIGNAL(clicked(void)),&label,SLOT(close(void)));
       //点击按钮关闭窗口
       //QObject::connect(&button2,SIGNAL(clicked(void)),&app,SLOT(closeAllWindows(void)));
       QObject::connect(&button2,SIGNAL(clicked(void)),&app,SLOT(quit(void)));//与上一条功能相同
       QObject::connect(&button2,SIGNAL(clicked(void)),&parent,SLOT(close(void)));
       return app.exec();
    }

    运行结果请查看./P2/Close/中的内容

  5. 语法要求

    • 信号和槽的参数要一致

      eg:

      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* reciver,SLOT(slotfun(int))); //可编译通过
      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* reciver,SLOT(slotfun(int,int))); //可编译报错
    • 允许带有缺省参数

      eg:

      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* reciver,SLOT(slotfun(int,int = 0))); //可编译通过
    • 信号函数的参数可以多于槽函数,多于的参数将会被自动忽略

      eg:

      QObject::connect(const QObject* sender,SIGNAL(signalfun(int,int)),const QObject* reciver,SLOT(slotfun(int))); //可编译通过
  6. 信号和槽连接的应用

    • 一个信号多个槽

      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* reciver1,SLOT(slotfun1(int))); //可编译通过
      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* reciver2,SLOT(slotfun2(int))); //可编译通过
    • 多个信号一个槽

      QObject::connect(const QObject* sender1,SIGNAL(signalfun1(int)),const QObject* reciver,SLOT(slotfun(int))); //可编译通过
      QObject::connect(const QObject* sender2,SIGNAL(signalfun2(int)),const QObject* reciver,SLOT(slotfun(int))); //可编译通过
    • 两个信号直接连接(信号级连,实现跨作用域消息的通知)

      QObject::connect(const QObject* sender,SIGNAL(signalfun(int)),const QObject* sender,SIGNAL(signalfun(int))); //可编译通过
  7. 源码案例:同步运行

    #include 
    #include       //滑块
    #include      //选值框
    #include 
    
    int main(int argc, char *argv[])
    {
       QApplication app(argc,argv);
       QDialog parent;
       parent.resize(180,100);
       //创建水平滑块
       QSlider slider(Qt::Horizontal,&parent);
       //设置滑块位置
       slider.move(15,30);
       //设置滑块滑动范围
       slider.setRange(0,10);
       //创建选值框
       QSpinBox spinbox(&parent);
       //设置选值框位置
       spinbox.move(110,30);
       //设置选值框范围
       spinbox.setRange(0,10);
       //滑块滑动影响选值框
       QObject::connect(&slider,SIGNAL(valueChanged(int)),&spinbox,SLOT(setValue(int)));
       //选值框影响滑块滑动
       QObject::connect(&spinbox,SIGNAL(valueChanged(int)),&slider,SLOT(setValue(int)));
       parent.show();
       return app.exec();
    }

    运行结果请查看./P2/Synchronize/中的内容

5. 面向对象的QT编程

​ 面向对象的QT编程和标准C++面向对象基本没有区别,主要分为基于对象上的QT编程和面向对象的QT编程

  • 基于对象的QT编程的问题

    1. 完全不使用任何面向对象的技术,而只利用QT所提供的类来创建对象,并调用接口以满足程序开发,由于内部对象内容较少,因此很难满足全部的开发需求,原因如下:
      • QT类中的保护成员中的诸多功能无法实现在类的外部被调用,QT通过多态实现的很多机制,例如时间处理完全无法使用
      • QT内部提供的信号和槽机制无法万阻全部程序开发需求
      • QTCreator,QTDesigner等工具连均使用面向对象的方式使用QT,若只使用面向对象将会与工具链本身作用发生矛盾
  • 源码案例: 通过面向对象的编程思想实现加法计算器

    设计要求:

    1. 输入2个数字,按下=按钮显示计算结果
    2. 进行异常处理,不接受非法字符
    3. 两个操作数只在均合法时等号才被激活,否则禁用
    4. 显示结果的控件只可查看不可修改,但可以复制到剪切板
    5. 所有子窗口的大小和位置随主窗口的缩放支持自适应

    源码:

    • 头文件CalculatorDialog.h
    #ifndef __CALCULATORDIALOG_H //若未定义,则定义
    #define __CALCULATORDIALOG_H
    
    #include 
    #include 
    #include 
    #include         //行编辑控件
    #include       //水平布局器
    #include  //数字验证器
    
    class CalculatorDialog : public QDialog
    {
        Q_OBJECT //moc元对象编译器翻译标志
    private:
        /* data */
        QLineEdit* m_edit_X;//左操作数
        QLineEdit* m_edit_Y;//右操作数
        QLineEdit* m_edit_Z;//显示计算结果
        QLabel* m_label;//"显示"+""
        QPushButton* m_button;//显示"=" 
    
    public:
        CalculatorDialog(void);
    
    public slots:
        //显示计算结果的槽函数
        void clacClicked(void);
        //使能等号按钮的操作数
        void enableButton(void);
    };
    
    #endif /* __CALCULATORDIALOG_H */
    • 源文件CalculatorDialog.cpp
    #include "CalculatorDialog.h"
    
    //构造函数
    CalculatorDialog::CalculatorDialog(void){
        /*界面初始化*/
        //设置窗口标题
        setWindowTitle("计算器");
        //创建左操作数,this指向当前父窗口指针
        m_edit_X = new QLineEdit(this);
        //设置右对齐
        m_edit_X->setAlignment(Qt::AlignRight);
        //设置数字验证器,使其只能输入数字形式的内容
        m_edit_X->setValidator(new QDoubleValidator(this));
        //创建右操作数
        m_edit_Y =  new QLineEdit(this);
        //设置右对齐
        m_edit_Y->setAlignment(Qt::AlignRight);
        //设置数字验证器
        m_edit_Y->setValidator(new QDoubleValidator(this));
        //创建显示结果的控件
        m_edit_Z = new QLineEdit(this);
        //设置右对齐
        m_edit_Z->setAlignment(Qt::AlignRight);
        //设置只读
        m_edit_Z->setReadOnly(true);
        //创建加号标签对象
        m_label = new QLabel("+",this);
        //创建等号按钮对象
        m_button = new QPushButton("=",this);
        //设置按钮初始化禁止点击
        m_button->setEnabled(false);
        /*创建布局器*///自动调整每个控件的大小和位置 
        QHBoxLayout* layout = new QHBoxLayout(this);
        //按水平方向依次将控件添加到布局器中
        layout->addWidget(m_edit_X);
        layout->addWidget(m_label);
        layout->addWidget(m_edit_Y);
        layout->addWidget(m_button);
        layout->addWidget(m_edit_Z);
        //设置布局器
        setLayout(layout);
        /*连接信号和槽*/
        //左右操作数文本改变时发送信号
        connect(m_edit_X,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
        connect(m_edit_Y,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
        //点击按钮时,发送信号
        connect(m_button,SIGNAL(clicked(void)),this,SLOT(clacClicked(void)));
    }
    
    //使能等号按钮的槽函数
    void CalculatorDialog::enableButton(void){
        /*判断两个数字是否合法*/
        bool bxOK,byOK;
        m_edit_X->text().toDouble(&bxOK);
        m_edit_Y->text().toDouble(&byOK);
        //设置按钮状态(当左右均合法时则使能点击按钮)
        m_button->setEnabled(bxOK && byOK);
    }
    
    //计算和显示结果的槽函数
    void CalculatorDialog::clacClicked(void){
        //加法计算
        double res = m_edit_X->text().toDouble() + m_edit_Y->text().toDouble();
        //转换数据类型
        QString str = QString::number(res);
        //设置使其显示结果
        m_edit_Z->setText(str);
    }
    • 主函数 main.cpp
    #include 
    #include "CalculatorDialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc,argv);
        CalculatorDialog calc;
        calc.resize(160,60);
        calc.show();
    
        return app.exec();
    }

    运行结果请查看./P2/AdditionCalculator/中的内容

  • 源码案例: 获取当前系统时间

    设计要求:

    1. 使用面向对象的QT编程技术创建获取时间窗口
    2. 点击按钮,通过自定义槽函数获取并显示时间到标签

    源码:

    • 头文件 TimeDialog.h
    #ifndef __TIMEDIALOG_H
    #define __TIMEDIALOG_H
    
    #include 
    #include 
    #include 
    #include  //垂直布局器
    #include  //时间
    
    class TimeDialog: public QDialog{
        Q_OBJECT //MOC
    
    public slots:
        //获取系统时间的槽函数
        void getTime(void);
    
    public:
        //构造函数
        TimeDialog(void);
    
    private:
        QLabel* m_label; //显示时间标签
        QPushButton* m_button; //获取时间按钮
    };
    
    #endif // __TIMEDIALOG_H
    • 源文件 TimeDialog.cpp
    #include "TimeDialog.h"
    #include 
    //构造函数
    TimeDialog::TimeDialog(void){
        /*初始化界面*/
        m_label = new QLabel("时间尚未获取",this);
        //设置标签边框为凹陷面板样式
        m_label->setFrameStyle(QFrame::Panel|QFrame::Sunken);
        //设置居中
        m_label->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        //设置label的字体大小
        QFont font;
        font.setPointSize(20);
        m_label->setFont(font);
        //创建获取系统时间的按钮
        m_button = new QPushButton("点我获取时间",this);
        m_button->setFont(font);
        /*垂直布局器布局*/
        QVBoxLayout* layout = new QVBoxLayout(this);
        layout->addWidget(m_label);
        layout->addWidget(m_button);
        //设置布局器
        setLayout(layout);
        /*处理信号和槽的链接*/
        connect(m_button,SIGNAL(clicked(void)),this,SLOT(getTime(void)));
    }
    //获取系统时间的槽函数
    void TimeDialog::getTime(void){
        //获取当前的系统时间
        QTime time = QTime::currentTime();
        //将时间对象转换为字符串的形式
        QString str = time.toString("hh:mm:ss");
        //显示当前时间
        m_label->setText(str);
    }
    • 主函数 main.cpp
    #include 
    #include "TimeDialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc,argv);
    
        TimeDialog time;
        time.show();
    
        return app.exec();
    }

    运行结果请查看./P2/getTime/中的内容

  • 对上述代码进行自定义信号处理:

    源码(仅给出被更新的代码)

    • 头文件
    signals:
        //自定义信号函数,只需要声明,不能写定义
        void mySignal(const QString &);
    • 源文件
    //通过自定义信号触发label的setText槽函数的执行
        connect(this,SIGNAL(mySignal(QString)),m_label,SLOT(setText(QString)));
    //emit 标记当前是发射信号操作,本身基本没有效果
        emit mySignal(str); //发射信号

    运行结果请查看./P2/getTimeChanged/中的内容

5. QT Designer的使用

  1. 绘制界面

  2. 使用UIC进行文件转换(将.ui文件转换为C++头文件)

    手动转换命令

    uic 原文件名.ui -o 转换后的头文件名.h

    注意! 如果不进行手动转换,在Makefile时也可以自动完成

  3. 代码的使用

    • 继承

      通过继承的方式,将界面代码直接继承过来使用

      操作流程:

      1. 对ui文件进行转换,转换成C++头文件(此步骤可自动完成)
      uic CalculatorDialog.ui -o ui_CalculatorDialog.h
      1. 自行编写头文件,内部继承ui转换出的头文件
      #ifndef __CALCULATORDIALOG_H //若未定义,则定义
      #define __CALCULATORDIALOG_H
      
      #include "ui_CalculatorDialog.h"
      #include  //数字验证器
      //从Ui的命名空间内继承父窗口功能
      class CalculatorDialog : public QDialog,public Ui::CalculatorDialog
      {
          Q_OBJECT //moc元对象编译器翻译标志
      
      public:
          CalculatorDialog(void);
      
      public slots:
          //显示计算结果的槽函数
          void clacClicked(void);
          //使能等号按钮的操作数
          void enableButton(void);
      };
      
      #endif /* __CALCULATORDIALOG_H */
      1. 编写源代码cpp文件,内部调用setupui函数进行界面初始化
      #include "CalculatorDialog.h"
      
      //构造函数
      CalculatorDialog::CalculatorDialog(void){
          /*界面初始化*/
          setupUi(this);
          //设置数字验证器,使其只能输入数字形式的内容
          m_edit_X->setValidator(new QDoubleValidator(this));
          //设置数字验证器
          m_edit_Y->setValidator(new QDoubleValidator(this));
          /*连接信号和槽*/
          //左右操作数文本改变时发送信号
          connect(m_edit_X,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
          connect(m_edit_Y,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
          //点击按钮时,发送信号
          connect(m_button,SIGNAL(clicked(void)),this,SLOT(clacClicked(void)));
      }
      
      //使能等号按钮的槽函数
      void CalculatorDialog::enableButton(void){
          /*判断两个数字是否合法*/
          bool bxOK,byOK;
          m_edit_X->text().toDouble(&bxOK);
          m_edit_Y->text().toDouble(&byOK);
          //设置按钮状态(当左右均合法时则使能点击按钮)
          m_button->setEnabled(bxOK && byOK);
      }
      
      //计算和显示结果的槽函数
      void CalculatorDialog::clacClicked(void){
          //加法计算
          double res = m_edit_X->text().toDouble() + m_edit_Y->text().toDouble();
          //转换数据类型
          QString str = QString::number(res);
          //设置使其显示结果
          m_edit_Z->setText(str);
      }
      1. 主函数
      #include 
      #include "CalculatorDialog.h"
      
      int main(int argc, char *argv[])
      {
          QApplication app(argc,argv);
          CalculatorDialog calc;
          calc.show();
      
          return app.exec();
      }

      继承实现源码请查看./P3/CalculatorInheritance/中的内容

    • 组合

      通过组合方式,添加一个界面类的成员变量(ui),再通过该成员访问界面相关的代码(推荐)

      操作流程:

      1. 对ui文件进行转换,转换成C++头文件(此步骤支持自动化完成)
      uic CalculatorDialog.ui -o ui_CalculatorDialog.h
      1. 自行编写头文件,内部创建ui对象指针以此来访问ui'内的代码
      #ifndef __CALCULATORDIALOG_H //若未定义,则定义
      #define __CALCULATORDIALOG_H
      
      #include "ui_CalculatorDialog.h"
      #include  //数字验证器
      
      class CalculatorDialog : public QDialog
      {
          Q_OBJECT //moc元对象编译器翻译标志
      
      public:
          CalculatorDialog(void);
          //添加析构函数,用于回收被实例化的ui
          ~CalculatorDialog(void);
      public slots:
          //显示计算结果的槽函数
          void clacClicked(void);
          //使能等号按钮的操作数
          void enableButton(void);
      
      private:
          //通过ui->来访问和界面相关的代码
          Ui::CalculatorDialog*ui;
      };
      
      #endif /* __CALCULATORDIALOG_H */
      1. 编写源代码cpp文件,构造函数时将ui在堆内存实例化,以此完成组合构造,后续代码均使用ui来访问界面控制代码
      #include "CalculatorDialog.h"
      
      //构造函数,创建堆内存对象用其对象的形式对界面进行初始化,并以后使用ui来访问界面控制代码
      CalculatorDialog::CalculatorDialog(void):ui(new Ui::CalculatorDialog){
          /*界面初始化*/
          ui->setupUi(this);
          //设置数字验证器,使其只能输入数字形式的内容
          ui->m_edit_X->setValidator(new QDoubleValidator(this));
          //设置数字验证器
          ui->m_edit_Y->setValidator(new QDoubleValidator(this));
          /*连接信号和槽*/
          //左右操作数文本改变时发送信号
          connect(ui->m_edit_X,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
          connect(ui->m_edit_Y,SIGNAL(textChanged(QString)),this,SLOT(enableButton(void)));
          //点击按钮时,发送信号
          connect(ui->m_button,SIGNAL(clicked(void)),this,SLOT(clacClicked(void)));
      }
      
      //析构函数,在窗口退出时执行delect操作清除ui对象避免内存泄漏
      CalculatorDialog::~CalculatorDialog(void){
          delete ui;
      }
      
      //使能等号按钮的槽函数
      void CalculatorDialog::enableButton(void){
          /*判断两个数字是否合法*/
          bool bxOK,byOK;
          ui->m_edit_X->text().toDouble(&bxOK);
          ui->m_edit_Y->text().toDouble(&byOK);
          //设置按钮状态(当左右均合法时则使能点击按钮)
          ui->m_button->setEnabled(bxOK && byOK);
      }
      
      //计算和显示结果的槽函数
      void CalculatorDialog::clacClicked(void){
          //加法计算
          double res = ui->m_edit_X->text().toDouble() + ui->m_edit_Y->text().toDouble();
          //转换数据类型
          QString str = QString::number(res);
          //设置使其显示结果
          ui->m_edit_Z->setText(str);
      }
      1. 主函数
      #include 
      #include "CalculatorDialog.h"
      
      int main(int argc, char *argv[])
      {
          QApplication app(argc,argv);
          CalculatorDialog calc;
          calc.show();
      
          return app.exec();
      }

      组合实现源码请查看./P3/CalculatorCombination/中的内容

  4. 源码案例: 设计登录对话框

    设计要求:

    1. 点击OK按钮判断输入的用户名和密码是否正确,并分别弹出登录成功和登录失败的对话框
    2. 点击Cancel按钮退出登录操作

    源码:

    1. 转换ui文件后的C++头文件

    2. 头文件

      #ifndef __LOGINDIALOG_H
      #define __LOGINDIALOG_H
      
      #include "ui_LoginDialog.h"
      #include  //消息提示框
      #include  //打印提示
      
      class LoginDialog:public QDialog{
        Q_OBJECT
      public:
        LoginDialog(void);
        ~LoginDialog(void);
      
      public slots:
        //处理OK按钮的槽函数
        void onAccepted(void);
        //处理Cancel按钮的函数
        void onRejected(void);
      
      private:
        Ui::LoginDialog* ui;
      };
      
      #endif //__LOGINDIALOG_H
    3. cpp源文件

      #include "LoginDialog.h"
      
      //构造函数
      LoginDialog::LoginDialog(void):ui(new Ui::LoginDialog){
        /*界面初始化*/
        ui->setupUi(this);
        /*信号和槽的连接*/
        //点击确定发送信号
        connect(ui->m_buttonBox,SIGNAL(accepted(void)),this,SLOT(onAccepted(void)));
        //点击取消发送信号
        connect(ui->m_buttonBox,SIGNAL(onRejected(void)),this,SLOT(onRejected(void)));
      }
      
      //析构函数
      LoginDialog::~LoginDialog(void){
        delete ui;
      }
      
      //处理OK按钮的槽函数
      void LoginDialog::onAccepted(void){
        //判断是否输入正确
        if(ui->m_usernameEdit->text() == "stvsl" && ui->m_passwdEdit->text() == "stvsl2060"){
            qDebug("登录成功");
            //创建登录成功消息提示框
            /*QMessageBox参数:图标,窗口标题,,提示消息,显示按钮,父窗口*/
            QMessageBox msg(QMessageBox::about,"Success","登录成功!",QMessageBox::Ok,this);
            close();
            //显示消息提示框并进入消息循环
            msg.exec();
        }else{
            qDebug("登录失败");
            //创建登录失败消息提示框
            QMessageBox msg(QMessageBox::Critical,"Error","用户名或密码错误!",QMessageBox::Ok,this);
            msg.exec();
        }
      }
      
      //处理取消按钮的槽函数
      void LoginDialog::onRejected(void){
        //点击Yes和No的事件效果不同!!!
        QMessageBox msg(QMessageBox::Question,"登录","是否确定取消?",QMessageBox::Yes|QMessageBox::No);
        if (msg.exec() == QMessageBox::Yes){
            close();
        }
      }
    4. 主函数

      #include 
      #include "LoginDialog.h"
      
      int main(int argc, char *argv[])
      {
        QApplication app(argc,argv);
        LoginDialog login;
        login.show();
        return app.exec();
      }

    运行结果请查看./P3/Login/中的内容

6. QT Creator的使用注意事项

  1. 尽可能的依赖IDE的自动补全功能来完成代码编写
  2. ->可以直接使用.来进行快速输入
  3. QT Creator默认开启影子构建功能,用来隔离构建代码和源代码
  4. 项目DEMO请自行查看./P3/CalculatorIDE/中的内容
  5. 槽自动链接:QTCreator内的设计器支持槽自动链接来避免手动编写链接函数, 此功能将会在ui.h文件中添加自动链接函数,此函数将递归检查相匹配的槽函数并自动完成链接

7. QT的事件处理机制

  1. 什么是事件

    在QT中,程序事件驱动UI工具集,包括信号和槽都依赖事件处理机制

    事件通常是由窗口系统或者QT自身产生,用来响应发生的各类程序事件,事件的一般运行流程为:控件接收到事件后发出信号,传到槽内执行槽函数

    在QT中,事件被封装成对象,所有的事件类型都继承自抽象类QEvent

  2. 如何处理事件

    当事件发生时,首先被调用的是QObject类中的虚函数event(),她的参数(QEvent)标识了具体的事件类型

    在QTWidgets桌面应用的开发中,QWidget类覆盖了基类中的evevt()虚函数,并根据具体事件调用具体的事件处理函数,以下为部分函数

    void QWidget::mousePressEvent(QMouseEvent *event)        //鼠标按下事件
    void QWidget::mouseReleaseEvent(QMouseEvent *event)      //鼠标抬起事件
    void QWidget::mouseMoveEvent(QMouseEvent *event)     //鼠标移动事件
    void QWidget::paintEvent(QPaintEvent *event)         //绘图事件
    void QWidget::mouseDoubleClickEvent(QMouseEvent *event)  //鼠标双击事件

    所有的事件处理函数都是虚函数,可以被QWidget的子类覆盖,以此来提供针对不同窗口控件类型的事件处理,控件的使用者所关心的是定义什么样的槽处理什么样的信号,而控件的实现者则更关心覆盖哪些事件处理函数

    若希望在窗口中自定义的处理事件,可以继承QWidget或者它的子类,在自定义的窗口子类中重写事件处理函数,当相应事件被出发时,会利用多态的语法机制,所执行的事件处理函数将是子类中重写的版本,从而实现想要的事件处理结果

  3. 绘图事件

    通过绘图事件,可以自定义的图像绘制,当有以下情况发生时,将被触发,进而调用paintEvent函数,以下是绘图事件

    1. 窗口被创建后第一次显示出来
    2. 窗口由隐藏状态转变为可见状态
    3. 窗口最小化状态变为正常或最大化状态
    4. 窗口因尺寸变化需要显示更多内容
    5. update()或者repaint()函数被调用

    如希望在窗口中显示某个图像,在QWidget窗口子类中可以重写绘图事件函数paintEvent,在其中可以使用QPainter(QT二维图形引擎)实现指定的图像绘制和渲染操作

    源码案例:图片浏览器

    1. 资源自动编译成C++源代码

    2. 源码:

      1. 头文件

        #ifndef PICTUREVIEWERDIALOG_H
        #define PICTUREVIEWERDIALOG_H
        
        #include 
        #include //画家类
        #include  //图片资源处理
        
        QT_BEGIN_NAMESPACE
        namespace Ui { class PictureViewerDialog; }
        QT_END_NAMESPACE
        
        class PictureViewerDialog : public QDialog
        {
         Q_OBJECT
        
        public:
         PictureViewerDialog(QWidget *parent = nullptr);
         ~PictureViewerDialog();
        
        private slots:
        
         void on_m_Button_last_clicked();
        
         void on_m_button_next_clicked();
        
        private:
         //绘图事件处理函数(虚函数)
         void paintEvent(QPaintEvent *);
        
        private:
         Ui::PictureViewerDialog *ui;
         int m_index;    //图片索引
        };
        #endif // PICTUREVIEWERDIALOG_H
      2. 源码文件

        #include "pictureviewerdialog.h"
        #include "ui_pictureviewerdialog.h"
        #include 
        
        PictureViewerDialog::PictureViewerDialog(QWidget *parent)
         : QDialog(parent)
         , ui(new Ui::PictureViewerDialog)
        {
         ui->setupUi(this);
         m_index = 1;
        }
        
        PictureViewerDialog::~PictureViewerDialog()
        {
         delete ui;
        }
        
        //自动生成和上一张按钮相关的槽函数
        void PictureViewerDialog::on_m_Button_last_clicked()
        {
         if(m_index > 1){
             m_index--;
             //触发绘图事件
             update();
         }
        
        }
        
        //自动生成和下一张按钮相关的槽函数
        void PictureViewerDialog::on_m_button_next_clicked()
        {
         if(m_index < 8){
             m_index++;
             update();
         }
        }
        
        //绘图事件处理函数(虚函数)
        void PictureViewerDialog::paintEvent(QPaintEvent *)
        {
         qDebug("PaintEvent");
         //创建画家对象
         QPainter painter(this);
         //获取绘图所在矩形区域
         QRect rect = ui->frame->frameRect();
         qDebug() << "平移前" << rect;
         //坐标值平移
         rect.translate(ui->frame->pos());
         qDebug() << "平移前" << rect;
         //构建图形对象
         QImage image(":/new/prefix1/Imagine/"+QString::number(m_index)+".jpg");
         //使用painter将图片画到rect
         painter.drawImage(rect,image);
        }

      运行结果请查看./P4/PictureView中的内容

  4. 定时器事件

    定时器事件常用于与定时更新和动态刷新有关的功能实现,QT通过两套机制为程序提供计时功能

    • 定时器事件
    • 定时器信号

    通过定时器事件实现定时器

    int QObject::startTimer(int interval);   //启动定时器,每隔interval毫秒触发一次定时事件,并返回计时器ID
    void QObject::timeEvent(QTimerEvent*);   //定时器事件处理函数(虚函数)
    void QObject::killTimer(int id);     //关闭指定ID的定时器

    源码案例:摇奖机

    1. 图片采用动态加载的方式载入

    2. 源码

      1. 头文件

        #ifndef LOTTERYMACHINEDIALOG_H
        #define LOTTERYMACHINEDIALOG_H
        
        #include 
        #include    //定时器
        #include  //绘图
        #include      //文件路径处理
        #include   //向量容器
        #include    //图片处理
        #include 
        #include 
        
        QT_BEGIN_NAMESPACE
        namespace Ui { class LotteryMachineDialog; }
        QT_END_NAMESPACE
        
        class LotteryMachineDialog : public QDialog
        {
         Q_OBJECT
        
        public:
         LotteryMachineDialog(QWidget *parent = nullptr);
         ~LotteryMachineDialog();
        
        private slots:
         void on_m_Button_clicked();
         void onTimeOut(void);
        
        private:
         //加载图片容器
         void loadPhotos(const QString& path);
         //定时器
         QTimer* m_timer;
         //绘图事件
         void paintEvent(QPaintEvent* );
        
        private:
         Ui::LotteryMachineDialog *ui;
         //保存图片的容器(泛型)S
         QVector m_photos;
         //作为索引
         int m_index;
         //系统状态描述
         bool isStarted;
        };
        #endif // LOTTERYMACHINEDIALOG_H
      2. 源码文件

        #include "lotterymachinedialog.h"
        #include "ui_lotterymachinedialog.h"
        
        LotteryMachineDialog::LotteryMachineDialog(QWidget *parent)
         : QDialog(parent)
         , ui(new Ui::LotteryMachineDialog)
        {
         ui->setupUi(this);
         m_index = 0;
         isStarted = false;
         //加载图片到容器
         loadPhotos("./Imagine");
         qDebug() << "加载到的图片个数:" << m_photos.size();
         //创建定时器
         m_timer = new QTimer(this);
         m_timer->stop();
         m_timer->setInterval(50);
         connect(m_timer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
        }
        
        LotteryMachineDialog::~LotteryMachineDialog()
        {
         delete ui;
        }
        
        void LotteryMachineDialog::on_m_Button_clicked()
        {
         if(isStarted == false){
             isStarted = true;
             m_timer->start();
             ui->m_Button->setText("STOP!!!");
         }else{
             isStarted = false;
             m_timer->stop();
             ui->m_Button->setText("START!!!");
         }
        
        }
        
        //加载图片容器
        void LotteryMachineDialog::loadPhotos(const QString& path)
        {
         QDir dir(path);
         //遍历当前目录下图片
         //递归遍历子目录
         QStringList list1 = dir.entryList(QDir::Files);
         for (int i = 0;i < list1.size();i++) {
             QImage image(path+"/"+list1.at(i));
             m_photos << image;
         }
         //遍历子目录中的图片
         QStringList list2 = dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot);
         for (int i = 0;i < list2.size();i++) {
             loadPhotos(path + "/" + list2.at(i));
         }
        }
        
        //定时器事件处理
        void LotteryMachineDialog::onTimeOut()
        {
         //随机的获取图片索引
         m_index = QRandomGenerator::global()->bounded(1,m_photos.size());
         update();
         qDebug("TimerDone Once!");
        }
        
        //绘图事件
        void LotteryMachineDialog::paintEvent(QPaintEvent* )
        {
         QPainter painter(this);
         QRect rect = ui->frame->frameRect();
         rect.translate(ui->frame->pos());
         //随机绘制
         painter.drawImage(rect,m_photos[m_index]);
        }

    运行结果请查看.P4/PictureView/中的文件

  5. 鼠标和键盘事件

  6. 鼠标事件

    鼠标事件类型

    virtual void mousePressEvent (QMouseEvent* event);       //鼠标按下
    virtual void mouseReleaseEvent (QMouseEvent* event); //鼠标抬起
    virtual void mouseDoubleEvent (QMouseEvent* event);      //鼠标点击
    virtual void mouseMoveEvent (QMouseEvent* event);        //鼠标移动
  7. 源码案例:让鼠标能用左键实现拖拽label方块移动

    源码:

    1. 头文件

      #ifndef MOUSEDIALOG_H
      #define MOUSEDIALOG_H
      
      #include 
      #include 
      #include 
      
      QT_BEGIN_NAMESPACE
      namespace Ui { class MouseDialog; }
      QT_END_NAMESPACE
      
      class MouseDialog : public QDialog
      {
        Q_OBJECT
      
      public:
        MouseDialog(QWidget *parent = nullptr);
        ~MouseDialog();
      
      private:
        //鼠标按下
        void mousePressEvent(QMouseEvent* );
        //鼠标抬起
        void mouseReleaseEvent(QMouseEvent* );
        //鼠标移动
        void mouseMoveEvent(QMouseEvent* );
      
      private:
        Ui::MouseDialog *ui;
        //描述是否是鼠标左键
        bool m_drag;
        QPoint m_pos;
      };
      #endif // MOUSEDIALOG_H
    2. 源码文件

      #include "mousedialog.h"
      #include "ui_mousedialog.h"
      
      MouseDialog::MouseDialog(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::MouseDialog)
      {
        ui->setupUi(this);
      }
      
      MouseDialog::~MouseDialog()
      {
        delete ui;
      }
      
      //鼠标按下
      void MouseDialog::mousePressEvent(QMouseEvent* event)
      {
        //判断是否为鼠标左键
        if(event->button() == Qt::LeftButton){
            //获取label所在区域
            QRect rect = ui->label->frameRect();
            //调整坐标
            rect.translate(ui->label->pos());
            //判断鼠标位置是否是在rect范围内
            if(rect.contains(event->pos())){
                m_drag = true;
                //获取鼠标与label之间的距离
                m_pos = ui->label->pos() - event->pos();
            }
        }
      }
      
      //鼠标抬起
      void MouseDialog:: mouseReleaseEvent(QMouseEvent* event)
      {
        if(event->button() == Qt::LeftButton){
            m_drag = false;
        }
      }
      
      //鼠标移动
      void MouseDialog:: mouseMoveEvent(QMouseEvent* event)
      {
        if(m_drag){
            //计算label要移动的新位置
            QPoint newPos = event->pos() + m_pos;
            //获取父窗口大小
            QSize s1 = size();
            //获取label的大小
            QSize s2 = ui->label->size();
            //边界校验
            if(newPos.x() < 0){
                newPos.setX(0);
            }else if(newPos.x() > s1.width() -s2.width()){
                newPos.setX(s1.width() - s2.width());
            }
            if(newPos.y() < 0){
                newPos.setY(0);
            }else if(newPos.y() > s1.height() - s2.height()){
                newPos.setY(s1.height() - s2.height());
            }
            //移动到新位置
            ui->label->move(newPos);
        }
      }

      运行结果请查看.P4/MouseEvent/中的文件

  8. 键盘事件

    键盘事件类型

    virtual void keyPressEvent(QKeyEvent* event);
    virtual void keyReleaseEvent(QKeyEvent* event);

    源码案例:通过键盘方向键控制label的移动

    源码:

    1. 头文件

      #ifndef KEYBOARDDIALOG_H
      #define KEYBOARDDIALOG_H
      
      #include 
      #include 
      
      QT_BEGIN_NAMESPACE
      namespace Ui { class keyboardDialog; }
      QT_END_NAMESPACE
      
      class keyboardDialog : public QDialog
      {
        Q_OBJECT
      
      public:
        keyboardDialog(QWidget *parent = nullptr);
        ~keyboardDialog();
        //重写键盘按下事件
        void keyPressEvent(QKeyEvent*);
      
      private:
        Ui::keyboardDialog *ui;
      
      };
      #endif // KEYBOARDDIALOG_H
    2. 源码文件

      #ifndef KEYBOARDDIALOG_H
      #define KEYBOARDDIALOG_H
      
      #include 
      #include 
      
      QT_BEGIN_NAMESPACE
      namespace Ui { class keyboardDialog; }
      QT_END_NAMESPACE
      
      class keyboardDialog : public QDialog
      {
        Q_OBJECT
      
      public:
        keyboardDialog(QWidget *parent = nullptr);
        ~keyboardDialog();
        //重写键盘按下事件
        void keyPressEvent(QKeyEvent*);
      
      private:
        Ui::keyboardDialog *ui;
      
      };
      #endif // KEYBOARDDIALOG_H

    运行结果请查看./P4/Keyboard/中的文件

8. 数据库

  1. 数据库操作语言(SQL)

    SQL是一种针对数据库的结构化查询语言,用于实现数据库查询和程序设计,常用于关系型数据库系统,以此来实现数据的存取,查询和更新等操作

    常用的SQL操作关键字

    关键字 功能
    SELECT 查询
    INSERT 插入
    DELETE 删除
    UPDATE 修改更新

    常用的数据库定义语言

    关键字 功能
    CREATE TABLE 创建一张表
  2. SQLite数据库

    SQLite 是一个软件库,实现了自成一体的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是世界上最广泛部署的 SQL 数据库引擎。SQLite 的源代码位于公有领域

    1. 安装SQLite

      sudo pacman -S sqlite
  3. MariaDB数据库

    1. 安装与配置

      sudo pacman -S mariadb                                                            #安装MariaDB
      sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql  #链接文件
      sudo systemctl start mariadb                                                  #启动数据库
      sudo systemctl enable mariadb                                                 #设置开机自启动
      sudo mysql_secure_installation                                                    #开启安装程序
      sudo mysql                                                                        #进入数据库控制台
      set password for root@localhost = password('新密码');                             #设置新密码
      exit;                                                                         #退出
  4. 数据库指令

    sqlite指令:

    1. 常用自身指令
      指令 功能
      .help 显示相关帮助信息
      .database 显示当前数据库目录
      .open 打开某个数据库
      .table 显示数据表名
      .schema 查看数据表创建时的详细信息
      .mode 设置显示模式
      .nullvalue 设置空白字段显示的字符串
      .header on/off 显示/关闭数据表表头
      .exit 退出
    2. 常用sql指令(DML)
      指令 功能
      CREATE TABLE 表名(列名1 类型 [约束],列名2 类型 [约束].......); 创建数据表
      DROP TABLE 表名; 删除表
      INSERT INTO 表名(列名1,列名2,......) VALUES(数值1,数值2,......); 向表中添加数据
      INSERT INTO 表名 VALUES(数值1,数值2,......); 缺省插入数据
      DELETE FROM 表名 WHERE 条件表达式(支持and/or); 删除从表中数据
      DELETE FROM 表名; 删除表中所有数据
      UPDATE 表名 SET 列名1=数值,列名2= 数值,..... WHERE 条件表达式; 修改数据
      SELECT 列名1,列名2,...... FROM 表名; 查询数据
      SELECT 列名1,列名2,...... FROM 表名 WHERE 条件表达式 ORDER BY 列名 排序方式; 条件查询数据
      排序升序排序:ASC
      降序排序:DESC
      SELECT 列名1,列名2,.... FROM 表名 WHERE 列名 LIKE 模糊匹配条件 模糊查询
      模糊匹配通配符:
      % 表示未知个数字或字符
      __ 表示单一数字或字符
    3. sql数据类型
      关键字 数据类型
      INT 整型
      TEXT 字符串
      REAL 浮点
    4. 常用约束
      关键字 约束类型
      PRIMARY KEY 主键(表示该列数据唯一,可加快访问速度)
      NOT NULL 非空

    推荐使用DBeaver来进行数据库操作来尽量减少直接命令操作数据库的次数

  5. QT中使用数据库

    QT中使用QSqlDataBase来建立程序与数据库的连接,需要在工程文件中添加QT += sql

    数据库连接:

    //   添加数据库驱动
    db = QSqlDatabase::addDatabase("QSQLITE");
    //设置主机名
    db.setHostName();
    //设置数据库文件名
    db.setDatabaseName();
    //设置用户名
    db.setUserName();
    //设置密码
    db.setPassword();
    //设置端口号
    db.setPort();
    //设置连接选项
    db.setConnectOptions();
    //打开数据库(会返回一个布尔值表示数据库状态)
    db.open();

    数据库执行

    QSqlQuery query;
    query.exec("SELECT,DELETE,INSERT,UPDATE等SQL语句");
    //以上可写成
    QSqlQuery query ("SELECT,DELETE,INSERT,UPDATE等SQL语句");

    获取结果集

    QString str = QString("SELECT 列名 FROM 表名");
    QSqlQueryModel* model = new QStringQueryModel;
    model->setQuery(str);//执行查询操作,并将结果保存到model对象
    ui->menuTableView->setModel(model)//显示查询结果
  6. 源码案例:学生成绩管理系统

    1. 头文件

      #ifndef STUDENTPERFORMANCEMANAGESYSTEMDIALOG_H
      #define STUDENTPERFORMANCEMANAGESYSTEMDIALOG_H
      
      #include 
      #include 
      #include 
      #include 
      #include     //获取数据库访问失败原因
      #include 
      
      QT_BEGIN_NAMESPACE
      namespace Ui { class StudentPerformanceManageSystemDialog; }
      QT_END_NAMESPACE
      
      class StudentPerformanceManageSystemDialog : public QDialog
      {
        Q_OBJECT
      
      public:
        StudentPerformanceManageSystemDialog(QWidget *parent = nullptr);
        ~StudentPerformanceManageSystemDialog();
      
      private:
        void crateDB();     //完成数据库创建
        void crateTable();  //完成数据表的创建
        void queryTable();  //完成查询操作
      
      private slots:
      
        void on_m_sadd_clicked();
      
        void on_m_sdelete_clicked();
      
        void on_m_supdate_clicked();
      
        void on_m_start_clicked();
      
      private:
        Ui::StudentPerformanceManageSystemDialog *ui;
        QSqlDatabase db;         //建立QT和数据库的连接
        QSqlQueryModel modle;    //保存结果集
      
      };
      #endif // STUDENTPERFORMANCEMANAGESYSTEMDIALOG_H
    2. 源文件

      运行结果请查看.P4/MouseEvent/中的文件#include "studentperformancemanagesystemdialog.h"
      #include "ui_studentperformancemanagesystemdialog.h"
      
      StudentPerformanceManageSystemDialog::StudentPerformanceManageSystemDialog(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::StudentPerformanceManageSystemDialog)
      {
        ui->setupUi(this);
        crateDB();
        crateTable();
        queryTable();
      }
      
      StudentPerformanceManageSystemDialog::~StudentPerformanceManageSystemDialog()
      {
        delete ui;
      }
      
      //完成数据库创建
      void StudentPerformanceManageSystemDialog::crateDB()
      {
        //添加数据库驱动
        db = QSqlDatabase::addDatabase("QMYSQL");
        //设置数据库连接
        db.setUserName("root");
        db.setPassword("2060");
        db.setPort(3306);
        db.setDatabaseName("student");
        //尝试打开数据库
        if (db.open()){
            qDebug("数据库连接成功!");
        }else{
            qDebug() << "数据库连接失败";
        }
      }
      
      //完成数据表的创建
      void StudentPerformanceManageSystemDialog::crateTable()
      {
        QSqlQuery query;
        QString str = QString("CREATE TABLE STUDENT ("
                              "id INT PRIMARY KEY NOT NULL,"
                              "name TEXT NOT NULL,"
                              "grade REAL NOT NULL,"
                              "class TEXT NOT NULL)");
        if(query.exec(str)){
            qDebug() << "未发现数据库表,新的数据库表已创建";
          }else{
            qDebug() << "数据库已存在" << str;
          }
      }
      
      //完成查询操作
      void StudentPerformanceManageSystemDialog::queryTable()
      {
      QString str = QString("SELECT * FROM STUDENT");
      modle.setQuery(str);
      ui->tableView->setModel(&modle);
      }
      
      //添加操作
      void StudentPerformanceManageSystemDialog::on_m_sadd_clicked()
      {
      QSqlQuery query;
      int id = ui->m_snumber->text().toInt();
      QString name = ui->m_sname->text();
      double grade = ui->m_sgrade->text().toDouble();
      QString classs = ui->m_sclass->text();
      QString str = QString("INSERT INTO STUDENT VALUES (%1,'%2',%3,'%4')").arg(id).arg(name).arg(grade).arg(classs);
      if(query.exec(str)){
          qDebug() << "数据添加成功";
          queryTable();
        }else{
          qDebug() << str;
        }
      }
      
      //删除操作
      void StudentPerformanceManageSystemDialog::on_m_sdelete_clicked()
      {
      QSqlQuery query;
      int id = ui->m_snumber->text().toInt();
      QString str = QString("DELETE FROM STUDENT WHERE id = %1").arg(id);
      if(query.exec(str)){
          qDebug() << "数据删除成功";
          queryTable();
        }else{
          qDebug() << "删除失败" << str;
        }
      }
      
      //修改操作
      void StudentPerformanceManageSystemDialog::on_m_supdate_clicked()
      {
      QSqlQuery query;
      int id = ui->m_snumber->text().toInt();
      double grade = ui->m_sgrade->text().toDouble();
      QString classs = ui->m_sclass->text();
      QString str = QString("UPDATE STUDENT SET grade=%1,class='%2' WHERE id = %3").arg(grade).arg(classs).arg(id);
      if(query.exec(str)){
          qDebug() << "数据更新成功";
          queryTable();
        }else{
          qDebug() << "数据更新失败" <m_svaluemodel->currentIndex() == 0){
          value = "id";
        }else{
          value = "grade";
        }
      //获取排序方式
      QString condition;
      if(ui->m_shortby->currentIndex() == 0){
          condition = "ASC";
        }else{
          condition = "DESC";
        }
      QString str = QString("SELECT * FROM STUDENT ORDER BY %1 %2").arg(value,condition);
      modle.setQuery(str);
      ui->tableView->setModel(&modle);
      }

    运行结果请查看./P5中的文件

9. 网络模块

  1. 服务器

    • QTcpServer提供了一个基于TCP的服务器,通过此类可以快速建立TCP服务器,并接受来自客户端的请求

    • 使用说明:

      QTcpServer::listen()       //设置服务器端口号,若未指定端口号则会被自动选择一个可用端口,同时,会开始监听主机指定的IP地址或自动设置为监听所有地址(QHostAddress::Any)    //表示监听IP为0.0.0.0
      //注意:端口范围是0~65535
    • 信号

      //当检测到来自客户端的连接请求,将会发送信号
      newConnection()        //可以依靠其自定义槽函数,在其中调用
      nextPendingConnection()    //获取和客户端通信的套接字
    • 代码样本

      //创建服务器对象
      QTcpServer tcpserver;
      //开启服务器,监听所有地址
      tcpServer.listen(QHostAddress::Qny,port);
      //连接槽函数
      connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
      /*响应客户端的连接请求*/
      void Server::onNewConnection(){
       QTcpSocket* tcpClientSocket = tcpServer.nextPendingConnection();
       tcpClientList.append(tcpClientSocket);
       //客户端有消息时触发readyRead()信号
       connect(tcpClientSocket,SIGNAL(redayRead()),this,SLOT(onReadyRead()));
      }
      /*接收客户端发来的消息*/
      void ServerDialog::onReadyRead(){
       //遍历检查哪个客户端有消息传来
       if(clientList.at(i)->bytesAvailable()){
           //读取消息并保存
           QByteArray buf = clientList.at(i)->readAll();
           //显示消息到界面
           ui->listWidget->addItem(buf);
           //转发消息给其它在线客户端
           sendMessage(buf);
       }
      }
  2. 客户端

    • QTcpSocket 提供了和服务器通信的tcp套接子

    • 信号

      //创建和服务器通信的套接字
      QTcpSocket tcpsocket;
      //向服务器发送连接请求
      tcpSocket.connectToHost(serverIP,Port)
      //和服务器连接时发送信号connected
      connect(&tcpSocket,SIGNAL(connected()),this,SLOT(onConnected()));
      //当收到服务器转发消息时发送readyRead信号
      connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
- THE END -
Tag:

stvsl

6月22日19:31

最后修改:2022年6月22日
7

非特殊说明,本博所有文章均为博主原创。

共有 1 条评论

  1. 1

    😶