該示例演示了如何使用QBasicTimer和timerEvent對(duì)小部件進(jìn)行動(dòng)畫處理和使用QFontMetrics確定屏幕上文本的大小。

??QBasicTimer是計(jì)時(shí)器的低級(jí)類。與QTimer不同,QBasicTimer不會(huì)從QObject繼承。它不會(huì)在經(jīng)過一定時(shí)間后發(fā)出timeout()信號(hào),而是將QTimerEvent發(fā)送到我們選擇的QObject。這使QBasicTimer成為QTimer的更輕量級(jí)替代。主要用于高度優(yōu)化或性能要求較高的應(yīng)用程序(例如嵌入式應(yīng)用程序)。
??該示例包含兩個(gè)類:
-
WigglyWidget是自定義的小部件,搖擺地顯示文本。 -
Dialog是允許用戶輸入文本的對(duì)話框小部件。它結(jié)合了WigglyWidget和QLineEdit。
Dialog類定義
??Dialog類提供了一個(gè)對(duì)話窗口小部件,允許用戶輸入文本。然后顯示W(wǎng)igglyWidget。
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
};
Dialog類實(shí)現(xiàn)
??Dialog構(gòu)造函數(shù)中,我們創(chuàng)建一個(gè)擺動(dòng)的窗口小部件以及l(fā)ine編輯,然后將這兩個(gè)窗口小部件置于垂直布局中。我們將行編輯的textChanged()信號(hào)連接到擺動(dòng)小部件的setText()槽函數(shù),以獲得與擺動(dòng)小部件的實(shí)時(shí)交互。
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
WigglyWidget *wigglyWidget = new WigglyWidget;
QLineEdit *lineEdit = new QLineEdit;
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(wigglyWidget);
layout->addWidget(lineEdit);
connect(lineEdit, &QLineEdit::textChanged, wigglyWidget, &WigglyWidget::setText);
lineEdit->setText(tr("Hello world!"));
setWindowTitle(tr("Wiggly"));
resize(360, 145);
}
WigglyWidget類定義
??WigglyWidget類提供了波浪線顯示文本。我們將QWidget子類化,并重新實(shí)現(xiàn)標(biāo)準(zhǔn)的paintEvent()和timerEvent()函數(shù)以繪制和更新窗口小部件。另外,我們實(shí)現(xiàn)了一個(gè)公共setText()插槽,用于設(shè)置窗口的文本。
??QBasicTimertimer類用于定期更新文本窗口,從而使文本移動(dòng)。text變量用于存儲(chǔ)當(dāng)前顯示的文本,并根據(jù)step計(jì)算搖擺線上每個(gè)字符的位置和顏色。
class WigglyWidget : public QWidget
{
Q_OBJECT
public:
WigglyWidget(QWidget *parent = nullptr);
public slots:
void setText(const QString &newText) { text = newText; }
protected:
void paintEvent(QPaintEvent *event) override;
void timerEvent(QTimerEvent *event) override;
private:
QBasicTimer timer;
QString text;
int step;
};
WigglyWidget類的實(shí)現(xiàn)
??在構(gòu)造函數(shù)中,我們使用QPalette::Midlight顏色WigglyWidget窗口的背景比通常的背景略淺。setFont為設(shè)置繪制背景的調(diào)色板中的畫筆和字體大小。
??最后,我們啟動(dòng)計(jì)時(shí)器,調(diào)用QBasicTimer::start()可確保WigglyWidget接收計(jì)時(shí)器超時(shí)(每60毫秒)時(shí)生成的計(jì)時(shí)器事件,從而刷新文本動(dòng)畫。
WigglyWidget::WigglyWidget(QWidget *parent)
: QWidget(parent), step(0)
{
setBackgroundRole(QPalette::Midlight);
setAutoFillBackground(true);
QFont newFont = font();
newFont.setPointSize(newFont.pointSize() + 20);
setFont(newFont);
timer.start(60, this);
}
??sineTable表示正弦曲線的y值乘以100。它用于使WigglyWidget沿正弦曲線移動(dòng)。
??而QFontMetrics對(duì)象提供有關(guān)文本的字體信息。該x變量是水平位置,是表示開始繪制文本的位置。y變量是文本基線的垂直位置。計(jì)算兩個(gè)變量以使文本在水平和垂直居中。為了計(jì)算基線,我們考慮了字體的上升(基線上方的字體的高度)和字體的下降(基線下方的字體的高度)。如果下降等于上升,則它們會(huì)相互抵消,并且基線位于height()/2處。
void WigglyWidget::paintEvent(QPaintEvent * /* event */)
{
static constexpr int sineTable[16] = {
0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38
};
QFontMetrics metrics(font());
int x = (width() - metrics.horizontalAdvance(text)) / 2;
int y = (height() + metrics.ascent() - metrics.descent()) / 2;
QColor color;
??每次paintEvent()調(diào)用該函數(shù)時(shí),我們都會(huì)創(chuàng)建一個(gè)QPainter對(duì)象painter用于繪制窗口的內(nèi)容。對(duì)于其中的每個(gè)字符text,我們根據(jù)step來確定顏色和在擺動(dòng)線上的位置。另外,x以字符的寬度遞增。
??為簡(jiǎn)單起見,我們假設(shè)QFontMetrics::horizo??ntalAdvance(text)返回單個(gè)字符進(jìn)度的總和QFontMetrics::horizo??ntalAdvance(text[i]))。實(shí)際上,情況并非總是如此,因?yàn)?code>QFontMetrics::horizo??ntalAdvance(text)還考慮了某些字母(例如'A'和'V')之間的字距調(diào)整。結(jié)果是文本不能完美居中。您可以通過在行編輯中鍵入"AVAVAVAVAVAVAV"來驗(yàn)證這一點(diǎn)。
QPainter painter(this);
for (int i = 0; i < text.size(); ++i) {
int index = (step + i) % 16;
color.setHsv((15 - index) * 16, 255, 191);
painter.setPen(color);
painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400),
QString(text[i]));
x += metrics.horizontalAdvance(text[i]);
}
}
??timerEvent函數(shù)接收WigglyWidget窗口生成的所有計(jì)時(shí)器事件。如果QBasicTimer發(fā)送了一個(gè)計(jì)時(shí)器事件,我們將遞增step以使文本移動(dòng),然后調(diào)用QWidget::update()刷新顯示。其他任何計(jì)時(shí)器事件都將傳遞給timerEvent函數(shù)的基類實(shí)現(xiàn)。
??需要注意的是,調(diào)用update()并不會(huì)立即執(zhí)行重繪時(shí)間,需要等待Qt的事件循環(huán)返回后才會(huì)執(zhí)行重繪操作。
void WigglyWidget::timerEvent(QTimerEvent *event)
{
if (event->timerId() == timer.timerId()) {
++step;
update();
} else {
QWidget::timerEvent(event);
}
...
}
關(guān)于更多
- 在QtCreator軟件可以找到:

- 或在以下Qt安裝目錄找到:
C:\Qt\{你的Qt版本}\Examples\{你的Qt版本}\widgets\widgets\wiggly
- 相關(guān)鏈接
https://doc.qt.io/qt-5/qtwidgets-widgets-wiggly-example.html
- Qt君公眾號(hào)回復(fù)『Qt示例』獲取更多內(nèi)容。