✿3-The Basics-GUI Design Using Qt Widgets

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✿3-The Basics-GUI Design Using Qt Widgets相关的知识,希望对你有一定的参考价值。

所有的小部件(widgets)都继承自QObjectQWidget是所有UI wdigets的父类,它包含绝大多数去描述一个小部件的属性,如geometry、color、mouse、keyboard、tooltips。

所有继承自QObject的对象都有一个父子关系,这种关系让开发者更便利,如:

  • 当一个部件销毁时,所有它的子类都会被销毁。这避免了内存泄露。
  • 你可以查找一个给定的QWidget类,通过findChild()findChildren
  • 在一个QWidget里的子部件自动地包含于它的父部件里。

QObject
QObject是Qt对象模型的心脏。该模型的核心功能是一种非常强大的无缝对象通信机制,称为信号和插槽(signals and slots)。可以使用connect()将信号连接到插槽,并使用disconnect()销毁连接。为了避免永无止境的通知循环,你可以使用blockSignals()临时阻止信号。受保护的函数connectNotify()和disconnectNotify()使跟踪连接成为可能。

QoObject在对象树中组织自己。当你以另一个对象作为父对象创建QObject时,该对象将自动将自己添加到父对象的children()列表中。父对象拥有该对象的所有权;例如,它将在析构函数中自动删除其子级。可以按名称查找对象,也可以选择使用findChild()或findChildren()。

template <typename T> T
QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
下面示例返回名为“button1”的parentWidget的子QPushButton,即使该按钮不是父对象的直接子对象:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1");

下面示例返回parentWidget的QListWidget子级:

QListWidget *list = parentWidget->findChild<QListWidget *>();

下面示例返回名为“button1”的parentWidget(其直接父对象)的子QPushButton:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1", Qt::FindDirectChildrenOnly);

下面示例返回parentWidget的QListWidget子级,即其直接父级:

QListWidget *list = parentWidget->findChild<QListWidget *>(QString(), Qt::FindDirectChildrenOnly);

每个对象都有一个objectName(),其类名可以通过相应的metaObject()找到(参见QMetaObject::className())。可以使用inherits()函数确定对象的类是否继承QObject继承层次结构中的另一个类。

QObject *obj = new QPushButton;
obj->metaObject()->className();             // returns "QPushButton"

QPushButton::staticMetaObject.className();  // returns "QPushButton"
QTimer *timer = new QTimer;         // QTimer inherits QObject
timer->inherits("QTimer");          // returns true
timer->inherits("QObject");         // returns true
timer->inherits("QAbstractButton"); // returns false

// QVBoxLayout inherits QObject and QLayoutItem
QVBoxLayout *layout = new QVBoxLayout;
layout->inherits("QObject");        // returns true
layout->inherits("QLayoutItem");    // returns true (even though QLayoutItem is not a QObject)

当一个对象被删除,它会触发destroyed()信号。你可以捕捉这个信号,以避免挂起对QoObject的引用。

QObjects可以接收通过event()并过滤其他对象的事件。可以重新实现方便的处理程序childEvent(),以捕获子事件。

bool QObject::event(QEvent *e)
此虚函数接收对象的事件,如果识别并处理了事件e,则应返回true。
可以重新实现event()函数来自定义对象的行为。
请确保为所有未处理的事件调用父事件类实现。

class MyClass : public QWidget 
Q_OBJECT
public:
    MyClass(QWidget *parent = nullptr);
    ~MyClass();
    bool event(QEvent* ev) override 
        if (ev->type() == QEvent::PolishRequest) 
            // overwrite handling of PolishRequest if any
            doThings();
            return true;
         else  if (ev->type() == QEvent::Show) 
            // complement handling of Show if any
            doThings2();
            QWidget::event(ev);
            return true;
        
        // Make sure the rest of events are handled
        return QWidget::event(ev);
    
;

最后但并非最不重要的一点是,QObject在Qt中提供了基本的计时器支持。

 QTimer *timer = new QTimer(this);
 connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
 timer->start(1000);// one second (1000 millisecond)

请注意,对于任何实现信号、插槽或属性的对象,Q_OBJECT宏都是必需的。你还需要在源文件上运行元对象编译器。我们强烈建议在QObject的所有子类中使用此宏,无论它们是否实际使用信号、插槽和属性,因为如果不这样做,可能会导致某些函数表现出奇怪的行为。

所有Qt widgets都继承自QObject。便利函数isWidgetType()返回一个对象是否实际上是一个小部件,它比qobject_cast<QWidget *>(obj)obj->inherits(“QWidget”)更快速。

一些OObject函数,如children()返回一个QObjectList。QObjectList是一个QList<QObject*>的类型定义。


使用QLabel展示一个简单的文本:

#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[]) 
	QApplication app(argc, argv);
	QLabel myLabel;
	myLabel.setText("Hello World");
	myLabel.show();
	return app.exec();


要记得将下面语句添加到.pro文件中,以便启用Qt Widgets模块:

