Qt Designer 窗口/可视化组件及四种编辑模式 和 signals、slots 与 emit的关联
背景介绍
QT元对象系统组成
QObject
是QT对象模型的核心,包含信号和槽的类,必须直接或间接继承自 QObject 
Q_OBJECT
Q_OBJECT宏必须出现在类声明的最顶部 (默认私有),用来开启信号和槽、动态属性系统,或QT元对象系统提供的其他服务
MOC
MOC编译器为QObject子类提供了一些实现元对象特性所需要的一些代码。比如说信号,大家只是在类声明的时候声明了所需要的信息,在MOC编译时,会为信号添加函数定义。
一、信号与槽的关联
1.1 信号(Signals)
“信号”,在Qt中,操作一个控件后,会产生和操作对应的信号
signals 前面不可加 public、private 和 protected 进行修饰;
类似虚函数,只要在头文件中声明它就够了,而且这些信号函数没有函数体定义,函数体由moc自动生成,不需要用户在.cpp文件中实现;
而且也不能有返回类型(即signals 区域的函数必须是 void 类型);
宏定义和函数指针不能用于信号和槽的参数,信号和槽也不能有缺省参数。
信号函数定义规则:
信号是类的成员函数,并且返回类型必须是void
信号函数只需要声明,不需要定义
参数可以随意指定,信号也支持重载
信号需要使用 signals 关键字进行声明
在程序中发送自定义信号:发送信号的本质就是调用信号函数 emit mysignals()
emit 是个空宏,没有特殊含义,仅用来表示这个语句是发射一个信号,不写可以,但不推荐。
内置公用信号:@e.g. clicked()、clicked(bool)、pressed()、released()、toggled(bool);findNext()和findPrevious()
正常情况下单击按钮,响应顺序为:pressed() — <about 215ms> — released() — <almost 0ms> — clicked()。
@e.g.
signals: void SendData(QString str); // 信号函数 /*********************/ signals: void mySignal(); void mySignal(int x); void mySignalParam(int x,int y);
emit 发射信号:
xxx::yyy(zzz) { emit mySignal(); //发射信号mySignal() emit mySignal(5); //发射信号mySignal(int) emit mySignalParam(5,100); //发射信号mySignalParam(5,100) }
1.2 槽(Slots)
“槽”,在Qt中,“槽” 就是用来连接 “信号” 的函数。比如说,点击按钮后,按钮会产生一个点击信号,我们使用槽函数来接收点击信号,然后在槽函数中执行功能代码。
slots 在头文件声明时前面可以加 public、private 和 protected,因为 Qt 说槽函数是普通的C++成员函数,能被正常调用;
唯一特别性就是:可以关联多个信号,当和其关联的信号被 发射 emit 时,这个槽就会被调用;
宏定义和函数指针不能用于信号和槽的参数,信号和槽也不能有缺省参数。
槽函数定义规则:
槽函数的返回类型必须是void
槽函数的参数必须等于或少于信号的参数
当信号与槽函数的参数数量相同时,它们参数类型要完全一致
@e.g.
public slots: private slots: void GetData(QString str); // 槽函数 /********************/ public slots: void mySlot(); void mySlot(int x); void mySignalParam(int x,int y);
1.3 关联(Connect)
连接“信号”到“槽函数”,Connect()将信号与槽连接上:
@e.g.  connect(&SD,SIGNAL(SendData(QString)),this,SLOT(GetData(QString)));
信号和槽的关联有三种方式,分别是直接关联、使用中介对象和使用Qt的元对象系统。下面将详细介绍这三种方式。
1.3.1 直接(函数)关联
@note 最简单常见,这是Qt早期的连接语法,使用"字符串"表示信号和槽。在编译时无法进行类型检查。
@fmt 
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
@anno connect(发送信号的对象,信号名, 接收信号的对象,槽函数名);
其中,sender是信号的发送者;SIGNAL()是代表信号的宏定义,里面的参数signal是信号;receiver是信号的接收者;SLOT()是代表槽的宏定义,里面的参数slot是槽函数。举个例子:
@e.g.
QObject::connect(sender, SIGNAL(mySignal()), receiver, SLOT(mySlot())); QObject::connect(ui->pushButton1, SIGNAL(clicked()), this, SLOT(func()));
@cmt 这个connect的作用是,将界面的按钮(按钮命名为pushButton1)的点击信号clicked()连接到当前界面(用this表示)的槽函数func()。当点击界面的pushButton1按钮时,程序就会跑到this类中的func()函数里。
1.3.2 函数指针关联
@note 这种连接方式在编译时进行了类型检查,但在一些情况下可能不够灵活,比如连接到一个基类的槽。
@fmt 
QObject::connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot);
1.3.3 Lambda表达式连接(C++11及以后版本):
@note 这种方式可以使用 Lambda表达式 作为槽的实现,非常方便。
@fmt 
QObject::connect(sender, &SenderClass::signal, [=]() { /* slot implementation */ });
1.3.4 使用中介对象
@note 复杂的通信关系,这种方式可以帮助解耦信号发送者和信号接收者之间的关系
@e.g.
QObject::connect(sender, SIGNAL(mySignal()), mediator, SLOT(handleSignal())); QObject::connect(mediator, SIGNAL(processedSignal()), receiver, SLOT(mySlot()));
1.3.5 使用Qt的元对象系统
@note Qt的 元对象系统 是一种基于反射的机制。QMetaMethod::fromSignal、QMetaMethod::fromSlot
@e.g.
QObject::connect(sender, QMetaMethod::fromSignal(&Sender::mySignal), receiver, QMetaMethod::fromSlot(&Receiver::mySlot));
1.3.6 Qt5 连接方式·函数指针关联
@note 这种语法在Qt5中引入,允许指定连接类型,例如Qt::AutoConnection、Qt::DirectConnection等。
@fmt
QObject::connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot, Qt::ConnectionType);
五种连接方式:
Qt::AutoConnection:"自行”连接为默认连接方式。连接类型在信号发射时确定(运行时,而非编译时)。
根据线程亲和性,即信号和槽所在的线程自动选择连接方式。如果在同一线程,使用的是Qt::DirectConnection;否则多线程时则使用Qt::QueuedConnnection。
Qt::DirectConnection:信号被发送时,立即"直接"调用槽函数。适用于信号和槽在同一线程的情况,槽在信号发射的线程里执行。
Qt::QueuedConnection:将信号放入接收者线程的"事件队列"中,由接收者线程在适当的时候处理。
适用于跨线程通信。当控制权回到receiver所在线程时才执行槽函数。槽函数在receiver的线程里执行。
Qt::BlockingQueuedConnection:与QueuedConnection类似,但发送信号的线程会被阻塞,直到接收者线程处理完,槽函数返回。
当receiver和sender在同一个线程里时,不可以使用该方式,否则会发生死锁。
Qt::UniqueConnection:确保连接是唯一的,防止多次连接同一个信号和槽。如果已经存在相同的连接,新的连接将不会被创建。
该类型可以和上面的类型配合,用或“|”处理即可。当连接已经存在时,再次连接会失败。其实就是为了保证连接的唯一性。
注意:当使用QueuedConnection时,参数类型必须是 Qt 的 meta-object system 知道的类型,因为Qt要拷贝参数。可以connect()前调用qRegisterMetaType()来注册数据类型。
https://blog.csdn.net/weixin_55887103/article/details/142086124
https://blog.csdn.net/weixin_42812764/article/details/109516019
二、 Qt Designer 四种编辑模式
Qt Designer提供四种编辑模式: Widget Editing Mode 、 Signals and Slots Editing Mode 、 Buddy Editing Mode 和 Tab Order Editing Mode 。

