qtcanpool 知 03:设计器pk手码

文章目录

  • 前言
  • 声音
  • 论证
  • 后语

前言

先来解释下标题:设计器pk手码……

设计器:是指Qt Designer,就是在Qt开发过程中,部分界面或全部界面采用Designer的拖拽完成,然后功能逻辑通过手敲代码完成,俗称“拖控件”,常见于MFC这类软件设计方式。
手码:就是在Qt开发过程中,不使用Designer,所有界面和功能逻辑都是手敲代码完成,手速快的,看起来比较牛掰。

解释完,突然发现,就这样结束似乎也没什么问题,上面就是二者的区别,还有啥好pk的呢?

pk之前,作者也仿照别人来一个心路历程好了。

  1. 作者以前是学C语言的,Qt是在自学嵌入式Linux时接触的。因为没学过C++,突然搞起来Qt,还是很有难度的(本来水平就菜),那时候霍亚飞还没出《Qt Creator快速入门》这本书,作者寒假中根据他的博客开始学起来的,不过也都是一知半解,能看懂但自己不会写,只会抄别人的然后修改之。
  2. 这本书后来作者也没看过,博客里面的细节也早都忘得一干二净了,但是有一点,多年之后,再接触Qt时,作者还可以通过Designer(主要根据博客中学了Designer,且以前也糊里糊涂搞过MFC)来拖出来一些类似于“Hello World”水平的界面,所有功能都在一个叫mainwindow.cpp的超级文件中完成。
  3. 工作后,再次接触Qt是看到前辈给的部分代码,老板让借鉴这个残本开发一个功能,那里面没有ui文件,我该怎么拖拽我的控件呀?当时反复折腾一个串口的属性配置界面(波特率,数据位、校验位……),通过Designer一会就拖完了,但用手写要写好久(打字又不快,控件又不熟悉,编码水平也水),手写尽管痛苦,但是作者已经开始学习怎么拆分mainwindow.cpp这个Super File了,功能最后没完成 ……
  4. 多年后,又接触了Qt,尽管水平还是依菜如故,但是,这次不再使用Designer了,已经全部通过手敲代码来完成,彻底算是抛弃Designer了。

读者看过上面的描述,可能会有疑虑:你也没怎么用过Desinger,你哪来勇气要将Designer和手码进行pk,你懂Designer的好吗?

确实,作者后来项目上再也没用过Designer,它的优美和高效作者也没有体会到,作者也怕写这篇文章会被那些Designer控喷……先不管Designer是为了高效和易学易用,还是对标其他语言的“拖控件”,作者不是要诋毁它,或者说不让人去用它,作者想将设计器和手码两种方式做一个梳理和比较,也许能给看到这篇文章的读者一些思路……

声音

关于Qt开发到底该不该用Designer,其实互联网上也有很多声音,大底如下:

  • 初学的话,还是手写代码,可以了解其中的机制,以后用Designer也会得心应手。
  • 机制是假的,能弄出来才是真。
  • 初学的话,还是用Desinger,能够快速拖拽出来,成就感满满。
  • 觉得Designer设计的又快又好,那就用它;如果觉得Designer是个累赘,还不如手写来的快,那就手码。
  • 还是自己手写代码,那个不爽。
  • Designer就是香。
  • 两者本质上是一样的,可以先用Designer设计个大概,不满意再手动修改。
  • 既然提供了Designer,说明有一定优势,多练习,慢慢体会两者的优缺点,最后达到灵活的在两者之间进行取舍和复合应用。
  • 如果用到大量布局管理器,建议手写。
  • 简单的默认样式用Designer当然快,但如果还要进行各种详细界面设置还是写代码方便。
  • 不用纠结这种问题,对于一个大型的项目,必然有很多自定义的widget,这些widget中有些纯代码方便(比如绘制全新的控件),有些Designer方便(比如组合现有的控件得到新的控件)。怎么方便怎么写就行了。
  • 如果你需要的界面控件都是相对标准,那么直接用designer;如果你需要的界面相对复杂,但主界面相对标准,用designer,个别自定义控件用手写。如果你需要的界面大框架就是自定义控件为主,那就手写。原则上能用designer就没什么必要手写,当然手写是很必要的辅助手段。所以不在于项目大小的问题,更多的在于你使用的整体界面风格。
  • 界面用Designer,逻辑手写,UI和逻辑分离。
  • 建议用designer ,当然有些情况下必须用代码。
  • 感觉直接拖会方便很多… 全自己写的话,布局感觉好麻烦,也不能马上看到效果。
  • 用代码写的,比较自由吧,缺点就是比较烦。
  • 用Designer的ui直接绘制吧,为什么要写代码呢?能省事就省吧。
  • ……