QT += widgets

一旦你更改了.pro文件,你需要运行qmake

你可以通过C++代码动态地添加一个垂直布局:

QWidget *widget = new QWidget; 
QPushButton *pushBtn1 = new QPushButton("Push Button 1"); 
QPushButton *pushBtn2 = new QPushButton("Push Button 2"); 
QPushButton *pushBtn3 = new QPushButton("Push Button 3"); 
QPushButton *pushBtn4 = new QPushButton("Push Button 4"); 
QVBoxLayout *verticalLayout = new QVBoxLayout(widget); 
verticalLayout->addWidget(pushBtn1); 
verticalLayout->addWidget(pushBtn2); 
verticalLayout->addWidget(pushBtn3); 
verticalLayout->addWidget(pushBtn4); 
widget->show ();

记住,QWidget实例将会成为应用程序的主窗口。上例中布局被设置为主布局顶层。如果你在构造函数中没有设置父窗口,你得使用QWidget::setLayout()在之后安装布局并重新设置小部件实例的父窗口。

Tools->C+±>Inspect C++ Code Model…->Working Copy
ui_mainwindow.h

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_MainWindow 
public:
    QWidget *centralWidget;
    QWidget *widget;
    QVBoxLayout *verticalLayout;
    QPushButton *pushButton;
    QPushButton *pushButton_2;
    QPushButton *pushButton_3;
    QPushButton *pushButton_4;
    QMenuBar *menuBar;
    QToolBar *mainToolBar;
    QStatusBar *statusBar;

    void setupUi(QMainWindow *MainWindow) 
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QStringLiteral("MainWindow"));
        MainWindow->resize(400, 300);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));
        widget = new QWidget(centralWidget);
        widget->setObjectName(QStringLiteral("widget"));
        widget->setGeometry(QRect(130, 50, 82, 74));
        verticalLayout = new QVBoxLayout(widget);
        verticalLayout->setSpacing(6);
        verticalLayout->setContentsMargins(11, 11, 11, 11);
        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
        verticalLayout->setContentsMargins(0, 0, 0, 0);
        
        pushButton = new QPushButton(widget);
        pushButton->setObjectName(QStringLiteral("pushButton"));
        pushButton->setCheckable(false);
        verticalLayout->addWidget(pushButton);

        pushButton_2 = new QPushButton(widget);
        pushButton_2->setObjectName(QStringLiteral("pushButton_2"));
        verticalLayout->addWidget(pushButton_2);

        pushButton_3 = new QPushButton(widget);
        pushButton_3->setObjectName(QStringLiteral("pushButton_3"));
        verticalLayout->addWidget(pushButton_3);

        pushButton_4 = new QPushButton(widget);
        pushButton_4->setObjectName(QStringLiteral("pushButton_4"));
        verticalLayout->addWidget(pushButton_4);

        MainWindow->setCentralWidget(centralWidget);
        
        menuBar = new QMenuBar(MainWindow);
        menuBar->setObjectName(QStringLiteral("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 400, 17));
        MainWindow->setMenuBar(menuBar);
        
        mainToolBar = new QToolBar(MainWindow);
        mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
        MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
        
        statusBar = new QStatusBar(MainWindow);
        statusBar->setObjectName(QStringLiteral("statusBar"));
        MainWindow->setStatusBar(statusBar);

        retranslateUi(MainWindow);

        QMetaObject::connectSlotsByName(MainWindow);
     // setupUi

    void retranslateUi(QMainWindow *MainWindow) 
        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR));
        pushButton->setText(QApplication::translate("MainWindow", "PushButton1", Q_NULLPTR));
        pushButton_2->setText(QApplication::translate("MainWindow", "PushButton2", Q_NULLPTR));
        pushButton_3->setText(QApplication::translate("MainWindow", "PushButton3", Q_NULLPTR));
        pushButton_4->setText(QApplication::translate("MainWindow", "PushButton4", Q_NULLPTR));
     // retranslateUi
;

namespace Ui 
    class MainWindow: public Ui_MainWindow ;
 // namespace Ui

QT_END_NAMESPACE


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui  class MainWindow; 

class MainWindow: public QMainWindow 
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    Ui::MainWindow *ui;
;
#endif

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;

main.cpp

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

创建自定义小部件




我们修改mylabel.h,添加头文件#include,在类声明前添加一个宏QDESIGNER_WIDGET_EXPORT,这样可确保类会正确的导出到DDL(dynamic-link library)或共享库。你的自定义小部件可能在没有这个宏时可以正常的工作,但添加这个宏是一个好的实践。

你得更新myframe.h和mylabel.h头文件如下,以避免depressed warning:

#include <QtUiPlugin/QDesignerCustomWidgetInterface>


在mylabelplugin.h中,实现group()函数:

QString MyFramePlugin::group() const 
	return QLatin1String("My Containers");

为了创建一个自定义geometry或其他属性的小部件,你可以实现domXml()函数:

QString MyLabelPlugin::domXml() const 
    return QLatin1String(
    "<widget class='MyLabel' name='myLabel'>\\n"
            "<property name='geometry'>\\n"
                "<rect>\\n"
                    "<x>0</x>\\n"
                    "<y>0</y>\\n"
                    "<width>100</width>\\n"
                    "<height>16</height>\\n"
                "</rect>\\n"
            "</property>\\n"
            "<property name='text'>\\n"
                "<string>MyLabel</string>\\n"
            "</property>\\n"
    "</widget>\\n");

在Release模式下,Build。然后,可以手动将新生成的文件夹里的release目录下的mywidgetcollectionplugin.dll复制到D:\\Qt\\Qt5.9.0\\5.9\\mingw53_32\\plugins\\designer路径下。这个路径及文件扩展名依不同操作系统而异。如果不做这样的拷贝操作,就无法在designer.exe窗口看到我们自定义的插件。
在D:\\Qt\\Qt5.9.0\\5.9\\mingw53_32\\bin目录下,双击designer.exe。
单击“创建”按钮,将MyLabel拖到MyFrame里面。

创建Qt样式表和自定义主题
Qt Style Sheet语法和html/CSS语法是一致的。

QPushButton  
	background-color: rgb(193, 255, 216); 
	border-width: 2px; 
	border-radius: 6; 
	border-color: lime; 
	border-style: solid; 
	padding: 2px; 
	min-height: 2.5ex; 
	min-width: 10ex; 
 
QPushButton:hover  
	background-color: rgb(170, 255, 127); 
 
QPushButton:pressed  
	background-color: rgb(170, 255, 127); 
	font: bold; 

在前面的示例中,只有按钮将获得样式表中描述的样式,而所有其他小部件将具有本机样式。还可以为每个按钮创建不同的样式,并通过在样式表中提及按钮的对象名称,将样式应用于各个按钮:QPushButton#pushButtonID

CSS选择符主要有3种:HTML选择符、class选择符 和 id选择符。
1.HTML选择符:以HTML标签作为选择符。如:

h1 text-align: center; color: blue

<h1>一级标题居中蓝色</h1>

2.class选择符:使用HTML标签的class属性值作为选择符。定义class选择符时,前面要加“.”标志。如:

.title text-align: center; color: blue

<p class="title">蓝色的段落</p>
<h1 class="title">蓝色的标题</h1>

3.id选择符:使用HTML标签的id属性值作为选择符。定义id选择符时,前面要加“#”标志。如:

#red color:red;
#green color:green;

<p id="red">这个段落是红色</p>
<p id="green">这个段落是绿色</p>

使用QSS文件
可以创建扩展名为.qss的新样式表文件,然后将其添加到资源文件(.qrc)中。
你可以应用样式表给小部件:

MyWidget::MyWidget(QWidget* parent): QWidget(parent) 
	setStyleSheet("QWidget background-color: green");

你也可以应用样式表给整个应用程序:

#include "mywidget.h" 
#include <QApplication> 
#include <QFile> 
int main(int argc, char *argv[])  
	QApplication app(argc, argv); 
	QFile file(":/qss/default.qss"); 
	file.open(QFile::ReadOnly); 
	QString styleSheet = QLatin1String(file.readAll()); 
	app.setStyleSheet(styleSheet); 
	Widget mywidget; 
	mywidget.show(); 
	return app.exec();

Qt提供了几个QStyle子类,它们模拟Qt支持的不同平台的样式。这些样式在Qt GUI模块中很容易获得。你可以构建自己的自定义样式,并将其导出为插件。Qt使用QStyle呈现Qt小部件,以确保它们的外观和感觉与本机小部件相同。

你可以对单独的小部件设置样式,使用QWidget::setStyle()函数。

通过创建自定义样式,可以自定义GUI的外观。创建自定义样式有两种不同的渠道。在静态渠道中,可以对QStyle类进行子类化,并重新实现虚函数以提供所需的行为,或者从头重写QStyle类。QCommonStyle通常用作基类,而不是QStyle。在动态渠道中,可以将QProxyStyle子类化,并在运行时修改系统样式的行为。你还可以通过使用诸如drawPrimitive()、drawItemText()和drawControl()等QStyle函数来开发支持样式的自定义小部件。

有几种方法可以在Qt应用程序中应用自定义样式。最简单的方法是在创建QApplication对象之前调用QApplication::setStyle()静态函数,如下所示:

#include "customstyle.h" 
int main(int argc, char *argv[])  
	以上是关于✿3-The Basics-GUI Design Using Qt Widgets的主要内容,如果未能解决你的问题,请参考以下文章

355. Design Twitter [classic design]

[Design Pattern] Adapter Design Pattern

零元学Expression Design 4 - Chapter 2 熟悉Design并且快速设计出Silverlight网页

Android Material Design-Creating Apps with Material Design(用 Material Design设计App)-(零)

Design Principles and Design Patterns

Design Support Library(支撑Material Design)