在使用Qt Designer进行界面设计时,您将始终处于以下四种模式之一。要在此模式之间切换,请从“编辑”菜单或工具栏中选择它即可。
下表详细描述了这些模式。
窗口部件编辑模式(Widget Editing Mode),窗体内部件,可以操作更改表单的外观、添加布局以及编辑部件的属性。要切换到此模式,请按 F3。这是Qt Designer的默认模式。
信号/槽编辑模式(Signals and Slots Editing Mode),可以使用Qt的信号和槽机制将不同部件关联到一起。要切换到此模式,请按 F4。
伙伴编辑模式(Buddy Editing Mode),可以将“伙伴部件Buddy widget”(如编辑框)分配给标签(Lable)组件,以帮助它们正确处理“键盘焦点”。
Tab顺序编辑模式(Tab Order Editing Mode),可以设置部件接收“键盘焦点”(Tab和Shift+Tab)的切换顺序。
三、QMainWindow, QWidget 和 QDialog 三个窗口类区别
QMainWindow、QWidget和QDialog是Qt框架中常用的三个窗口类,它们在Qt应用程序中扮演不同的角色。

3.1 QMainWindow(主窗口):
概念:QMainWindow是Qt中的主窗口类,通常用于创建具有菜单栏 QMenuBar、工具栏 QTooBar、状态栏 QStatusBar等常见应用程序界面元素的窗口。
分类:属于顶级窗口类。
优势:提供了丰富的布局和管理功能,方便构建复杂的多文档界面(MDI)应用程序。
应用场景:适用于需要展示多个子窗口、具有菜单和工具栏的应用程序,如文本编辑器、图形编辑器等。