上面都是用户使用过程中体会到的两者区别,再来看看官方是怎么说的:
qtcanpool 知 03:设计器pk手码_第1张图片
特点如下:

  • 所见即所得的方式编写和定义窗口或对话框,并可以使用不同的样式和分辨率对其进行测试。
  • 使用Qt的信号和插槽机制,Designer创建的小部件和表单可以与编程代码无缝集成,因此您可以轻松地将行为分配给图形元素。
  • 在Designer中设置的所有属性都可以在代码中动态更改。
  • 可以将自定义的组件通过插件方式集成到Designer。

好了,作者试着总结一下这些声音:

  • Designer所见即所得;手码靠空间想象。
  • Designer设计标准、层级简单的界面方便;手码灵活多变,写起来麻烦。
  • Designer设计界面,手码写逻辑,UI和逻辑分离。

暂时先不论这些声音的对错,下面用一个简单的例子看看真实情况是什么样的。

论证

Designer可以设计很复杂的界面,如果一个界面元素太多,可以分拆到不同的ui文件中完成,就是不必在一个ui文件(超级文件)上吊死。
Designer中有那么多控件,一个例子要多复杂才可以把它们拖拽清楚呢?还好,这里不是讲解Designer的使用教程,只需要通过一个小小的例子来探探其中的道道即可。

例子:实现一个窗口类MainWindow,其包含两个QPushButton,一个QLabel,一个QTextBrowser和多个Spacer,两个QPushButton文本分别显示“设计器”和“手码”,用户单击哪个按钮哪方就胜利,同时会改变显示“PK”的QLabel的底色。
qtcanpool 知 03:设计器pk手码_第2张图片
主要代码如下:
qtcanpool 知 03:设计器pk手码_第3张图片

  • mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
  • mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// designer
void MainWindow::on_pushButton_clicked()
{
    ui->label->setStyleSheet(QString::fromUtf8("background-color: rgb(0, 255, 0);\n"
"color: rgb(255, 255, 255);"));
}

// manual
void MainWindow::on_pushButton_2_clicked()
{
    ui->label->setStyleSheet(QString::fromUtf8("background-color: rgb(0, 0, 255);\n"
"color: rgb(255, 255, 255);"));
}
  • ui_mainwindow.h
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.15.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
    QWidget *centralwidget;
    QVBoxLayout *verticalLayout;
    QSpacerItem *verticalSpacer_2;
    QHBoxLayout *horizontalLayout_2;
    QSpacerItem *horizontalSpacer;
    QHBoxLayout *horizontalLayout;
    QPushButton *pushButton;
    QLabel *label;
    QPushButton *pushButton_2;
    QSpacerItem *horizontalSpacer_2;
    QTextBrowser *textBrowser;
    QSpacerItem *verticalSpacer;
    QMenuBar *menubar;
    QStatusBar *statusbar;

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
        MainWindow->resize(800, 600);
        centralwidget = new QWidget(MainWindow);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        verticalLayout = new QVBoxLayout(centralwidget);
        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
        verticalSpacer_2 = new QSpacerItem(20, 149, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer_2);

        horizontalLayout_2 = new QHBoxLayout();
        horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2"));
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout_2->addItem(horizontalSpacer);

        horizontalLayout = new QHBoxLayout();
        horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
        pushButton = new QPushButton(centralwidget);
        pushButton->setObjectName(QString::fromUtf8("pushButton"));

        horizontalLayout->addWidget(pushButton);

        label = new QLabel(centralwidget);
        label->setObjectName(QString::fromUtf8("label"));
        label->setStyleSheet(QString::fromUtf8("background-color: rgb(255, 0, 0);\n"
"color: rgb(255, 255, 255);"));
        label->setAlignment(Qt::AlignCenter);

        horizontalLayout->addWidget(label);

        pushButton_2 = new QPushButton(centralwidget);
        pushButton_2->setObjectName(QString::fromUtf8("pushButton_2"));

        horizontalLayout->addWidget(pushButton_2);


        horizontalLayout_2->addLayout(horizontalLayout);

        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout_2->addItem(horizontalSpacer_2);


        verticalLayout->addLayout(horizontalLayout_2);

        textBrowser = new QTextBrowser(centralwidget);
        textBrowser->setObjectName(QString::fromUtf8("textBrowser"));

        verticalLayout->addWidget(textBrowser);

        verticalSpacer = new QSpacerItem(20, 149, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer);

        MainWindow->setCentralWidget(centralwidget);
        menubar = new QMenuBar(MainWindow);
        menubar->setObjectName(QString::fromUtf8("menubar"));
        menubar->setGeometry(QRect(0, 0, 800, 22));
        MainWindow->setMenuBar(menubar);
        statusbar = new QStatusBar(MainWindow);
        statusbar->setObjectName(QString::fromUtf8("statusbar"));
        MainWindow->setStatusBar(statusbar);

        retranslateUi(MainWindow);

        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi

    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
        pushButton->setText(QCoreApplication::translate("MainWindow", "\350\256\276\350\256\241\345\231\250", nullptr));
        label->setText(QCoreApplication::translate("MainWindow", "PK", nullptr));
        pushButton_2->setText(QCoreApplication::translate("MainWindow", "\346\211\213\347\240\201", nullptr));
        textBrowser->setHtml(QCoreApplication::translate("MainWindow", "\n"
"\n"
"

PK\345\272\225\350\211\262\350\257\264\346\230\216\357\274\232

\n"
"

\347\272\242\350\211\262\357\274\232\350\241\200\346\210\230\344\270\255

\n"
"

\347\273\277\350\211\262\357\274\232\350\256\276\350\256\241\345\231\250\350\203\234

\n"
"

"t-block-indent:0; text-indent:0px;\">\350\223\235\350\211\262\357\274\232\346\211\213\347\240\201\350\203\234

"
, nullptr)); } // retranslateUi }; namespace Ui { class MainWindow: public Ui_MainWindow {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_MAINWINDOW_H

