📋 教程概述
本教程将手把手教你使用 Qt QML 在 HarmonyOS PC 平台上开发一个功能完整、界面精美的时钟应用。从零开始,逐步实现实时时钟显示、日期显示、12/24 小时制切换等功能。
🎯 学习目标
通过本教程,你将学会:

本教程开发的应用支持多平台运行:
首先,创建一个基本的 QML 窗口结构:
// main.qml - 基础结构
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 800
height: 1200
visible: true
title: "Qt 时钟 for 鸿蒙"
Rectangle {
anchors.fill: parent
color: "#2C5364"
}
}
代码说明:
Window:创建应用窗口width/height:设置窗口尺寸Rectangle:填充整个窗口作为背景此时运行应用,应该看到一个纯色背景的窗口。
使用 Gradient 组件创建美观的渐变背景:
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#0F2027" }
GradientStop { position: 0.5; color: "#203A43" }
GradientStop { position: 1.0; color: "#2C5364" }
}
}
技术要点:
Gradient:定义渐变效果GradientStop:设置渐变节点position:0.0(顶部)到 1.0(底部)color:每个节点的颜色颜色选择:
#0F2027:深蓝灰色(顶部)#203A43:中蓝色(中间)#2C5364:浅蓝灰色(底部)效果:创建从上到下的平滑渐变,营造深邃海洋的视觉感受。
在 Rectangle 内部添加时间显示:
Rectangle {
anchors.fill: parent
gradient: Gradient {
// ... 渐变代码
}
// 添加时钟显示
Text {
id: timeDisplay
text: Qt.formatTime(new Date(), "hh:mm:ss")
font.pixelSize: 120
font.bold: true
font.family: "Arial"
color: "#FFFFFF"
anchors.centerIn: parent
// 发光效果
style: Text.Outline
styleColor: "#00D4FF"
}
}
代码详解:
id: timeDisplay // 组件唯一标识
text: Qt.formatTime(...) // 显示内容
font.pixelSize: 120 // 字体大小
font.bold: true // 粗体
color: "#FFFFFF" // 白色
Qt.formatTime(new Date(), "hh:mm:ss")
new Date():获取当前时间"hh:mm:ss":格式化为 时:分:秒hh:24 小时制的小时(00-23)mm:分钟(00-59)ss:秒(00-59)style: Text.Outline // 描边样式
styleColor: "#00D4FF" // 青色描边
此时应该看到一个大号的白色时间显示,带有青色发光效果。但是,时间是静态的,不会更新。
使用 Timer 让时钟每秒自动更新:
Rectangle {
anchors.fill: parent
Text {
id: timeDisplay
// ... 时钟显示代码
}
// 添加定时器
Timer {
interval: 1000 // 1000毫秒 = 1秒
running: true // 自动启动
repeat: true // 重复执行
onTriggered: {
// 每秒执行一次
var currentTime = new Date()
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
}
}
}
Timer 组件详解:
属性 | 说明 | 取值 |
|---|---|---|
interval | 触发间隔(毫秒) | 1000 = 1 秒 |
running | 是否运行 | true/false |
repeat | 是否重复 | true = 持续触发 |
onTriggered | 触发时的回调 | JavaScript 代码 |
为什么需要 var currentTime?
// 每次获取最新时间
var currentTime = new Date()
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
这样可以确保每秒都获取系统的最新时间,而不是使用旧的时间对象。
现在时钟应该每秒自动更新了!🎉
使用 Column 组织时钟和日期:
Column {
anchors.centerIn: parent
spacing: 40 // 子元素间距
// 时钟显示
Text {
id: timeDisplay
text: Qt.formatTime(new Date(), "hh:mm:ss")
font.pixelSize: 120
font.bold: true
color: "#FFFFFF"
anchors.horizontalCenter: parent.horizontalCenter
style: Text.Outline
styleColor: "#00D4FF"
}
// 日期显示
Text {
id: dateDisplay
text: Qt.formatDate(new Date(), "yyyy年MM月dd日")
font.pixelSize: 36
color: "#B0E0E6"
anchors.horizontalCenter: parent.horizontalCenter
}
}
Qt.formatDate() 格式说明:
格式符 | 说明 | 示例 |
|---|---|---|
yyyy | 4 位年份 | 2025 |
MM | 2 位月份 | 01-12 |
dd | 2 位日期 | 01-31 |
年月日 | 中文字符 | 直接显示 |
完整示例:
Qt.formatDate(new Date(), "yyyy年MM月dd日")
// 输出:2025年11月06日
在 Timer 的 onTriggered 中同时更新日期:
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var currentTime = new Date()
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
dateDisplay.text = Qt.formatDate(currentTime, "yyyy年MM月dd日")
}
}
在 Qt 开发中,我们可以选择使用 JavaScript 或 C++ 来实现业务逻辑。对于星期显示功能,使用 C++ 有以下优势:
特性 | JavaScript | C++ |
|---|---|---|
性能 | 较慢 | 快速 |
类型安全 | 弱类型 | 强类型 |
代码复用 | 仅 QML | 可跨平台复用 |
调试 | 较难 | 工具完善 |
维护性 | 中等 | 更好 |
本教程选择 C++ 来展示如何将 C++ 逻辑暴露给 QML,这是 Qt 开发的最佳实践之一。
首先在 entry/src/main/cpp/ 目录下创建 DateHelper.h:
// DateHelper.h
#ifndef DATEHELPER_H
#define DATEHELPER_H
#include <QObject>
#include <QDateTime>
#include <QString>
class DateHelper : public QObject
{
Q_OBJECT
public:
explicit DateHelper(QObject *parent = nullptr);
// Q_INVOKABLE 使方法可以在 QML 中调用
Q_INVOKABLE QString getWeekDay();
Q_INVOKABLE QString getWeekDayEnglish();
Q_INVOKABLE int getWeekDayNumber();
signals:
public slots:
};
#endif // DATEHELPER_H
代码详解:
Q_OBJECT
Q_INVOKABLE QString getWeekDay();
public slots方法 | 返回值 | 说明 |
|---|---|---|
getWeekDay() | QString | 中文星期(星期一) |
getWeekDayEnglish() | QString | 英文星期(Monday) |
getWeekDayNumber() | int | 数字(1-7) |
创建 DateHelper.cpp 实现文件:
// DateHelper.cpp
#include "DateHelper.h"
DateHelper::DateHelper(QObject *parent)
: QObject(parent)
{
}
// 获取中文星期
QString DateHelper::getWeekDay()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
int dayOfWeek = currentDateTime.date().dayOfWeek();
QStringList weekDays;
weekDays << "星期一" << "星期二" << "星期三" << "星期四"
<< "星期五" << "星期六" << "星期日";
// Qt 的 dayOfWeek() 返回 1-7 (1=Monday, 7=Sunday)
return weekDays[dayOfWeek - 1];
}
// 获取英文星期
QString DateHelper::getWeekDayEnglish()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
return currentDateTime.toString("dddd");
}
// 获取星期数字 (1-7)
int DateHelper::getWeekDayNumber()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
return currentDateTime.date().dayOfWeek();
}
代码详解:
C++ 方式:
QDateTime currentDateTime = QDateTime::currentDateTime();
int day = currentDateTime.date().dayOfWeek(); // 1-7
JavaScript 方式:
var date = new Date();
var day = date.getDay(); // 0-6 (0=Sunday)
关键区别:
QStringList weekDays;
weekDays << "星期一" << "星期二" << "星期三";
<< 操作符:追加元素currentDateTime.toString("dddd")
dddd:完整星期名称(Monday)ddd:缩写星期名称(Mon)在 entry/src/main/cpp/CMakeLists.txt 中添加新文件:
# 添加 DateHelper.cpp 和 DateHelper.h
add_library(entry SHARED
main.cpp
DateHelper.cpp
DateHelper.h
qml.qrc
)
完整配置:
cmake_minimum_required(VERSION 3.5.0)
project(qtdemo)
set(CMAKE_AUTOMOC ON) # 自动处理 MOC (元对象编译器)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# ... 其他配置 ...
# 添加源文件
add_library(entry SHARED
main.cpp
DateHelper.cpp
DateHelper.h
qml.qrc
)
# 链接 Qt 库
target_link_libraries(entry PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Qml
Qt${QT_VERSION_MAJOR}::Quick
# ... 其他库 ...
)
CMAKE_AUTOMOC 说明:
修改 main.cpp,将 DateHelper 暴露给 QML:
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "DateHelper.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 创建 DateHelper 实例
DateHelper dateHelper;
// 将 C++ 对象注册到 QML 上下文
engine.rootContext()->setContextProperty("dateHelper", &dateHelper);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
代码详解:
engine.rootContext()->setContextProperty("dateHelper", &dateHelper);
参数 | 说明 |
|---|---|
第一个参数 | QML 中的对象名称(字符串) |
第二个参数 | C++ 对象的指针 |
效果:
dateHelperDateHelper dateHelper; // 栈对象
engine.rootContext()->setContextProperty("dateHelper", &dateHelper);
注意:
dateHelper 必须在整个应用生命周期内有效main() 函数结束前一直有效DateHelper *dateHelper = new DateHelper();修改 main.qml,使用 C++ 实现的星期功能:
Column {
anchors.centerIn: parent
spacing: 40
// 时钟显示
Text {
id: timeDisplay
text: Qt.formatTime(new Date(), "hh:mm:ss")
font.pixelSize: 120
font.bold: true
color: "#FFFFFF"
anchors.horizontalCenter: parent.horizontalCenter
style: Text.Outline
styleColor: "#00D4FF"
}
// 日期和星期
Column {
spacing: 10
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: dateDisplay
text: Qt.formatDate(new Date(), "yyyy年MM月dd日")
font.pixelSize: 36
color: "#B0E0E6"
anchors.horizontalCenter: parent.horizontalCenter
}
// 使用 C++ 实现的星期显示
Text {
id: weekDisplay
text: dateHelper.getWeekDay() // 调用 C++ 方法
font.pixelSize: 28
color: "#87CEEB"
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
关键点:
text: dateHelper.getWeekDay()
dateHelper:在 C++ 中注册的对象名getWeekDay():C++ 中的 Q_INVOKABLE 方法Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var currentTime = new Date()
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
dateDisplay.text = Qt.formatDate(currentTime, "yyyy年MM月dd日")
weekDisplay.text = dateHelper.getWeekDay() // 调用 C++ 方法
}
}
Rectangle {
// JavaScript 函数
function getWeekDay() {
var days = ["星期日", "星期一", "星期二", "星期三",
"星期四", "星期五", "星期六"]
return days[new Date().getDay()]
}
Text {
text: getWeekDay() // 调用 QML 函数
}
}
// DateHelper.cpp
QString DateHelper::getWeekDay() {
QDateTime currentDateTime = QDateTime::currentDateTime();
int dayOfWeek = currentDateTime.date().dayOfWeek();
QStringList weekDays;
weekDays << "星期一" << "星期二" << "星期三" << "星期四"
<< "星期五" << "星期六" << "星期日";
return weekDays[dayOfWeek - 1];
}
// main.qml
Text {
text: dateHelper.getWeekDay() // 调用 C++ 方法
}
方面 | JavaScript | C++ |
|---|---|---|
性能 | 解释执行 | 编译执行,快 10-100 倍 |
类型安全 | 运行时检查 | 编译时检查 |
代码复用 | 仅 QML 使用 | 可在多个地方复用 |
单元测试 | 困难 | 容易 |
调试 | 有限 | 完善的工具 |
维护性 | 对大型项目较难 | 结构清晰 |
Text {
text: dateHelper.getWeekDayEnglish() // Monday
font.pixelSize: 24
color: "#87CEEB"
}
Text {
id: weekDisplay
text: dateHelper.getWeekDay()
font.pixelSize: 28
// 周末显示红色
color: {
var dayNum = dateHelper.getWeekDayNumber()
return (dayNum === 6 || dayNum === 7) ? "#FF6B6B" : "#87CEEB"
}
}
Rectangle {
property bool showEnglish: false
Text {
text: showEnglish ?
dateHelper.getWeekDayEnglish() :
dateHelper.getWeekDay()
}
MouseArea {
anchors.fill: parent
onDoubleClicked: {
parent.showEnglish = !parent.showEnglish
}
}
}
现在我们已经实现了核心功能(时钟、日期、星期),让我们添加一些装饰元素让界面更精致。
在星期和品牌信息之间添加装饰线:
Column {
anchors.centerIn: parent
spacing: 40
// 时钟、日期、星期...
// 装饰性分隔线
Rectangle {
width: 300
height: 2
color: "#00D4FF"
radius: 1
anchors.horizontalCenter: parent.horizontalCenter
opacity: 0.6
}
// 品牌信息
Text {
text: "坚果派 × Qt for 鸿蒙"
font.pixelSize: 24
color: "#FFFFFF"
opacity: 0.7
anchors.horizontalCenter: parent.horizontalCenter
}
}
装饰线技巧:
width: 300:宽度height: 2:高度(很细)radius: 1:轻微圆角opacity: 0.6:60% 透明度,更柔和在 Rectangle 中添加状态变量:
Rectangle {
anchors.fill: parent
// 状态标志
property bool is24Hour: true
// ... UI 组件
}
Rectangle {
anchors.fill: parent
property bool is24Hour: true
// ... UI 组件
// 点击切换
MouseArea {
anchors.fill: parent
onClicked: {
// 切换状态
parent.is24Hour = !parent.is24Hour
var currentTime = new Date()
if (parent.is24Hour) {
// 24小时制:00:00:00
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
} else {
// 12小时制:1:00:00 AM
timeDisplay.text = Qt.formatTime(currentTime, "h:mm:ss AP")
}
}
}
}
时间格式对比:
格式 | 说明 | 示例 |
|---|---|---|
hh:mm:ss | 24 小时制 | 13:30:45 |
h:mm:ss AP | 12 小时制 | 1:30:45 PM |
格式说明:
hh:两位数小时(00-23)h:一位或两位小时(1-12)AP:AM/PM 标识Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var currentTime = new Date()
// 根据当前模式更新时间
if (parent.is24Hour) {
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
} else {
timeDisplay.text = Qt.formatTime(currentTime, "h:mm:ss AP")
}
// 更新日期和星期
dateDisplay.text = Qt.formatDate(currentTime, "yyyy年MM月dd日")
weekDisplay.text = getWeekDay()
}
}
entry/src/main/cpp/
├── main.cpp # C++ 入口文件,注册 DateHelper
├── main.qml # QML 界面文件
├── DateHelper.h # C++ 头文件,定义 DateHelper 类
├── DateHelper.cpp # C++ 实现文件
├── CMakeLists.txt # 构建配置文件
└── qml.qrc # QML 资源文件
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSurfaceFormat>
#include <QQmlContext>
#include "DateHelper.h"
int main(int argc, char *argv[])
{
// 配置 OpenGL ES 表面格式
QSurfaceFormat format;
format.setAlphaBufferSize(8); // 确保 Alpha 通道位数
format.setBlueBufferSize(8); // 蓝色通道位数
format.setGreenBufferSize(8); // 绿色通道位数
format.setRedBufferSize(8); // 红色通道位数
format.setDepthBufferSize(24); // 深度缓冲区位数
format.setStencilBufferSize(8); // 模板缓冲区位数
format.setRenderableType(QSurfaceFormat::OpenGLES); // 使用 OpenGL ES
format.setVersion(3, 0); // OpenGL ES 3.0
QSurfaceFormat::setDefaultFormat(format);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 创建 DateHelper 实例并将其暴露给 QML
DateHelper dateHelper;
engine.rootContext()->setContextProperty("dateHelper", &dateHelper);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
// DateHelper.h
#ifndef DATEHELPER_H
#define DATEHELPER_H
#include <QObject>
#include <QDateTime>
#include <QString>
class DateHelper : public QObject
{
Q_OBJECT
public:
explicit DateHelper(QObject *parent = nullptr);
// Q_INVOKABLE 使方法可以在 QML 中调用
Q_INVOKABLE QString getWeekDay();
Q_INVOKABLE QString getWeekDayEnglish();
Q_INVOKABLE int getWeekDayNumber();
signals:
public slots:
};
#endif // DATEHELPER_H
// DateHelper.cpp
#include "DateHelper.h"
DateHelper::DateHelper(QObject *parent)
: QObject(parent)
{
}
// 获取中文星期
QString DateHelper::getWeekDay()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
int dayOfWeek = currentDateTime.date().dayOfWeek();
QStringList weekDays;
weekDays << "星期一" << "星期二" << "星期三" << "星期四"
<< "星期五" << "星期六" << "星期日";
// Qt 的 dayOfWeek() 返回 1-7 (1=Monday, 7=Sunday)
return weekDays[dayOfWeek - 1];
}
// 获取英文星期
QString DateHelper::getWeekDayEnglish()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
return currentDateTime.toString("dddd");
}
// 获取星期数字 (1-7)
int DateHelper::getWeekDayNumber()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
return currentDateTime.date().dayOfWeek();
}
// main.qml - 时钟应用
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 800
height: 1200
visible: true
title: "Qt 时钟 for 鸿蒙"
// 渐变背景
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#0F2027" }
GradientStop { position: 0.5; color: "#203A43" }
GradientStop { position: 1.0; color: "#2C5364" }
}
Column {
anchors.centerIn: parent
spacing: 40
// 数字时钟显示
Text {
id: timeDisplay
text: Qt.formatTime(new Date(), "hh:mm:ss")
font.pixelSize: 120
font.bold: true
font.family: "Arial"
color: "#FFFFFF"
anchors.horizontalCenter: parent.horizontalCenter
// 发光效果
style: Text.Outline
styleColor: "#00D4FF"
}
// 日期显示
Column {
spacing: 10
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: dateDisplay
text: Qt.formatDate(new Date(), "yyyy年MM月dd日")
font.pixelSize: 36
color: "#B0E0E6"
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
id: weekDisplay
text: dateHelper.getWeekDay() // 使用 C++ 实现
font.pixelSize: 28
color: "#87CEEB"
anchors.horizontalCenter: parent.horizontalCenter
}
}
// 装饰性分隔线
Rectangle {
width: 300
height: 2
color: "#00D4FF"
radius: 1
anchors.horizontalCenter: parent.horizontalCenter
opacity: 0.6
}
// 品牌信息
Text {
text: "坚果派 × Qt for 鸿蒙"
font.pixelSize: 24
color: "#FFFFFF"
opacity: 0.7
anchors.horizontalCenter: parent.horizontalCenter
}
}
// 定时器 - 每秒更新
Timer {
interval: 1000 // 1秒
running: true
repeat: true
onTriggered: {
var currentTime = new Date()
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
dateDisplay.text = Qt.formatDate(currentTime, "yyyy年MM月dd日")
weekDisplay.text = dateHelper.getWeekDay() // 使用 C++ 实现
}
}
// 点击切换12/24小时制
property bool is24Hour: true
MouseArea {
anchors.fill: parent
onClicked: {
parent.is24Hour = !parent.is24Hour
var currentTime = new Date()
if (parent.is24Hour) {
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
} else {
timeDisplay.text = Qt.formatTime(currentTime, "h:mm:ss AP")
}
}
}
// 原来的 JavaScript getWeekDay() 函数已移至 C++ DateHelper 类
// 现在通过 dateHelper.getWeekDay() 调用 C++ 实现
}
}
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(qtdemo)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND CMAKE_FIND_ROOT_PATH ${QT_PREFIX})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS
Concurrent Gui Network Qml Quick QuickControls2
Widgets QuickTemplates2 QmlWorkerScript)
add_library(entry SHARED
main.cpp
DateHelper.cpp
DateHelper.h
qml.qrc
)
target_link_libraries(entry PRIVATE
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Qml
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::QuickControls2
Qt${QT_VERSION_MAJOR}::QuickTemplates2
Qt${QT_VERSION_MAJOR}::QmlWorkerScript
Qt${QT_VERSION_MAJOR}::QOpenHarmonyPlatformIntegrationPlugin
)
Column {
anchors.centerIn: parent // 居中对齐
spacing: 40 // 子元素间距
}
// 获取当前时间
var now = new Date()
// 格式化时间
Qt.formatTime(now, "hh:mm:ss")
// 格式化日期
Qt.formatDate(now, "yyyy年MM月dd日")
// 获取星期(0-6)
now.getDay()
// 获取当前时间
QDateTime currentDateTime = QDateTime::currentDateTime();
// 获取星期(1-7)
int dayOfWeek = currentDateTime.date().dayOfWeek();
// 格式化
QString formatted = currentDateTime.toString("yyyy-MM-dd");
Timer {
interval: 1000 // 间隔(毫秒)
running: true // 是否运行
repeat: true // 是否重复
onTriggered: {} // 触发回调
}
class DateHelper : public QObject
{
Q_OBJECT // 必须添加
public:
Q_INVOKABLE QString getWeekDay(); // 可在 QML 中调用
};
// main.cpp
DateHelper dateHelper;
engine.rootContext()->setContextProperty("dateHelper", &dateHelper);
Text {
text: dateHelper.getWeekDay() // 调用 C++ 方法
}
property bool is24Hour: true
text: is24Hour ? "24小时制" : "12小时制"
set(CMAKE_AUTOMOC ON) # 自动运行 MOC
add_library(entry SHARED
main.cpp
DateHelper.cpp
DateHelper.h
)
QSurfaceFormat format;
format.setAlphaBufferSize(8);
format.setRenderableType(QSurfaceFormat::OpenGLES);
format.setVersion(3, 0);
QSurfaceFormat::setDefaultFormat(format);
Text {
id: timeDisplay
Behavior on text {
NumberAnimation {
duration: 300
easing.type: Easing.InOutQuad
}
}
}
property var backgrounds: [
["#0F2027", "#203A43", "#2C5364"],
["#834d9b", "#d04ed6", "#f2709c"],
["#000428", "#004e92", "#004e92"]
]
MouseArea {
onDoubleClicked: {
// 切换背景
}
}
property string alarmTime: "07:00:00"
Timer {
onTriggered: {
if (timeDisplay.text === alarmTime) {
// 触发闹钟
console.log("闹钟响了!")
}
}
}
function getTimeZone(offset) {
var now = new Date()
var utc = now.getTime() + (now.getTimezoneOffset() * 60000)
var newTime = new Date(utc + (3600000 * offset))
return Qt.formatTime(newTime, "hh:mm:ss")
}
Text {
text: "纽约: " + getTimeZone(-5)
}
鸿蒙 PC 是鸿蒙生态的重要组成部分,将应用适配到 PC 平台可以:
特性 | 手机/平板 | 鸿蒙 PC |
|---|---|---|
窗口大小 | 固定全屏 | 可调整窗口 |
交互方式 | 触摸 | 鼠标+键盘 |
分辨率 | 较低 | 较高 |
多任务 | 有限 | 完全支持 |
对于 PC 应用,我们需要设置合适的窗口尺寸:
Window {
// 根据平台自动调整窗口大小
width: Qt.platform.os === "ohos" ? 480 : 800
height: Qt.platform.os === "ohos" ? 800 : 600
visible: true
title: "Qt 时钟 for 鸿蒙"
// PC 特性:允许调整窗口大小
minimumWidth: 400
minimumHeight: 500
maximumWidth: 1200
maximumHeight: 900
}
配置说明:
minimumWidth/Height:窗口最小尺寸maximumWidth/Height:窗口最大尺寸使窗口大小变化时,界面自动适配:
Window {
id: mainWindow
Rectangle {
anchors.fill: parent
Column {
anchors.centerIn: parent
spacing: mainWindow.height * 0.03 // 根据窗口高度动态计算
// 时钟显示 - 字体大小自适应
Text {
id: timeDisplay
text: Qt.formatTime(new Date(), "hh:mm:ss")
font.pixelSize: Math.min(mainWindow.width, mainWindow.height) * 0.15
font.bold: true
color: "#FFFFFF"
anchors.horizontalCenter: parent.horizontalCenter
style: Text.Outline
styleColor: "#00D4FF"
}
// 日期显示 - 自适应字体
Column {
spacing: 10
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: dateDisplay
text: Qt.formatDate(new Date(), "yyyy年MM月dd日")
font.pixelSize: mainWindow.width * 0.045
color: "#B0E0E6"
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
id: weekDisplay
text: getWeekDay()
font.pixelSize: mainWindow.width * 0.035
color: "#87CEEB"
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
}
响应式设计要点:
mainWindow.width/height 动态计算尺寸在 PC 上,添加键盘快捷键提升用户体验:
Window {
id: mainWindow
Rectangle {
anchors.fill: parent
// 启用键盘事件
focus: true
// 键盘事件处理
Keys.onPressed: {
if (event.key === Qt.Key_Space) {
// 空格键:切换12/24小时制
is24Hour = !is24Hour
updateTimeDisplay()
event.accepted = true
}
else if (event.key === Qt.Key_F11) {
// F11:全屏切换
if (mainWindow.visibility === Window.FullScreen) {
mainWindow.visibility = Window.Windowed
} else {
mainWindow.visibility = Window.FullScreen
}
event.accepted = true
}
else if (event.key === Qt.Key_Escape) {
// ESC:退出全屏
if (mainWindow.visibility === Window.FullScreen) {
mainWindow.visibility = Window.Windowed
}
event.accepted = true
}
else if (event.key === Qt.Key_Q && event.modifiers & Qt.ControlModifier) {
// Ctrl+Q:退出应用
Qt.quit()
event.accepted = true
}
}
// 辅助函数:更新时间显示
function updateTimeDisplay() {
var currentTime = new Date()
if (is24Hour) {
timeDisplay.text = Qt.formatTime(currentTime, "hh:mm:ss")
} else {
timeDisplay.text = Qt.formatTime(currentTime, "h:mm:ss AP")
}
}
}
}
快捷键列表:
快捷键 | 功能 |
|---|---|
空格 | 切换 12/24 小时制 |
F11 | 全屏/窗口模式切换 |
ESC | 退出全屏模式 |
Ctrl+Q | 退出应用 |
PC 用户习惯使用右键菜单:
Rectangle {
anchors.fill: parent
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
// 左键:切换时间格式
parent.is24Hour = !parent.is24Hour
updateTimeDisplay()
}
else if (mouse.button === Qt.RightButton) {
// 右键:显示上下文菜单
contextMenu.popup()
}
}
}
// 上下文菜单
Menu {
id: contextMenu
MenuItem {
text: is24Hour ? "切换到12小时制" : "切换到24小时制"
onTriggered: {
is24Hour = !is24Hour
updateTimeDisplay()
}
}
MenuSeparator {}
MenuItem {
text: "全屏显示"
onTriggered: {
mainWindow.visibility = Window.FullScreen
}
}
MenuItem {
text: "关于"
onTriggered: {
aboutDialog.open()
}
}
MenuSeparator {}
MenuItem {
text: "退出"
onTriggered: {
Qt.quit()
}
}
}
}
注意:需要添加 import QtQuick.Controls 2.15 来使用 Menu 组件。
为 PC 应用添加传统的菜单栏:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
id: mainWindow
width: 800
height: 600
visible: true
title: "Qt 时钟 for 鸿蒙 PC"
// 菜单栏
menuBar: MenuBar {
Menu {
title: "文件(&F)"
MenuItem {
text: "退出(&Q)"
shortcut: "Ctrl+Q"
onTriggered: Qt.quit()
}
}
Menu {
title: "视图(&V)"
MenuItem {
text: "全屏(&F)"
shortcut: "F11"
checkable: true
checked: mainWindow.visibility === Window.FullScreen
onTriggered: {
if (checked) {
mainWindow.visibility = Window.FullScreen
} else {
mainWindow.visibility = Window.Windowed
}
}
}
MenuSeparator {}
MenuItem {
text: is24Hour ? "12小时制(&1)" : "24小时制(&2)"
shortcut: "Space"
onTriggered: {
is24Hour = !is24Hour
updateTimeDisplay()
}
}
}
Menu {
title: "帮助(&H)"
MenuItem {
text: "关于(&A)"
onTriggered: {
aboutDialog.open()
}
}
}
}
// 主内容区域
Rectangle {
anchors.fill: parent
// ... 时钟界面代码
}
}
根据不同平台显示不同的提示:
Text {
text: {
if (Qt.platform.os === "windows") {
return "坚果派 × Qt for 鸿蒙 PC (Windows)"
} else if (Qt.platform.os === "linux") {
return "坚果派 × Qt for 鸿蒙 PC (Linux)"
} else if (Qt.platform.os === "ohos") {
return "坚果派 × Qt for 鸿蒙"
} else {
return "坚果派 × Qt"
}
}
font.pixelSize: 24
color: "#FFFFFF"
opacity: 0.7
anchors.horizontalCenter: parent.horizontalCenter
}
在 PC 上添加系统托盘图标:
import QtQuick 2.15
import QtQuick.Window 2.15
import Qt.labs.platform 1.1
ApplicationWindow {
id: mainWindow
// 系统托盘图标
SystemTrayIcon {
id: systemTray
visible: true
icon.source: "qrc:/icons/clock.png"
tooltip: "鸿蒙时钟"
menu: Menu {
MenuItem {
text: "显示窗口"
onTriggered: {
mainWindow.show()
mainWindow.raise()
}
}
MenuItem {
text: "隐藏窗口"
onTriggered: mainWindow.hide()
}
MenuSeparator {}
MenuItem {
text: "退出"
onTriggered: Qt.quit()
}
}
onActivated: {
if (reason === SystemTrayIcon.Trigger) {
// 单击托盘图标
if (mainWindow.visible) {
mainWindow.hide()
} else {
mainWindow.show()
mainWindow.raise()
}
}
}
}
}
在将应用发布到鸿蒙 PC 前,请确认:
Window {
width: 800
height: 1200
visible: true
Rectangle {
anchors.fill: parent
// 固定布局
}
}
ApplicationWindow {
width: 800
height: 600
minimumWidth: 400
minimumHeight: 500
visible: true
// 菜单栏
menuBar: MenuBar { /* ... */ }
Rectangle {
anchors.fill: parent
focus: true // 接收键盘事件
// 响应式布局
Keys.onPressed: { /* 键盘处理 */ }
}
// 系统托盘
SystemTrayIcon { /* ... */ }
}
功能 | 移动端 | PC 端 |
|---|---|---|
基础时钟显示 | ✅ | ✅ |
触摸交互 | ✅ | ✅ |
窗口调整 | ❌ | ✅ |
键盘快捷键 | ❌ | ✅ |
菜单栏 | ❌ | ✅ |
右键菜单 | ❌ | ✅ |
系统托盘 | ❌ | ✅ |
全屏模式 | 默认 | ✅ |
症状:时钟显示静态时间
原因:Timer 未启动或 running 设置为 false
解决:
Timer {
running: true // 确保为 true
repeat: true // 确保为 true
}
症状:日期显示为英文或格式不对
原因:格式字符串错误
解决:
// 正确的中文格式
Qt.formatDate(new Date(), "yyyy年MM月dd日")
// 注意大小写
// MM - 月份(01-12)
// dd - 日期(01-31)
症状:星期始终显示相同或显示错误
原因:getDay() 返回值使用错误
解决:
// getDay() 返回 0-6
// 0 = 星期日,不是星期一!
var days = [
"星期日", // 0
"星期一", // 1
// ...
]
症状:点击屏幕无法切换 12/24 小时制
原因:MouseArea 被其他组件遮挡
解决:
// 确保 MouseArea 在最后
Rectangle {
// UI 组件...
MouseArea {
anchors.fill: parent
onClicked: { /* ... */ }
}
}
Timer {
onTriggered: {
var currentTime = new Date()
var newTimeText = Qt.formatTime(currentTime, "hh:mm:ss")
// 只在时间改变时更新
if (timeDisplay.text !== newTimeText) {
timeDisplay.text = newTimeText
}
}
}
property string cachedDate: ""
Timer {
onTriggered: {
var newDate = Qt.formatDate(new Date(), "yyyy年MM月dd日")
// 日期通常不会每秒改变
if (cachedDate !== newDate) {
cachedDate = newDate
dateDisplay.text = newDate
}
}
}
使用 Timer 进行实时更新
Timer { interval: 1000; running: true; repeat: true }
为组件设置有意义的 id
Text { id: timeDisplay } // 清晰明了
使用函数封装重复逻辑
function getWeekDay() { /* ... */ }
添加用户交互反馈
MouseArea {
onClicked: { /* 提供视觉或音频反馈 */ }
}
使用 property 管理状态
property bool is24Hour: true
硬编码所有值
// 不好
Text { text: "19:30:45" }
// 好
Text { text: Qt.formatTime(new Date(), "hh:mm:ss") }
在 Timer 中执行复杂计算
// Timer 频繁触发,避免复杂操作
onTriggered: {
// 简单的更新操作
}
忘记清理资源
Component.onDestruction: {
// 如有需要,清理定时器等资源
}
通过本教程,我们从零开始构建了一个功能完整的时钟应用,涵盖了:
移动端功能扩展:
PC 端功能扩展:
跨平台特性:
继续探索,创造更多精彩的应用!🚀
本教程的时钟应用展示了鸿蒙全场景开发的特点:
一套代码 → 多端运行
📱 手机端 ←→ 📱 平板端 ←→ 💻 PC端
触摸 触摸+鼠标 鼠标+键盘
竖屏 横屏/竖屏 大屏
这正是鸿蒙生态的优势所在!
文档版本:v1.0
最后更新:2025-11-06
作者:坚果派
难度等级:⭐⭐⭐ (中级)
预计学习时间:2-3 小时
标签:QtQMLHarmonyOS时钟实战教程
参考资料
[1]
Qt QML Timer: https://doc.qt.io/qt-5/qml-qtqml-timer.html
[2]
Qt Global Object: https://doc.qt.io/qt-5/qml-qtqml-qt.html
[3]
QML Date Functions: https://doc.qt.io/qt-5/qml-date.html
[4]
Q_INVOKABLE Macro: https://doc.qt.io/qt-5/qobject.html#Q_INVOKABLE
[5]
QQmlContext: https://doc.qt.io/qt-5/qqmlcontext.html
[6]
QDateTime: https://doc.qt.io/qt-5/qdatetime.html
[7]
Qt MOC: https://doc.qt.io/qt-5/moc.html
[8]
Integrating QML and C++: https://doc.qt.io/qt-5/qtqml-cppintegration-overview.html
[9]
Exposing C++ Objects to QML: https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html