QMainWindow 中使用布局管理器
我们有时候会在 QMainWindow 中使用 QVBoxLayout 或 QHBoxLayout 时,无法显示布局中的内容。
原因:QMainWindow 类默认使用了一个 QVBoxLayout 作为其主布局。当尝试将 QVBoxLayout 或 QHBoxLayout 直接设置为 QMainWindow 的布局时,它们可能会与 QMainWindow 默认的主布局冲突,导致布局无法正确显示。
解决办法:
要在 QMainWindow 中使用 QVBoxLayout 或 QHBoxLayout,可以将它们放置在 QWidget 中,并将该 QWidget 设置为 QMainWindow 的  “中心部件central widget”。
KDiff3App::KDiff3App(QWidget* pParent, const QString& name, KDiff3Part* pKDiff3Part):
    QMainWindow(pParent) //previously KMainWindow
{
    setWindowFlags(Qt::Widget);
    setObjectName(name);
    m_pKDiff3Part = pKDiff3Part;
    m_pKDiff3Shell = qobject_cast<KParts::MainWindow*>(pParent);
    /* 1. 创建一个 QWidget 作为this"主窗口QMainWindow"的“中心部件” *
    m_pCentralWidget = new QWidget(this);
    
    /* 创建一个 QVBoxLayout,并将其设置为 “中心部件” 的“垂直布局管理器” */
    QVBoxLayout* pCentralLayout = new QVBoxLayout(m_pCentralWidget);
    pCentralLayout->setContentsMargins(0, 0, 0, 0);
    pCentralLayout->setSpacing(0);    
    /*  设置 QWidget 为 “主窗口QMainWindow” 的 “中心部件central widget” */
    setCentralWidget(m_pCentralWidget);
    /* 2. 创建一个 QWidget 作为“中心部件”的 “主工作区” */
    m_pMainWidget = new QWidget(m_pCentralWidget);
    m_pMainWidget->setObjectName("MainWidget");
    /* 设置 QWidget 为 “中心部件central widget” 的 “主工作区” */    
    pCentralLayout->addWidget(m_pMainWidget);
    m_pMainWidget->hide();
    setWindowTitle("KDiff3");
    setUpdatesEnabled(false);
    
    // ...
}
布局/分割函数,使用方式:
// 创建布局同时绑定父窗口部件: new QVBoxLayout( QWidget* parent) // 后期将布局添加到 QWidget 窗口中: QWidget*parent->setLayout( QVBoxLayout* ); // 布局添加“分割器”: QVBoxLayout* ->addWidget( QSplitter* ) // 布局添加窗口: QVBoxLayout* ->addWidget( QWidget* ) // 布局添加滚轮: QVBoxLayout* ->addWidget( QScrollBar* ) // 创建可水平分割“主”分割器(无父窗口): new QSplitter(Qt::Horizontal,0) // 从父窗口创建可垂直分割器: new QSplitter(Qt::Vertical, QWidget* parent) // 分割器添加新窗口: QSplitter* ->addWidget( QWidget* )
3.2 QWidget(窗口):
概念:QWidget是Qt中的基本窗口类,是所有用户界面控件的基类,可以作为其他窗口类的父类。
分类:属于顶级窗口类。
优势:提供了基本的窗口功能,如事件处理、布局管理等,可用于创建自定义的窗口。
应用场景:适用于创建简单的窗口,如对话框、工具窗口等。
3.3 QDialog(对话框):
概念:QDialog是Qt中用于创建对话框的窗口类,通常用于与用户进行交互,获取用户输入或显示消息。
分类:属于顶级窗口类。
优势:提供了对话框特有的功能,如模态对话框、按钮布局等,方便与用户进行简单的交互。
应用场景:适用于需要与用户进行交互的场景,如消息框、输入框、文件选择框等。
3.4 其他Qt内置(对话框):
对话框输入控件:QFileDialog(文件对话框),QInputDialog(输入对话框),QColorDialog(颜色对话框),QFontDialog(字体对话框)
对话框显示控件:QMessageBox(信息提示框),QErrorMessage(错误提示框),QProgressDialog(进度提示框)
总结:
QMainWindow适用于创建具有菜单栏、工具栏和状态栏等复杂界面的应用程序;
QWidget是基本的窗口类,可作为其他窗口类的父类;
QDialog用于创建对话框,方便与用户进行交互。
它们在功能和应用场景上有所区别,开发者可以根据具体需求选择合适的窗口类来构建Qt应用程序的界面。
四、 Qt GUI 设计:UI 界面可视化组件
参考文献:
https://developer.aliyun.com/article/1478616
https://xie.infoq.cn/article/9938aee0c0919b74cef3df266
4.0 界面可视化组件分类:
分别为:布局组件(Layouts)、分隔组件(Spacers)、按钮组件(Buttons)、表项视图(Item Views)、表项组件(Item Widgets)、容器组件(Containers)、输入组件(Input Widgets)、显示组件(Display Widgets)

