✿3-The Basics-GUI Design Using Qt Widgets
Posted itzyjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✿3-The Basics-GUI Design Using Qt Widgets相关的知识,希望对你有一定的参考价值。
所有的小部件(widgets)都继承自QObject。QWidget是所有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)-(零)