zoukankan      html  css  js  c++  java
  • Qt入门

    安装了Qt 6.1,记录一下自己学到的内容,梳理思路。(更新中)
    因为只是自己的理解,所以可能有错误及不准确的地方,请多指正。

    一、简单的界面设计

    1.概述

    用Qt Creater创建一个项目,会生成 .pro 文件、.h 文件和 .cpp 文件。
    .pro 文件记录了该项目使用的编译器,以及包含的头文件、源代码有哪些等信息。
    .h 文件和 .cpp 文件就是面向对象设计的思路。将类的声明放在 .h 文件中,将类函数的实现放在 .cpp 文件中。
    main.cpp 会生成一个 QApplication 对象以及一个类,类呈现用户的设计,QApplication 对象控制程序的进行。

    #include "textchange2.h"
    
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        TextChange2 w;
        w.show();
        return a.exec();
    }
    

    自己设计的类可以继承 QWidget 、QMainWindow 、QDialog 三个顶层窗体之一,独立显示。
    在类里面先加入 Q_OBJECT 的宏,这样才可以使用元对象系统、信号/槽机制。类里面可以通过 "signals:" 和 "slots:" 来声明信号和槽。一般我会在这个类的生成函数里调用 iniUI(), iniSignalSlots(), iniAction() 等,把一些组件的设置、信号与槽的链接等写在这些函数里。
    如果要把某个类联系到UI界面(创建时选择了Generate Form),在对应的头文件里把该类加入 Ui 命名空间

    QT_BEGIN_NAMESPACE
    namespace Ui { class QmyWidget; }
    QT_END_NAMESPACE
    

    然后在私有成员中声明 ui 指针

    private:
        Ui::QmyWidget *ui;
    

    最后在构造函数里创建 ui 实例,接下来就可以通过 ui->... 来调用 UI 界面中的对象了。析构函数里删除 ui 指针。

    QmyWidget::QmyWidget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::QmyWidget)
    {
        ui->setupUi(this);
    }
    
    QmyWidget::~QmyWidget()
    {
        delete ui;
    }
    

    直接在 ui designer 里通过拖动组件等方式进行的设计会体现在编译后生成的 ui_***.h 文件中。

    2.信号与槽

    有三种写法,下面是例子

    void QWMainWind::iniAction()
    {
        //connect(ui->actPaste,SIGNAL(ui->actPaste->triggered()),ui->txtEdit,SLOT(ui->txtEdit->paste()));
        connect(ui->actPaste,&QAction::triggered,ui->txtEdit,&QTextEdit::paste);
        //connect(ui->actCopy,SIGNAL(triggered()),ui->txtEdit,SLOT(copy()));
        connect(ui->actCopy,&QAction::triggered,ui->txtEdit,[&](){ui->txtEdit->copy();});
        connect(ui->actCut,SIGNAL(triggered()),ui->txtEdit,SLOT(cut()));
        connect(ui->actClose,SIGNAL(triggered()),this,SLOT(close()));
        connect(ui->actClear,SIGNAL(triggered()),ui->txtEdit,SLOT(clear()));
    }
    

    如果用 SIGNAL() 和 SLOT() ,函数参数类型必须写上;如果用 &QAction::triggered 这种写法就无须写函数参数,因此有时会比较方便。还可以用 Lambda 表达式,较灵活。

    3.在无Qt环境下运行写好的程序

    https://blog.csdn.net/qq_44977889/article/details/107693034

    4.写一个项目时踩的坑与收获

    (1)指针对象初始化
    指针对象一定要记得初始化!用 new 给它们分配空间。
    不然的话可能报错 "no reference to ..." 或者闪退并提示 "the process was ended forcefully".
    没有找到指针数组的一次性初始化的方法。也许只能给里面的对象挨个初始化。

    (2)设置QPushButton背景为透明
    在QPushButton上加载了图片之后,图片未填满的地方是白色背景,很显眼。
    https://blog.csdn.net/weixin_44100850/article/details/90521859

    (3)自定义按钮类的显示
    只是自定义了一个按钮的话,它是不会显示在窗口上的。
    在作为主界面的类里将该自定义类的对象的父亲设置为 centralWidget 才行。
    比如我在自定义的类 myPos 里声明了 QPushButton 实例 button,那么我在继承了 QMainWindow 的类里面需要这样写(pos[]是myPos的实例):

    pos[cur].button = new QPushButton(centralWidget());
    

    但是这样虽然能让 QPushButton 上的图片显示出来,点击按钮这个事件仍然没有反应……
    终于发现我是在 myPos 类里声明了 QPushButton 对象,但重载函数、信号等都写在 myPos 类里面。即使 myPos 类继承的是 QPushButton ,但重载的都是 myPos 而不是自己在用的那个 QPushButton !脑袋要清楚呢!
    那应该在哪里重载这 QPushButton 呢?其实在继承 QMainWindow 的那个类里面重载即可。刚才那个语句也是把这个 button 的父亲设成了这个类。
    方法一:把定义出的这些 button 依次重载一遍。
    方法二:自定义 myPushButton.
    方法三:lambda函数!好处是可以在连接的时候直接把参数写上。

    connect(pos[cur].button,&QPushButton::clicked,this,[=](){btnPressed(cur);});
    

    (4)停靠窗口QDockWidget的显示
    在定义了自己的QDockWidget之后(假设名字为mydock),记得在继承QMainWindow的那个类里面调用addDockWidget(Qt::TopDockWidgetArea,mydock)!这里Qt::TopDockWidgetArea需要头文件<QMainWindow>,将窗口停靠在顶部。还可以用RightDockWidgetArea使其停靠在右侧之类。
    QDockWidget的大小取决于内部组件的大小。一种写法是给QDockWidget设置最大、最小高度和宽度以设置其大小,另一种(感觉更优美的)写法是定义一个QWidget(假设名字为dockContents),然后把各种组件都加入 dockContents 里面,最后 mydock->setWidget(dockContents).这样可以直接调整mydock的大小而把QDockWidget“撑”起来。
    此外要注意,setGeometry的位置是相对于自己的父亲而言的。

    (5)关于QString
    自己写了这样的初始化:

    QString name[15]={
        "Flag","Mine","Engineer","Platoon"
        ,"Company","Battalion","Head","Brigadier"
        ,"Division","Army","Commander","Bomb"
    };
    

    但是调用 name[0] 的时候会异常退出,报出 "D:SogouInputComponents" 。最后还是一个一个赋值了。仍不知为何上面那种方法不行。

        name[0]="Flag"; name[1]="Mine"; name[2]="Engineer";
        name[3]="Platoon"; name[4]="Company"; name[5]="Battalion";
        name[6]="Head"; name[7]="Brigadier"; name[8]="Division";
        name[9]="Army"; name[10]="Commander"; name[11]="Bomb";
    

    (6)用QTCPSocket读写数据的waitFor
    发现不是一次write()引起一次readyRead(). 自己把数据长度和数据本身用两个句子write(),结果对面还是一次readAll()就全读完了。
    https://blog.csdn.net/asklw/article/details/71591763
    于是开始想如何把数据长度弄成4个字节。但还是不太成功。
    最后改成按行读写。在写入的数据后面加' ',读的时候用readLine().
    记得在write()之后加一个waitForBytesWritten(),让数据写完之后再继续后面的操作。
    也要记得如果需要的话在connect()之后加一个waitForConnected()!怪不得总是显示“连接失败”弹窗,但是实际上却连接成功,原来是因为我连接了connected信号和isConnected=1操作,然后在connect()请求之后写了一个if(isConnected==1){},但是在执行这个if语句的时候可能还没来得及连上。去掉那个信号/槽,改成isConnected=...->waitForConnected(),就没问题了~
    当然,在disconnectFromHost()后面也可以写一个waitForDisconnected().

    注意,除非是要利用构造函数设定某个对象的父亲,否则尽量在声明指针的时候或在类的构造函数里就给它划好空间,否则极容易出现野指针。
    connect(...)也尽量在类的构造函数里写好,而别写在“创建服务器”函数里。否则如果创建两次的话就会出问题。

    (7)用QTcp时连接信号/槽语句的位置
    https://blog.csdn.net/qq_42784403/article/details/104349362
    原来应该在QTcpSocket被赋值为nextPendingConnection之后再连接信号/槽!
    仔细一看,nextPendingConnection()是一个 *QTcpSocket 类型的,怪不得!

    (8)QTcpSocket的readyRead()使用注意
    自己的程序将readyRead()信号与recvMessage()函数连接。在recvMessage()函数中,如果message是“点击”操作,那么就去执行点击指令。
    数据是按行读写的,每条数据后面加了一个' '。每次写完之后都会waitForByteWritten().
    但是出现问题:server端先“点击”,第一次点击的指令没有传到client端。但是client端的指令能传到server端。此后,server端每一次“点击”,传到client端的都恰是其上一次点击的数据。
    现在的猜想是当readyRead()信号发出的时候,自己的程序可能还在执行“startNewRound()”之类的函数,所以无法开始执行“recvMessage()”.
    在recvMessage()里面写上:

    while(readWriteSocket->bytesAvailable())
    {
    ...
    }
    

    就没有再发生那种问题了!
    这篇博客把QTcpSocket的收发机制解释得很清楚!
    https://blog.csdn.net/dengdew/article/details/79065608
    QTcpSocket的flush()函数可以将缓冲区的数据立刻发送。waitForBytesWritten()则是等待bytesWritten()信号,该信号在有数据被写入缓冲区时触发。
    所以刚才那个问题,通过bytesAvailable(),而是在waitForBytesWritten()后面再写上flush(),也能解决问题!

    (9)QMessageBox阻塞线程
    用QMessageBox会阻塞线程。因为每回合都要计时,所以现在变成双方在第一回合的时刻始于各自关掉“你的颜色是……”的消息框的时刻,也就是没有同步。
    其实只要把开表的操作放到弹窗之前就行了。停表的操作也放到弹窗之前。总之就是一组操作的最后一步才是弹窗,关闭弹窗后这组操作也就结束,这样的思路比较好。

  • 相关阅读:
    py程序----两个判断回文的程序
    Python特性
    python-基本数据类型
    shell编程第一天
    iptables防火墙
    纤维参数测量
    线性代数及其应用(最小二乘、PCA、SVD)
    水流方向检测
    微信跳一跳-MATLAB实现
    相机标定opencv实现
  • 原文地址:https://www.cnblogs.com/Narh/p/15156676.html
Copyright © 2011-2022 走看看