【开发】利用AI · 纯小白也能编写精美的电脑日历📅组件

💠前言✨


  • 利用目前国内外主流的AI工具,可以编程小程序,比如打包为电脑时间日历小组件,完全不懂代码,也可以成功.
  • 电脑win11系统,安装编程软件:pycharm

💠正文✨


  • 去Github找一个你满意的软件,把网站扔给AI,让他根据你的需求和电脑环境进行代码提供。
  • Pycharm软件,左上角的文件,需要创建新项目,环境为自定义虚拟环境,python 3.14, 如图⬇️:
  • 新建项目
  • 左侧的菜单栏,选择根目录,鼠标右键,点击新建,名称根据项目自定义即可,如图⬇️:
  • 新建py
  • 代码,粘贴到右侧空白区域即可,右上角的绿色▶️按钮为实时展示成品
  • 作者写了一款HTC的翻页桌面大时钟+日历📅的组件,鼠标右键还支持查看每月的日期.
  • 效果如图:
  • clock
  • 先安装依赖包,代码如下:
pip install PyQt6 pystray Pillow borax
  • 代码如下:
import sys
import os
import threading
import calendar
import winreg
from datetime import datetime

from PyQt6.QtWidgets import (
    QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QGridLayout, 
    QPushButton, QFrame, QStackedLayout
)
from PyQt6.QtCore import QTimer, Qt, QPoint, QSettings
from PyQt6.QtGui import QFont, QIcon  # 确保导入 QIcon

import pystray
from PIL import Image, ImageDraw

# --- 0. 打包资源路径获取函数 (解决 EXE 运行图标丢失) ---
def resource_path(relative_path):
    """ 获取程序运行时的绝对路径,兼容开发环境和 PyInstaller 打包环境 """
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

# --- 1. Borax 农历支持 ---
BORAX_AVAILABLE = False
try:
    from borax.calendars.lunardate import LunarDate
    BORAX_AVAILABLE = True
except:
    BORAX_AVAILABLE = False