下面只详细介绍一些常用组件:
4.1 布局组件(Layouts)
布局组件包括:Vertical Layout(水平布局)、Horizontal Layout(垂直布局)、Grid Layout(网格布局)、Form Layout(表单布局)。

QVBoxLayout (垂直布局):VBox Layout按垂直方向排列其子部件。它们会根据容器的大小自动调整子部件的大小和位置。可以向垂直布局添加部件,并控制它们的对齐方式、间距等属性。
QHBoxLayout (水平布局):HBox Layout按水平方向排列其子部件。类似于垂直布局,它们会根据容器的大小自动调整子部件的大小和位置。可以向水平布局添加部件,并控制它们的对齐方式、间距等属性。
4.2 分隔组件(Spacers)
分隔组件包括:Horizontal Spacer(水平分隔)、Vertical Spacer(垂直分隔)。

4.3 按钮组件(Buttons)
按钮组件包括:QPushButton(按键按钮)、QToolButton(工具按钮)、QRadioButton(单选框)、QCheckButton(复选框)、QCommandLinkButton(命令链接按钮)、QDialogButtonBox(对话框确认-选择按钮框)。

 
4.4 表项视图(Item Views)
表项视图包括:QListView(列表视图)、QTreeView(树状视图)、QTableView(表格视图)、QColumnView(列表视图)、QUndoView(撤销视图)。

4.5 表项组件(Item Widgets)
表项组件包括:QListWidget(列表表项)、QTreeWidget(树状表项)QTableWidget(表格表项)。

4.6 容器组件(Containers)
容器组件包括:QGroupBox(组合框,可以在内部添加内容,并修改标题头)、QScrollArea(带滑动条的框)、QToolBox(抽屉式框)、QTabWidget(标签式框)、QStackedWidget(栈式,需要信号启动)、
QFrame(带边框的布局)、QWidget(不带边框的布局)、QMdiarea(多文档分栏显示){QMdiSubWindow}、QDockWidget(浮动窗口)、QAxWidget(只可以在 Windows 环境使用)。

QSplitter (窗口分割器):
4.7 输入组件(Input Widgets)
输入组件包括:QComboBox(组合框下拉框)、QFontDialog(下拉字体对话框)、QlineEdit(单行编辑框)、QTextEdit(多行编辑框,只可以查看文字、图片和动画)、QPlainTextEdit(多行文字编辑框)、
QSpinBox(整型数字调节框)、QDoubleSpinBox(浮点数字调节框)、QTimeEdit(时间输入)、QDateEdit(日期输入)、QDateTimeEdit(日期和时间输入)、QDial(罗盘旋转输入)、
QScrollBar(滚动条){Horizontal Scroll Bar(水平滚动条)、Vertical Scroll Bar(垂直滚动条)}、QSlider(直线拖动输入){Horizontal Slider(水平滑动条)、Vertical Slider(垂直滑动条)}、
QkeySequenceEdit(快捷键输入)。

调节输入控件:QAbstractSpinBox(步长调节输入)
滑动输入控件:QRubberBand(橡皮筋拖拽)
QScrollBar (滚动条):
4.8 显示组件(Display Widgets)
显示组件包括:QLabel(标签显示框,可查看文本、网页、图片和动画等)、QTextBrowser(文本签显示框)、QGraphicsView(绘图工具)、QCalendarWidget(日历)、QLCDNumber(LCD 液晶数码显示)、
QProgressBar(进度条显示)、Line{Horizontal Line(水平分割线)、Vertical Line(垂直分割线)}、QOpenGLWidget(OpenGL(Open Graphics Library,开放图形库)图形渲染的部件,
可以在 PyQt 和 Qt 的应用中显示图形(包括 2D 和 3D 图形))、QQuickWidget(加载 QML 文件)。

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号