- Qt 5 Blueprints
- Symeon Huang
- 1301字
- 2021-07-23 19:48:18
Tweaking the digital clock
It's time to make this basic digital clock look more beautiful. Let's add something like a transparent background, which sits on top of the frameless window. Using a transparent background can deliver a fantastic visual effect. While the frameless window hides window decorations, including a border and the title bar, a desktop widget, such as a clock, should be frameless and displayed on top of the desktop.
To make our clock translucent, simply add the following line to the constructor of MainWindow
:
setAttribute(Qt::WA_TranslucentBackground);
The effect of the WA_TranslucentBackground
attribute depends on the composition managers on the X11 platforms.
A widget may have lots of attributes, and this function is used to switch on or switch off a specified attribute. It's turned on by default. You need to pass a false Boolean as the second argument to disable an attribute. The full list of Qt::WidgetAttribute
can be found in the Qt Reference Documentation.
Now, add the following line to the constructor as well, which will make the clock look frameless and make it stay on top of the desktop:
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
Similarly, Qt::WindowFlags
is used to define the type of widget. It controls the behavior of the widget, rather than of its properties. Thus, two hints are given: one is to stay on top and the other is to be frameless. If you want to preserve old flags while setting new ones, you need to add them to the combination.
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | windowFlags());
Here, the windowFlags
function is used to retrieve the window flags. One thing you may be interested to know is that setWindowFlags
will result in the invisibility of the widget after the show
function. So, you can either call setWindowFlags
before the show
function of the window or widget or call show
again after setWindowFlags
.
After the modification to the constructor, this is how the clock is expected to look:

There is a useful trick that you can use to hide the clock from the taskbar. Of course, a clock doesn't need to be displayed among the applications in a taskbar. You should never set a flag such as Qt::Tool
or Qt::ToolTip
alone to achieve this because this will cause the exit behavior of the application to be abnormal. This trick is even simpler; here is the code of main.cpp
:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget wid; MainWindow w(&wid); w.show(); return a.exec(); }
The preceding code makes our MainWindow w
object a child of QWidget wid
. The child widgets won't display on the taskbar because there should be only one top parent widget. Meanwhile, our parent widget, wid
, doesn't even show. It's tricky, but it's the only one that does the trick without breaking any other logic.
Well, a new problem has just surfaced. The clock is unable to move and the only way to close it is by stopping it through the Qt Creator's panel or through a keyboard shortcut. This is because we declared it as a frameless window, which led to an inability to control it via a window manager. Since there is no way to interact with it, it's impossible to close it by itself. Hence, the solution to this problem is to write our own functions to move and close the clock.
Closing this application may be more urgent. Let's see how to reimplement some functions to achieve this goal. First, we need to declare a new showContextMenu
slot to display a context menu and reimplement mouseReleaseEvent
. The following code shows the content of 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; private slots: void updateTime(); void showContextMenu(const QPoint &pos); protected: void mouseReleaseEvent(QMouseEvent *); }; #endif // MAINWINDOW_H
There are two new classes defined in the preceding code: QPoint
and QMouseEvent
. The QPoint
class defines a point in the plane by using integer precision. Relatively, there is another class named QPointF
, which provides float precision. Well, the QMouseEvent
class inherits QEvent
and QInputEvent
. It contains some parameters that describe a mouse event. Let's see why we need them in mainwindow.cpp
:
#include <QTimer> #include <QTime> #include <QMouseEvent> #include <QMenu> #include <QAction> #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | windowFlags()); connect(this, &MainWindow::customContextMenuRequested, this, &MainWindow::showContextMenu); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::updateTime); timer->start(1000); updateTime(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::updateTime() { QTime currentTime = QTime::currentTime(); QString currentTimeText = currentTime.toString("hh:mm"); if (currentTime.second() % 2 == 0) { currentTimeText[2] = ' '; } ui->lcdNumber->display(currentTimeText); } void MainWindow::showContextMenu(const QPoint &pos) { QMenu contextMenu; contextMenu.addAction(QString("Exit"), this, SLOT(close())); contextMenu.exec(mapToGlobal(pos)); } void MainWindow::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton) { emit customContextMenuRequested(e->pos()); } else { QMainWindow::mouseReleaseEvent(e); } }
Note that you should include QMouseEvent
, QMenu
, and QAction
in order to utilize these classes. There is a predefined customContextMenuRequested
signal, which is coupled with the newly created showContextMenu
slot. For the sake of consistency, we will follow the rule that Qt defined, which means that the QPoint
argument in customContextMenuRequested
should be a local position instead of a global position. That's why we need a mapToGlobal
function to translate pos
to a global position. As for the QMenu
class, it provides a menu
widget for a menu bar, context menu, or other pop-up menus. So, we create the contextMenu
object, and then add a new action with the Exit
text. This is coupled with a close
slot of MainWindow
. The last statement is used to execute the contextMenu
object at the specified global position. In other words, this slot will display a pop-up menu at the given position.
The reimplementation of mouseReleaseEvent
is done to check the triggered button of the event. If it's the right button, emit the customContextMenuRequested
signal with the local position of the mouse. Otherwise, simply call the default mouseReleaseEvent
function of QMainWindow
.
Make use of the default member functions of the base class when you reimplement it.
Run the application again; you can quit by right-clicking on it and then selecting Exit. Now, we should continue the reimplementation to make the clock movable. This time, we need to rewrite two protected functions: mousePressEvent
and mouseMoveEvent
. Therefore, this is how the header file looks:
#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; QPoint m_mousePos; private slots: void updateTime(); void showContextMenu(const QPoint &pos); protected: void mouseReleaseEvent(QMouseEvent *); void mousePressEvent(QMouseEvent *); void mouseMoveEvent(QMouseEvent *); }; #endif // MAINWINDOW_H
There is also a declaration of a new private member variable in the preceding code, m_mousePos
, which is a QPoint
object used to store the local position of the mouse. The following code defines mousePressEvent
and mouseMoveEvent
:
void MainWindow::mousePressEvent(QMouseEvent *e) { m_mousePos = e->pos(); } void MainWindow::mouseMoveEvent(QMouseEvent *e) { this->move(e->globalPos() - m_mousePos); }
It's easier than you thought. When a mouse button is pressed, the local position of the mouse is stored as m_mousePos
. When the mouse is moving, we call the move
function to move MainWindow
to a new position. Because the position passed to move
is a global position, we need to use globalPos
of the event minus the local position of the mouse. Confused? The m_mousePos
variable is the mouse's relative position to the top-left point of the parent widget, which is MainWindow
in our case. The move
function will move the top-left point of MainWindow
to the given global position. While the e->globalPos()
function is the global position of the mouse and not MainWindow
, we need to subtract the relative position of m_mousePos
to translate the mouse's global position to the top-left point position of MainWindow
. After all this effort, the clock should look much more satisfying.
- 大話PLC(輕松動漫版)
- Getting started with Google Guava
- SQL Server 2016從入門到精通(視頻教學超值版)
- Web開發(fā)的貴族:ASP.NET 3.5+SQL Server 2008
- Learning ELK Stack
- Android 應用案例開發(fā)大全(第3版)
- Serverless computing in Azure with .NET
- Unity UI Cookbook
- PLC應用技術(三菱FX2N系列)
- Go語言精進之路:從新手到高手的編程思想、方法和技巧(2)
- 零基礎學C語言第2版
- Android系統(tǒng)下Java編程詳解
- Practical Predictive Analytics
- Web開發(fā)新體驗
- Learning Rust