class HTCFlipClock(QWidget):
    def __init__(self):
        super().__init__()
        self.old_pos = None
        self.is_calendar_mode = False
        self.is_refreshing = False
        self.view_year = datetime.now().year
        self.view_month = datetime.now().month
        
        self.settings = QSettings("MyStudio", "HTC_Clock_Pro")
        self.current_font_family = self.settings.value("font_family", "Arial")

        self.init_ui()
        self.load_settings()
        self.set_auto_start()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.refresh_display)
        self.timer.start(1000)
        self.refresh_display()

    def create_flip_card(self, parent):
        container = QFrame(parent)
        container.setFixedSize(110, 115)
        back = QFrame(container); back.setGeometry(5, 8, 100, 100)
        back.setStyleSheet("background: rgba(255, 255, 255, 15); border-radius: 6px;")
        front = QFrame(container); front.setGeometry(0, 0, 110, 105)
        front.setStyleSheet("background: rgba(255, 255, 255, 22); border-radius: 8px; border: 1px solid rgba(255,255,255,15);")
        lbl = QLabel(front); lbl.setGeometry(0, 0, 110, 105); lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
        lbl.setStyleSheet(f"color: white; font-family: '{self.current_font_family}'; font-size: 80px; font-weight: bold;")
        line = QFrame(front); line.setGeometry(0, 52, 110, 2); line.setStyleSheet("background: rgba(0, 0, 0, 100);")
        return container, lbl

    def init_ui(self):
        # --- 显式设置窗口图标 ---
        icon_path = resource_path("clock.ico")
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        # 移除 Tool 标记以确保在任务栏显示图标 (如果需要任务栏不显示图标,请保留 Tool)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        
        self.main_container = QWidget(self)
        self.main_container.setFixedWidth(280)
        self.main_container.setStyleSheet("background-color: rgba(20, 20, 20, 245); border-radius: 15px;")
        self.layout_total = QVBoxLayout(self.main_container)
        self.layout_total.setContentsMargins(12, 12, 12, 12)

        self.top_bar = QHBoxLayout()
        lbl_s = "font-family: 'Microsoft YaHei'; font-size: 11px; color: #777;"
        self.date_lbl = QLabel(); self.date_lbl.setStyleSheet(lbl_s)
        self.week_lbl = QLabel(); self.week_lbl.setStyleSheet(f"{lbl_s} color: #FF3B30; font-weight: bold;")
        self.lunar_lbl = QLabel(); self.lunar_lbl.setStyleSheet(lbl_s)
        self.top_bar.addWidget(self.date_lbl); self.top_bar.addStretch(); self.top_bar.addWidget(self.week_lbl); self.top_bar.addStretch(); self.top_bar.addWidget(self.lunar_lbl)
        self.layout_total.addLayout(self.top_bar)

        self.view_stack = QStackedLayout()
        
        # 时钟页
        self.page_time = QWidget()
        layout_time = QHBoxLayout(self.page_time)
        layout_time.setContentsMargins(0, 10, 0, 0); layout_time.setSpacing(10)
        self.h_card, self.h_lbl = self.create_flip_card(self.page_time)
        self.m_card, self.m_lbl = self.create_flip_card(self.page_time)
        layout_time.addWidget(self.h_card); layout_time.addWidget(self.m_card)
        
        # 日历页
        self.page_cal = QWidget()
        layout_cal_root = QVBoxLayout(self.page_cal)
        layout_cal_root.setContentsMargins(0, 5, 0, 0)
        
        nav = QHBoxLayout()
        btn_s = "color: #AAA; border: none; font-size: 14px; font-weight: bold; background: transparent;"
        self.btn_year_prev = QPushButton("«", self); self.btn_year_prev.setStyleSheet(btn_s); self.btn_year_prev.setFixedWidth(25)
        self.btn_year_next = QPushButton("»", self); self.btn_year_next.setStyleSheet(btn_s); self.btn_year_next.setFixedWidth(25)
        self.btn_mon_prev = QPushButton("‹", self); self.btn_mon_prev.setStyleSheet(btn_s); self.btn_mon_prev.setFixedWidth(25)
        self.btn_mon_next = QPushButton("›", self); self.btn_mon_next.setStyleSheet(btn_s); self.btn_mon_next.setFixedWidth(25)
        self.cal_title = QLabel(); self.cal_title.setStyleSheet("color: white; font-weight: bold; font-size: 13px;")
        
        nav.addWidget(self.btn_year_prev); nav.addWidget(self.btn_mon_prev); nav.addStretch()
        nav.addWidget(self.cal_title); nav.addStretch(); nav.addWidget(self.btn_mon_next); nav.addWidget(self.btn_year_next)
        layout_cal_root.addLayout(nav)
        
        self.grid_layout = QGridLayout()
        self.grid_layout.setSpacing(1)
        layout_cal_root.addLayout(self.grid_layout)

        self.view_stack.addWidget(self.page_time)
        self.view_stack.addWidget(self.page_cal)
        self.layout_total.addLayout(self.view_stack)

        self.btn_year_prev.clicked.connect(lambda: self.safe_change_view(-1, 0))
        self.btn_year_next.clicked.connect(lambda: self.safe_change_view(1, 0))
        self.btn_mon_prev.clicked.connect(lambda: self.safe_change_view(0, -1))
        self.btn_mon_next.clicked.connect(lambda: self.safe_change_view(0, 1))

        main_vbox = QVBoxLayout(self)
        main_vbox.setContentsMargins(0, 0, 0, 0)
        main_vbox.addWidget(self.main_container)

    # ... [保持之前的 safe_change_view, refresh_calendar_ui, refresh_display 逻辑不变] ...

    def safe_change_view(self, y_delta, m_delta):
        if self.is_refreshing: return
        self.is_refreshing = True
        self.view_year += y_delta
        self.view_month += m_delta
        if self.view_month > 12: self.view_month = 1; self.view_year += 1
        elif self.view_month < 1: self.view_month = 12; self.view_year -= 1
        QTimer.singleShot(5, self.refresh_calendar_ui)

    def refresh_calendar_ui(self):
        try:
            while self.grid_layout.count():
                child = self.grid_layout.takeAt(0)
                if child.widget(): child.widget().deleteLater()
            self.cal_title.setText(f"{self.view_year}年 {self.view_month}月")
            for i, h in enumerate(["一", "二", "三", "四", "五", "六", "日"]):
                lbl = QLabel(h); lbl.setStyleSheet("color: #666; font-size: 10px;"); self.grid_layout.addWidget(lbl, 0, i, Qt.AlignmentFlag.AlignCenter)
            month_days = calendar.monthcalendar(self.view_year, self.view_month)
            now = datetime.now()
            for r, week in enumerate(month_days):
                for c, day in enumerate(week):
                    if day != 0:
                        d_lbl = QLabel(str(day))
                        is_today = (day == now.day and self.view_year == now.year and self.view_month == now.month)
                        d_lbl.setStyleSheet(f"color: {'#FF3B30' if is_today else 'white'}; font-size: 12px; font-weight: {'bold' if is_today else 'normal'};")
                        self.grid_layout.addWidget(d_lbl, r + 1, c, Qt.AlignmentFlag.AlignCenter)
        finally:
            self.is_refreshing = False

    def refresh_display(self):
        now = datetime.now()
        self.date_lbl.setText(now.strftime("%m-%d"))
        self.week_lbl.setText(["周一", "周二", "周三", "周四", "周五", "周六", "周日"][now.weekday()])
        self.lunar_lbl.setText(self.get_lunar_text() if BORAX_AVAILABLE else "未加载Borax")
        self.h_lbl.setText(now.strftime("%H"))
        self.m_lbl.setText(now.strftime("%M"))

    def get_lunar_text(self):
        try:
            today = datetime.now()
            lunar = LunarDate.from_solar_date(today.year, today.month, today.day)
            return lunar.term if lunar.term else f"{['','正月','二月','三月','四月','五月','六月','七月','八月','九月','十月','冬月','腊月'][lunar.month]}{lunar.strftime('%D')}"
        except: return "日期错误"

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.old_pos = event.globalPosition().toPoint()
        elif event.button() == Qt.MouseButton.RightButton:
            self.is_calendar_mode = not self.is_calendar_mode
            if self.is_calendar_mode:
                self.refresh_calendar_ui()
                self.view_stack.setCurrentIndex(1)
            else:
                self.view_stack.setCurrentIndex(0)

    def mouseMoveEvent(self, event):
        if self.old_pos:
            delta = QPoint(event.globalPosition().toPoint() - self.old_pos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.old_pos = event.globalPosition().toPoint()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.settings.setValue("window_pos", self.pos())

    def toggle_on_top(self):
        is_top = bool(self.windowFlags() & Qt.WindowType.WindowStaysOnTopHint)
        if is_top: self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint)
        else: self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint)
        self.show()
        self.settings.setValue("is_top", not is_top)

    def set_auto_start(self):
        try:
            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Run", 0, winreg.KEY_SET_VALUE)
            winreg.SetValueEx(key, "HTC_FlipClock_Ultimate", 0, winreg.REG_SZ, f'"{os.path.realpath(sys.argv[0])}"')
            winreg.CloseKey(key)
        except: pass

    def load_settings(self):
        pos = self.settings.value("window_pos", QPoint(200, 200))
        self.move(pos)
        if self.settings.value("is_top", False, type=bool):
            self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint)

