所有的小部件(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();
QPushButton::staticMetaObject.className();
QTimer *timer = new QTimer;
timer->inherits("QTimer");
timer->inherits("QObject");
timer->inherits("QAbstractButton");
QVBoxLayout *layout = new QVBoxLayout;
layout->inherits("QObject");
layout->inherits("QLayoutItem");
当一个对象被删除,它会触发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) {
doThings();
return true;
} else if (ev->type() == QEvent::Show) {
doThings2();
QWidget::event(ev);
return true;
}
return QWidget::event(ev);
}
};
最后但并非最不重要的一点是,QObject在Qt中提供了基本的计时器支持。
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);
请注意,对于任何实现信号、插槽或属性的对象,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);
}
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));
}
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
}
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[]) {
QApplication::setStyle(new CustomStyle);
QApplication app(argc, argv);
Widget helloworld;
helloworld.show();
return app.exec();
}
小部件是可以在屏幕上显示的GUI元素。这可能包括标签、按钮、列表视图、窗口、对话框等。所有小部件都在屏幕上向用户显示特定信息,其中大多数小部件允许用户通过键盘或鼠标进行交互。
窗口是没有其他父窗口小部件的顶级窗口小部件。通常,除非指定了任何窗口标志,否则窗口都有标题栏和边框。窗口样式和某些策略由底层窗口系统决定。Qt中的一些常见窗口类是QMainWindow、QMessageBox和QDialog。主窗口通常遵循桌面应用程序的预定义布局,包括菜单栏、工具栏、中心小部件区域和状态栏。QMainWindow需要一个中心小部件,即使它只是一个占位符。其他组件可以在主窗口中移除。下图显示了QMainWindow的布局结构。我们通常调用show()方法来显示小部件或主窗口。
QMenuBar位于QMainWindow的顶部。你可以添加菜单选项,如文件、编辑、查看和帮助。在下面显示QMenuBar的屏幕截图中,有QToolBar。QDockWidget提供了一个小部件,可以停靠在QMainWindow内部,也可以作为顶级窗口浮动。中心窗口小部件是主视图区域,你可以在其中添加表单或子窗口小部件。使用子窗口小部件创建自己的视图区域,然后调用setCentralWidget()。
重要提示: QMainWindow不应与QWindow混淆。QWindow是一个方便的类,表示底层窗口系统中的窗口。通常,应用程序的UI使用QWidget或QMainWindow。但是,如果希望保持最小的依赖关系,可以直接渲染到QWindow。
QMessageBox是一种对话框,用于显示信息和警报,或向用户提问。通常,exec()方法用于显示对话框。 可以使用以下代码段创建一个简单的消息框:
QMessageBox messageBox;
messageBox.setText("This is a simple QMessageBox.");
messageBox.exec();
|