Qt图形界面开发

下面使用 Qt Quick 技术创建一个日历程序

环境准备

sudo apt update -y
sudo apt install -y qt6-base-dev qt6-declarative-dev qt6-multimedia-dev \
     qmake6 libgstreamer1.0-dev libjpeg62-turbo-dev libjpeg62-turbo-dev \
     libswscale-dev gcc g++ cmake make

创建项目文件

新建calendar.pro文件

QT += quick
SOURCES += main.cpp
RESOURCES += qml.qrc

创建资源文件

新建qml.qrc文件

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
</RCC>

创建qml文件

新建main.qml文件

import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    id: mainWindow
    visible: true
    title: "日历"

    Rectangle {
        id: calendarRoot
        anchors.fill: parent
        color: "#eeeeee"
        property real sideMargin: width * 0.04  // 左右边距为宽度的2%
        property date currentDate: new Date()
        property int currentMonth: currentDate.getMonth()
        property int currentYear: currentDate.getFullYear()
        property date selectedDate: currentDate

        ColumnLayout {
            anchors.fill: parent
            anchors.margins: calendarRoot.sideMargin  // 应用整体边距
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter

            // 标题栏
            RowLayout {
                Layout.preferredHeight: 40
                Layout.fillWidth: true

                Button {
                    text: "<"
                    onClicked: {
                        calendarRoot.currentMonth--
                        if (calendarRoot.currentMonth < 0) {
                            calendarRoot.currentMonth = 11
                            calendarRoot.currentYear--
                        }
                        calendarGrid.updateCalendar()
                    }
                }

                Label {
                    text: calendarRoot.currentYear + "年 " + (calendarRoot.currentMonth + 1) + "月"
                    font.pointSize: 16
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    Layout.fillWidth: true
                }

                Button {
                    text: ">"
                    onClicked: {
                        calendarRoot.currentMonth++
                        if (calendarRoot.currentMonth > 11) {
                            calendarRoot.currentMonth = 0
                            calendarRoot.currentYear++
                        }
                        calendarGrid.updateCalendar()
                    }
                }
            }

            Item {
                Layout.preferredHeight: 20  // 高度越大,日历网格下移越多
                Layout.fillWidth: true
            }
            // 星期标题
            GridView {
                id: weekHeader
                model: 7
                // 计算单元格宽度(扣除左右边距后平均分配)
                cellWidth: (parent.width - 2 * calendarRoot.sideMargin) / 7
                cellHeight: 30
                Layout.preferredHeight: 30
                Layout.fillWidth: true

                delegate: Rectangle {
                    color: "#e0e0e0"
                    border.color: "#cccccc"
                    border.width: 1

                    Text {
                        anchors.centerIn: parent
                        text: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][index]
                        font.bold: true
                        font.pointSize: 18
                        color: index === 0 || index === 6 ? "#ff6666" : "#333333"
                    }
                }
            }

            Item {
                Layout.preferredHeight: 20  // 高度越大,日历网格下移越多
                Layout.fillWidth: true
            }
            // 日历网格
            GridView {
                id: calendarGrid
                model: 42
                cellWidth: (parent.width - 2 * calendarRoot.sideMargin) / 7
                cellHeight: 55
                Layout.fillWidth: true
                Layout.fillHeight: true

                property var calendarData: createCalendarModel()
                property int firstDayOfMonth: new Date(calendarRoot.currentYear, calendarRoot.currentMonth, 1).getDay()

                function createCalendarModel() {
                    var daysInMonth = new Date(calendarRoot.currentYear, calendarRoot.currentMonth + 1, 0).getDate()
                    var firstDay = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, 1).getDay()

                    var days = []
                    // 填充上个月的末尾几天
                    for (var i = 0; i < firstDay; i++) {
                        var prevMonthDays = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, 0).getDate()
                        days.push({
                            day: prevMonthDays - firstDay + i + 1,
                            isCurrentMonth: false,
                            isSelected: false,
                            isToday: false
                        })
                    }

                    // 填充当前月的天数
                    for (var i = 1; i <= daysInMonth; i++) {
                        var isToday = (i === calendarRoot.currentDate.getDate() &&
                        calendarRoot.currentMonth === calendarRoot.currentDate.getMonth() &&
                        calendarRoot.currentYear === calendarRoot.currentDate.getFullYear())
                        var isSelected = (i === calendarRoot.selectedDate.getDate() &&
                        calendarRoot.currentMonth === calendarRoot.selectedDate.getMonth() &&
                        calendarRoot.currentYear === calendarRoot.selectedDate.getFullYear())
                        days.push({
                            day: i,
                            isCurrentMonth: true,
                            isSelected: isSelected,
                            isToday: isToday
                        })
                    }

                    // 填充下个月的开头几天
                    var remainingCells = 42 - days.length
                    for (var i = 1; i <= remainingCells; i++) {
                        days.push({
                            day: i,
                            isCurrentMonth: false,
                            isSelected: false,
                            isToday: false
                        })
                    }

                    return days
                }

                function updateCalendar() {
                    calendarData = createCalendarModel()
                    firstDayOfMonth = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, 1).getDay()
                    calendarGrid.update()
                }

                delegate: Rectangle {
                    border.color: "#cccccc"
                    border.width: 1
                    color: {
                        if (calendarGrid.calendarData[index].isSelected) return "#4a86e8"
                        if (!calendarGrid.calendarData[index].isCurrentMonth) return "#f8f8f8"
                        if (calendarGrid.calendarData[index].isToday) return "#d9ead3"
                        return "#ffffff"
                    }

                    Text {
                        anchors.centerIn: parent
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        text: calendarGrid.calendarData[index].day
                        color: {
                            if (!calendarGrid.calendarData[index].isCurrentMonth) return "#dddddd"
                            if ((index % 7 === 0 || index % 7 === 6) && calendarGrid.calendarData[index].isCurrentMonth) return "#ff6666"
                            if (calendarGrid.calendarData[index].isSelected) return "red"
                            return "#000000"
                        }
                        font.pointSize: 14
                        font.bold: {
                            if (calendarGrid.calendarData[index].isSelected) return true
                            return false
                        }
                    }

                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            if (calendarGrid.calendarData[index].isCurrentMonth) {
                                calendarRoot.selectedDate = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, calendarGrid.calendarData[index].day)
                                calendarGrid.updateCalendar()
                            } else if (index < calendarGrid.firstDayOfMonth) {
                                calendarRoot.currentMonth--
                                if (calendarRoot.currentMonth < 0) {
                                    calendarRoot.currentMonth = 11
                                    calendarRoot.currentYear--
                                }
                                calendarRoot.selectedDate = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, calendarGrid.calendarData[index].day)
                                calendarGrid.updateCalendar()
                            } else {
                                calendarRoot.currentMonth++
                                if (calendarRoot.currentMonth > 11) {
                                    calendarRoot.currentMonth = 0
                                    calendarRoot.currentYear++
                                }
                                calendarRoot.selectedDate = new Date(calendarRoot.currentYear, calendarRoot.currentMonth, calendarGrid.calendarData[index].day)
                                calendarGrid.updateCalendar()
                            }
                        }
                    }
                }
            }
        }

        Component.onCompleted: {
            calendarGrid.updateCalendar()
        }
    }

    Component.onCompleted: {
        //mainWindow.visibility = Window.FullScreen;
        width = 900
        height = 600
        flags = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint;
    }
}

编写程序代码

新建main.cpp文件

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QString>

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("appCurrtentDir", QCoreApplication::applicationDirPath());
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

编译

运行命令:

qmake6 calendar.pro
make

生成可执行文件:calendar

运行

XDG_SESSION_TYPE=wayland ./calendar

运行效果