def setup_tray(win):
    img = Image.new('RGBA', (64, 64), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    d.rounded_rectangle([10, 10, 54, 54], radius=12, fill=(255, 255, 255, 240))
    
    # 获取图标路径用于托盘(如果托盘库支持设置图标)
    menu = pystray.Menu(
        pystray.MenuItem("窗口置顶", lambda: win.toggle_on_top(), checked=lambda item: bool(win.windowFlags() & Qt.WindowType.WindowStaysOnTopHint)),
        pystray.MenuItem("退出程序", lambda: os._exit(0))
    )
    pystray.Icon("Clock", img, "HTC Flip Pro", menu).run()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    # --- 重要:在 APP 层也设置一下图标 ---
    app.setWindowIcon(QIcon(resource_path("clock.ico")))
    window = HTCFlipClock()
    window.show()
    threading.Thread(target=setup_tray, args=(window,), daemon=True).start()
    sys.exit(app.exec())
  • 由于需要图标,所以需要将.ico格式的图标命名为clock,放在clock.py的相同目录,进行打包为exe程序.
  • 打包,需要利用PyInstaller依赖包,在下方的终端中,输入命令行:
pip install pyinstaller
  • 开始全部打包📦,引号名称自定义,右侧的英文是你当前项目的名字,命令如下:
python -m PyInstaller --noconsole --onefile --icon=clock.ico --add-data "clock.ico;." --collect-all borax --name "HTC桌面时钟" clock.py
  • 打包📦成功后,软件包exe就在dist电脑目录里面了.

💠折腾不止,热爱不停~✨


© 版权声明
文章版权归作者所有,未经允许请勿转载。

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息