看破

  • 看:头文件中声明了namespace Ui { class MainWindow; },并定义了私有成员变量Ui::MainWindow *ui;源文件中包含了#include "ui_mainwindow.h",这个头文件哪里来的?
    破:ui_mainwindow.h头文件是根据mainwindow.ui生成的,mainwindow.ui实际上是一个xml文件,里面定义了控件的一些信息,qmake编译过程,会调用一个uic工具,会将xxx_ui文件编译成ui_xxx.h头文件。
  • 看:头文件中有两个按钮的槽函数,除了命名奇怪外,单击按钮是怎么将信号关联到槽的呢?
    破:pushButton和pushButton_2实际上是两个按钮的对象名,可以在designer界面进行有意义的命名,此处为了演示就比较随意。至于如何关联到槽的,注意观察ui_mainwindow.h中的这句QMetaObject::connectSlotsByName(MainWindow);,根据名称连接槽,官方介绍如下:
    qtcanpool 知 03:设计器pk手码_第4张图片

效果如下:
qtcanpool 知 03:设计器pk手码_第5张图片

例子也看完了,是时候总结一下了:

  1. Designer适合初学者吗?
    作者认为是适合的,你看Designer的界面,左侧是基础的控件集合,上侧是信号、布局等操作,右上侧是整个界面元素树,右下侧是不同控件的属性,中心是设计区……右键中心区中的控件,还可以快速设置样式、关联槽、提升(promote)类等,这多适合初学者全面快速了解Qt Widgets的类呀!
  2. Designer高效吗?
    中心区在布局的时候,所见即所得,很爽,即便有时需要打破布局重新布,也是动动鼠标分分钟解决,但如果元素特别多,会稍微麻烦点,有时候可能牵一发而动全身。
  3. Designer真的做到UI和逻辑分离了吗?
    我们看到ui_xxx.h头文件中,Ui类中定义了一堆开放的成员变量,然后在setupUi接口中进行创建,只要Designer支持控件的操作,都可以在setupUi中自动生成,但对于那些自己设计的类呢?那些非标准的接口怎么设置?只能在ui文件之外单独动态的设置,这相当于初始化被强行分在了不同地方,实际上破坏了整体,而且手码就不能做到逻辑分离吗?手码一切!!况且这里都是属于界面,有什么要分离的呢。
  4. Designer中控件命名问题
    如果不对这些控件进行有意义的命名,在ui外部获取成员变量的时候会很头疼,常常记不住名字,通过图形化的方式来命名不一定比手码快。控件命名和对象名被强绑成一样的,不知道是不是可以解绑。
  5. Designer的ui文件不利于阅读
    ui文件是xml文件,不利于阅读,需要先编译生成ui_xxx.h头文件才行,不过拿来先编译,似乎是必须的流程,无图无真相,谁上来还看代码。
  6. Designer的ui文件编辑受限制
    ui文件需要依赖Designer来编辑,对于一些环境上没有Designer的比较费事,当然你如果不装Designer的话,可以直接修改ui文件(靠本事吃饭),毕竟它本质是xml文件。
  7. Designer的模块化设计怎么样?
    通常开发过程中,一个界面布局可能由多个部分组成,每个部分都可以设计成一个类,模块化设计,维护和复用等都方便。采用ui文件进行设计,如果把所有元素都布局在一起,当需要复用某一部分的时候,就有点不方便了,如果也采用模块化设计,可能需要提升类或多ui文件的方式……设计自己的类是常有的事,用提升来兼容Designer是不是显得多余……
  8. ……

上面总结的,是作者个人以为的,难免有的观点会片面,因为作者确实对Designer不是特别熟。

后语

为什么有人会觉得只会“拖控件”的开发是比较Low的?如果你只会拖控件,可能你没有参与过稍大点的项目,可能你不经常设计自己的类,可能你编码能力偏弱,可能你只是初学者,可能你很菜……

个人觉得,要尽快摆脱“拖控件”的束缚,你看那qt-creator的代码,你看那QtitanRibbon的代码,你看那qtcanpool的代码,基本上没有ui文件,所以,你懂得……

你可能感兴趣的:(qtcanpool)