“ 忠诚、笃学、严谨、守纪 ”

点击任意处进入

20252328 2025-2026-2 《Python程序设计》实验四报告

20252328 2025-2026-2 《Python程序设计》实验四报告

课程:《Python程序设计》
班级: 2523
姓名: 向一
学号:20252328
实验教师:王志强
实验日期:2026年5月16日
必修/选修: 公选课

1.实验内容

Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
这次实验利用阿里云QODER CN,开发了一款基于 Python PyQt6 的桌面应用程序——SmartDaily(智能每日助手),为用户提供简单的日常生活管理服务,包含天气,日记,记账,日程安排,每日总结。
!!!完整代码仓库
具体实验内容包括:

1.1 功能模块开发

(1)天气查询模块
集成和风天气和高德地图双 API,实现实时天气数据获取
支持城市搜索和切换功能
显示温度、湿度、风力、空气质量等详细信息
提供未来几小时的天气趋势预测
实现本地缓存机制,减少 API 调用频率
(2)日程管理模块
实现今日日程的添加、查看、删除功能
支持设置日程的开始时间和结束时间
提供双击或右键菜单标记日程完成状态
基于 APScheduler 实现定时提醒功能
通过系统托盘弹出通知提醒用户
自动清理 7 天前的已完成日程
(3)快速记账模块
提供简洁的支出录入界面(金额、类别、备注)
实时计算并显示今日总支出
实现历史记账明细查看功能
支持按日期和类别筛选记录
提供删除记账记录功能
(4)日记记录模块
实现日记编写功能(支持标题和正文)
提供历史记录列表,按日期倒序排列
支持查看、编辑和删除已有日记
在日终总结时展示当日日记内容
(5)AI 智能建议模块
集成 DeepSeek 大语言模型 API
根据天气、日程、支出等信息生成个性化建议
实现本地规则模板降级方案(API 不可用时)
提供晨间建议和日终 AI 点评功能
(6)系统功能
实现开始/结束今日流程管理
提供统一设置中心(AI 配置、城市设置)
实现系统托盘最小化功能

1.2 技术实现要点

GUI 框架: 使用 PyQt6 构建图形用户界面
数据库: 使用 SQLite3 实现数据持久化存储
网络请求: 使用 httpx 库进行 HTTP API 调用
定时任务: 使用 APScheduler 实现后台任务调度
异步编程: 使用 QThread 避免阻塞主 UI 线程
事件驱动: 基于 PyQt6 信号槽机制实现模块解耦
设计模式: 应用单例模式、策略模式、上下文管理器等

2. 实验过程及结果

2.1 实验环境搭建

开发环境配置:
操作系统: Windows 11
Python 版本: 3.14
IDE: VS Code / PyCharm
依赖库安装:

bash
pip install PyQt6 APScheduler httpx

项目结构创建:

DailyAssistant/├── main.py                 # 程序入口
├── config/                 # 配置模块
│   ├── paths.py           # 路径工具
│   └── theme.json         # 主题配置
├── core/                   # 核心模块
│   ├── db_manager.py      # 数据库管理器
│   ├── event_bus.py       # 事件总线
│   └── scheduler.py       # 任务调度器
├── services/               # 服务模块
│   ├── weather.py         # 天气服务
│   └── advice_engine.py   # AI 建议引擎
├── ui/                     # 界面模块
│   ├── main_window.py     # 主窗口
│   ├── weather_card.py    # 天气卡片
│   ├── schedule_widget.py # 日程组件
│   ├── expense_widget.py  # 记账组件
│   ├── note_widget.py     # 日记组件
│   └── dialogs.py         # 对话框集合
└── assets/                 # 资源文件
    └── icon.png           # 应用图标

2.2 数据库设计与实现

步骤1: 创建 DatabaseManager 单例类

class DatabaseManager:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    @contextmanager
    def get_connection(self):
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
            conn.commit()
        except Exception as e:
            conn.rollback()
            raise e
        finally:
            conn.close()

步骤2: 初始化数据表结构
创建 5 张核心数据表:weather_log(天气日志)、schedules(日程)、notes(笔记)、expenses(支出)、user_settings(用户设置)。
步骤3: 实现 CRUD 接口
为每张表提供增删改查方法,所有 SQL 语句使用参数化查询防止注入:

def add_schedule(self, title, start_time=None, end_time=None, reminder_time=None, date=None):
    if not date:
        date = datetime.now().strftime("%Y-%m-%d")
    created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    with self.get_connection() as conn:
        cursor = conn.execute(
            '''INSERT INTO schedules (title, date, start_time, end_time, reminder_time, is_completed, created_at)
               VALUES (?, ?, ?, ?, ?, 0, ?)''',
            (title, date, start_time, end_time, reminder_time, created_at)
        )
        return cursor.lastrowid

2.3 事件总线实现

步骤1: 创建 EventBus 单例
基于 PyQt6 的 QObject 和 pyqtSignal 实现全局事件总线:

class EventBus(QObject):
    _instance = None

    weather_updated = pyqtSignal(dict)
    schedule_changed = pyqtSignal()
    expense_added = pyqtSignal(dict)
    expense_deleted = pyqtSignal(dict)

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

步骤2: 模块间通信
发送方发射信号,接收方连接槽函数,实现解耦:

# 发送信号(ExpenseWidget)
event_bus.expense_added.emit({"amount": 50, "category": "餐饮"})

# 接收信号(MainWindow 或其他组件)
event_bus.expense_added.connect(self.on_expense_changed)

结果: 模块间耦合度降低,代码可维护性提高。

2.4 定时调度器实现

步骤1: 创建 SchedulerWorker 线程
继承 QThread,在后台运行 APScheduler:

class SchedulerWorker(QThread):
    reminder_triggered = pyqtSignal(dict)

    def run(self):
        self.scheduler = BackgroundScheduler(timezone='Asia/Shanghai')

        # 每分钟检查提醒
        self.scheduler.add_job(
            func=self._check_reminders,
            trigger='interval',
            minutes=1,
            id='check_reminders'
        )

        # 每天凌晨清理数据
        self.scheduler.add_job(
            func=self._cleanup_old_data,
            trigger='cron',
            hour=0, minute=0,
            id='cleanup_data'
        )

        self.scheduler.start()
        while self._is_running:
            self.msleep(1000)

步骤2: 实现提醒检查逻辑
遍历今日未完成日程,判断提醒时间是否到期:

def _check_reminders(self):
    now = datetime.now()
    schedules = db_manager.get_today_schedules()

    for schedule in schedules:
        if schedule.get('is_completed', 0):
            continue

        reminder_time = schedule.get('reminder_time')
        if not reminder_time:
            continue

        reminder_dt = datetime.strptime(reminder_time[:16], "%Y-%m-%d %H:%M")

        if reminder_dt <= now and (now - reminder_dt).total_seconds() < 60:
            self.reminder_triggered.emit({
                'title': schedule['title'],
                'start_time': schedule.get('start_time', '')
            })

结果: 定时提醒准确触发,系统托盘通知正常显示,不阻塞 UI。

2.5 天气服务实现

步骤1: 创建 WeatherService 类
实现多源 API 集成和降级策略:

class WeatherService:
    def __init__(self):
        self.qweather_key = db_manager.get_setting("qweather_api_key", "")
        self.amap_key = db_manager.get_setting("amap_api_key", "")

    def fetch_weather(self, city_name=None):
        if city_name is None:
            city_name = db_manager.get_setting("city_name", "北京")

        # 优先尝试和风天气
        if self.qweather_key:
            data = self._fetch_qweather(city_name)
            if data: 
                return data

        # 降级到高德地图
        data = self._fetch_amap(city_name)
        if data: 
            return data

        # 最后尝试缓存
        return self._get_cached_data(city_name)

步骤2: 实现和风天气 API 调用
并行获取实况、预报、空气质量等多维度数据:

def _fetch_qweather(self, city_name):
    location_id = self._get_location_id(city_name, source="qweather")

    with httpx.Client(timeout=8) as client:
        headers = {"Authorization": f"{self.qweather_key}"}

        now_r = client.get("https://devapi.qweather.com/v7/weather/now", 
                          params={"location": location_id}, headers=headers)
        daily_r = client.get("https://devapi.qweather.com/v7/weather/3d", 
                            params={"location": location_id}, headers=headers)
        air_r = client.get("https://devapi.qweather.com/v7/air/now", 
                          params={"location": location_id}, headers=headers)

        now = now_r.json()["now"]
        daily = daily_r.json()["daily"][0]
        air = air_r.json()["now"]

        return {
            "temperature": float(now["temp"]),
            "humidity": int(now["humidity"]),
            "condition_text": now["text"],
            "aqi": int(air["aqi"]),
            "temp_max": float(daily["tempMax"]),
            "temp_min": float(daily["tempMin"]),
            # ... 更多字段
        }

步骤3: 实现城市地理位置查询
调用地理编码 API 将城市名称转换为 Location ID:

def _get_location_id(self, city_name, source="qweather"):
    with httpx.Client(timeout=8) as client:
        if source == "qweather":
            resp = client.get("https://geoapi.qweather.com/v2/city/lookup", 
                             params={"location": city_name, "number": 1},
                             headers={"Authorization": f"{self.qweather_key}"})
            data = resp.json()
            if data.get("code") == "200" and data.get("location"):
                return data["location"][0]["id"]

结果: 天气数据获取成功,API 降级机制有效,缓存减少重复调用。

2.6 AI 建议引擎实现

步骤1: 创建 AdviceEngine 类
支持在线 AI 和本地规则两种策略:

class AdviceEngine:
    def generate(self, weather_data, schedule_status=None, expense_total=0, note_content=""):
        config = self.get_config()

        # 检查 AI 是否启用
        if not config.get("ai_enabled"):
            return self._rule_based_advice(weather_data)

        # 检查 API Key
        if not config.get("api_key"):
            return "⚠️ AI 建议未启用:请在设置中心填写 DeepSeek API Key。"

        try:
            return self._call_llm_api(weather_data, schedule_status, expense_total, note_content, config)
        except Exception as e:
            # 降级到本地规则
            return self._rule_based_advice(weather_data)

步骤2: 实现 DeepSeek API 调用
构造提示词,调用大模型生成个性化建议:

def _call_llm_api(self, weather, schedules, expenses, note, config):
    prompt = f"""
你是一位贴心的生活助手。请根据以下信息给出一段简短、温馨的今日建议(120字以内):

【当前天气】
- 实时:{weather.get('condition_text')},{weather.get('temperature')}°C
- 今日最高/最低:{weather.get('temp_max')}°C / {weather.get('temp_min')}°C
- 空气质量:AQI {weather.get('aqi')}

【今日安排】
- 日程进度:{schedules}
- 已支出:{expenses}元
- 笔记:{note if note else '无'}

请结合当前天气和未来趋势,给出穿衣、出行、健康等方面的实用建议。语气要温暖贴心。
"""

    payload = {
        "model": config.get("model", "deepseek-chat"),
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.7
    }

    with httpx.Client(timeout=8) as client:
        response = client.post(
            config["api_url"], 
            json=payload,
            headers={"Authorization": f"Bearer {config['api_key']}"}
        )
        return response.json()["choices"][0]["message"]["content"].strip()

步骤3: 实现本地规则降级
基于温度和天气代码返回预设模板:

def _rule_based_advice(self, weather):
    temp = weather.get("temperature", 20)
    code = weather.get("condition_code", 0)

    if temp > 28:
        return "🌞 今日气温较高,注意防晒补水。"
    if code in [61, 63, 65, 80]: 
        return "🌧 降水概率大,请携带雨具。"
    if temp < 10:
        return "❄️ 气温较低,请注意保暖。"

    return "🌤 今日天气平稳,适宜日常通勤。"

结果: AI 建议生成成功,内容丰富且个性化;API 失败时自动降级,保证功能可用性。

2.7 主窗口与界面整合

步骤1: 创建 MainWindow 类
整合所有功能模块,设置整体布局:

class MainWindow(QMainWindow):
    def _setup_ui(self):
        central_widget = QWidget()
        main_layout = QVBoxLayout()

        # 顶部工具栏
        toolbar_layout = QHBoxLayout()
        settings_btn = QPushButton("⚙️ 设置")
        settings_btn.clicked.connect(self.show_settings)
        toolbar_layout.addWidget(settings_btn)
        main_layout.addLayout(toolbar_layout)

        # 天气卡片
        self.weather_card = WeatherCard()
        main_layout.addWidget(self.weather_card)

        # 日程管理
        self.schedule_widget = ScheduleWidget()
        main_layout.addWidget(self.schedule_widget)

        # 记账和笔记(横向布局)
        record_layout = QHBoxLayout()
        self.note_widget = NoteWidget()
        self.expense_widget = ExpenseWidget()
        record_layout.addWidget(self.note_widget)
        record_layout.addWidget(self.expense_widget)
        main_layout.addLayout(record_layout)

        # 开始/结束今日按钮
        self.day_action_btn = QPushButton("🌅 开始今日")
        self.day_action_btn.clicked.connect(self.handle_day_action)
        main_layout.addWidget(self.day_action_btn)

        self.setCentralWidget(central_widget)

步骤2: 实现系统托盘

def _setup_tray(self):
    self.tray_icon = QSystemTrayIcon(self)
    self.tray_icon.setIcon(QIcon("assets/icon.png"))
    self.tray_icon.setToolTip("Smart Daily Assistant")

    tray_menu = QMenu()
    show_action = QAction("显示主窗口", self)
    show_action.triggered.connect(self.show)
    tray_menu.addAction(show_action)

    quit_action = QAction("退出", self)
    quit_action.triggered.connect(QApplication.quit)
    tray_menu.addAction(quit_action)

    self.tray_icon.setContextMenu(tray_menu)
    self.tray_icon.show()

def closeEvent(self, event):
    if self.tray_icon.isVisible():
        self.hide()
        self.tray_icon.showMessage("Smart Daily", "助手已最小化到托盘")
        event.ignore()
    else:
        event.accept()

步骤3: 实现日程变化监听
连接 EventBus 信号,自动刷新日程列表:

def _connect_signals(self):
    event_bus.schedule_changed.connect(self.on_schedule_changed)
    task_scheduler.worker.reminder_triggered.connect(self.show_reminder_notification)

def on_schedule_changed(self):
    if hasattr(self, 'schedule_widget'):
        self.schedule_widget.load_schedules()

def show_reminder_notification(self, reminder_data):
    title = reminder_data.get('title', '日程提醒')
    message = f"📅 {title}"

    self.tray_icon.showMessage("日程提醒", message, QSystemTrayIcon.MessageIcon.Information, 5000)
    QMessageBox.information(self, "日程提醒", message)

结果: 使主窗口布局合理,各模块协调工作,系统托盘功能正常。

2.8 各功能组件实现

  • (1)WeatherCard 天气卡片
    异步加载天气数据(使用 QThread)
    显示温度、湿度、风力、空气质量
    提供城市搜索和切换功能
    调用 AdviceEngine 生成智能建议
    关键代码:
  def load_weather(self):
    self.refresh_btn.setEnabled(False)
    self.refresh_btn.setText("加载中...")
  
    self.weather_fetcher = WeatherFetcher(self.current_city)
    self.weather_fetcher.finished.connect(self.on_weather_loaded)
    self.weather_fetcher.error.connect(self.on_weather_error)
    self.weather_fetcher.start()

  def on_weather_loaded(self, data):
    self.refresh_btn.setEnabled(True)
    self.refresh_btn.setText("🔄 刷新")

    temp = data.get('temperature', '--')
    humidity = data.get('humidity', '--')
    condition = data.get('condition_text', '未知')
    
    weather_text = f"🌡️ 温度: {temp}°C\n💧 湿度: {humidity}%\n☁️ 天气: {condition}"
    self.weather_info_label.setText(weather_text)
    
    event_bus.weather_updated.emit(data)
    self.generate_advice(data)
  • (2)ScheduleWidget 日程组件
    加载并显示今日日程列表
    添加日程对话框(标题、开始时间、结束时间)
    右键菜单操作(标记完成、删除)
    双击切换完成状态
    添加日程时同步注册定时提醒任务
    关键代码:
def add_schedule(self):
    dialog = QDialog(self.window())
    title_input = QLineEdit()
    start_time_edit = QDateTimeEdit()
    end_time_edit = QDateTimeEdit()

    def on_save():
        title = title_input.text().strip()
        if not title:
            QMessageBox.warning(dialog, "提示", "请输入日程标题")
            return

        start_time = start_time_edit.dateTime().toString("yyyy-MM-dd HH:mm:ss")
        date = start_time_edit.dateTime().toString("yyyy-MM-DD")

        schedule_id = db_manager.add_schedule(
            title=title, start_time=start_time,
            end_time=end_time_edit.dateTime().toString("yyyy-MM-DD HH:mm:ss"),
            reminder_time=start_time, date=date
        )

        # 添加定时提醒
        task_scheduler.add_reminder_job(schedule_id, start_time)

        dialog.accept()
        self.load_schedules()

    save_btn.clicked.connect(on_save)
    dialog.exec()
  • (3)ExpenseWidget 记账组件
    快速录入支出(金额、类别、备注)
    实时显示今日总支出
    监听 expense_added/deleted 信号自动更新总额
    打开 ExpenseHistoryDialog 查看历史明细
    关键代码:

    def add_expense(self):
      amount_text = self.amount_input.text().strip()
      if not amount_text:
          return
    
      try:
          amount = float(amount_text)
      except ValueError:
          return
    
      if amount <= 0:
          return
    
      category = self.category_combo.currentText()
      remark = self.remark_input.text().strip()
    
      db_manager.add_expense(amount, category, remark)
    
      event_bus.expense_added.emit({"amount": amount, "category": category})
    
      self.amount_input.clear()
      self.remark_input.clear()
      self.update_total()
    
  • (4)NoteWidget 日记组件
    DiaryManagerDialog 实现完整日记管理
    左侧历史列表,右侧编辑区域
    支持标题和正文
    保存后刷新历史列表
    关键代码:

    def save_current_note(self):
      title = self.title_input.text()
      content = self.content_editor.toPlainText()
    
      if not content.strip() and not title.strip():
          QMessageBox.warning(self, "提示", "标题和正文不能同时为空")
          return
    
      db_manager.save_note(self.current_date, content, title)
      self.load_history_list()
      QMessageBox.information(self, "成功", "日记已保存!")
    
  • (5)SettingsDialog 设置中心
    使用 QStackedWidget 实现多页面切换
    AI 设置(启用开关、API Key、模型名称、URL)
    主题设置(浅色/深色)
    城市设置
    关于页面
    关键代码:

    def save_all_settings(self):
      ai_enabled = "true" if self.ai_enabled_check.currentIndex() == 0 else "false"
      db_manager.set_setting("ai_enabled", ai_enabled)
      db_manager.set_setting("api_key", self.api_key_input.text().strip())
      db_manager.set_setting("ai_model", self.model_input.text().strip())
      db_manager.set_setting("current_city", self.city_input.text().strip())
    
      QMessageBox.information(self, "成功", "设置已保存!")
      self.accept()
    
  • (6)DaySummaryDialog 日终总结
    汇总今日日程完成情况
    统计今日总支出
    展示当日日记内容
    调用 AI 生成综合点评
    关键代码:

    def load_summary(self):
      schedules = db_manager.get_today_schedules()
      completed_count = sum(1 for s in schedules if s.get("is_completed", 0))
      expense_total = db_manager.get_today_expense_total()
      note_obj = db_manager.get_note(self.today)
      note_content = note_obj.get("content", "") if note_obj else ""
    
      # 获取 AI 点评
      try:
          weather_service = WeatherService()
          weather_data = weather_service.fetch_weather()
          engine = AdviceEngine()
          ai_comment = engine.generate(
              weather_data, 
              {"total": len(schedules), "completed": completed_count},
              expense_total, 
              note_content
          )
      except:
          ai_comment = "💡 今日辛苦了!好好休息,明天继续加油。"
    
      summary = f"📅 日期: {self.today}\n\n"
      summary += f"🤖 AI 每日点评:\n{ai_comment}\n\n"
      summary += f"📋 日程: {len(schedules)} 项 | 已完成: {completed_count} 项\n"
      summary += f"💰 今日支出: ¥{expense_total:.2f}\n\n"
      if note_content:
          summary += f"📝 今日笔记:\n{note_content}\n\n"
    
      self.summary_text.setPlainText(summary)
    

    2.9 程序启动流程

    main.py 入口文件:

    if __name__ == "__main__":
      app = QApplication(sys.argv)
    
      # 应用主题
      theme_manager = ThemeManager()
      theme_manager.apply_theme(app)
    
      # 启动定时调度器
      task_scheduler.start()
    
      # 创建并显示主窗口
      window = MainWindow()
      window.show()
    
      exit_code = app.exec()
    
      # 停止调度器
      task_scheduler.stop()
      sys.exit(exit_code)
    

    2.10 测试结果

  • 主界面截图: 展示整体布局和所有功能模块
    主界面

  • 日程管理截图: 展示日程列表和添加对话框
    编辑日程

  • 提醒通知截图: 系统托盘弹出的日程提醒
    弹框提醒

  • 记账界面截图: 快速录入和历史明细表格
    记账

  • 日记编辑截图: 日记编写界面和历史列表
    编辑一条日记

  • 日终总结截图: AI 点评和数据汇总
    结束每日

视频演示


性能指标:
启动时间: 约 2-3 秒
内存占用: 约 80-120 MB
占用
天气 API 响应: 1-3 秒
AI 建议生成: 2-5 秒
数据库操作: < 100ms

3. 实验过程中遇到的问题和解决过程

问题1: PyQt6 多线程 UI 更新导致崩溃

问题描述: 在 WeatherFetcher 和 AdviceFetcher 等后台线程中直接更新 QLabel 等 UI 控件时,程序出现崩溃或无响应。错误提示:"QWidget: Must construct a QApplication before a QWidget" 或界面卡死。

原因分析: PyQt6 要求所有 UI 操作必须在主线程(GUI 线程)中执行。在子线程中直接修改 UI 违反了线程安全规则。

解决方案: 使用 PyQt6 的信号槽机制,通过 pyqtSignal 将数据从子线程传递到主线程:

class WeatherFetcher(QThread):
    finished = pyqtSignal(dict)  # 定义信号
    error = pyqtSignal(str)

    def run(self):
        try:
            data = weather_service.fetch_weather(city)
            self.finished.emit(data)  # 发射信号,而非直接更新 UI
        except Exception as e:
            self.error.emit(str(e))

# 在主窗口中连接信号
self.weather_fetcher = WeatherFetcher(city)
self.weather_fetcher.finished.connect(self.on_weather_loaded)  # 槽函数在主线程执行
self.weather_fetcher.start()

def on_weather_loaded(self, data):
    # 此方法在主线程中执行,可以安全更新 UI
    self.weather_info_label.setText(f"温度: {data['temperature']}°C")

效果: 程序运行稳定,UI 响应流畅,不再出现线程安全问题。

问题2: APScheduler 定时任务精度不足

问题描述: 设置的日程提醒有时延迟几分钟才触发,甚至偶尔遗漏。

原因分析:

APScheduler 的 interval 触发器存在误差
仅依赖 scheduler 的一次性任务,若程序重启则任务丢失
时间判断逻辑不够精确
解决方案:

采用双重保障:每分钟轮询检查 + 一次性精确任务
扩大时间窗口判断(60 秒内均视为到期)
程序启动时重新注册所有未完成的提醒任务

  # 每分钟轮询检查
self.scheduler.add_job(
    func=self._check_reminders,
    trigger='interval',
    minutes=1,
    id='check_reminders'
)

def _check_reminders(self):
    now = datetime.now()
    schedules = db_manager.get_today_schedules()

    for schedule in schedules:
        if schedule.get('is_completed', 0):
            continue

        reminder_time = schedule.get('reminder_time')
        if not reminder_time:
            continue

        reminder_dt = datetime.strptime(reminder_time[:16], "%Y-%m-%d %H:%M")

        # 60 秒窗口,避免遗漏
        if reminder_dt <= now and (now - reminder_dt).total_seconds() < 60:
            self._emit_reminder(schedule)

问题3: 界面布局在不同分辨率下显示异常

问题描述: 在高 DPI 屏幕或小尺寸屏幕上,部分控件重叠或溢出窗口边界。

原因分析:

使用了固定尺寸(setFixedSize)而非自适应布局
未考虑不同屏幕的 DPI 缩放
文本内容过长时未设置换行
解决方案:

优先使用布局管理器(QVBoxLayout、QHBoxLayout)而非绝对定位
设置最小/最大尺寸而非固定尺寸
启用文本自动换行(setWordWrap)
使用 stretch 因子灵活分配空间

# 原来固定尺寸容易重叠
label.setFixedSize(200, 50)

# 改为用布局管理器和stretch分配空间
layout = QVBoxLayout()
label = QLabel("长文本内容...")
label.setWordWrap(True)  # 启用换行
layout.addWidget(label)

button = QPushButton("按钮")
button.setMinimumWidth(100)  # 最小宽度
button.setMaximumWidth(200)  # 最大宽度
layout.addWidget(button)

layout.addStretch()  # 弹性空间

问题4: 删除操作缺乏二次确认导致误删

问题描述: 在日程列表或记账列表中点击删除时,没有确认提示,容易误删重要数据。

原因分析: 删除按钮直接执行删除操作,未给用户反悔机会。

解决方案: 实现通用的删除确认对话框,在所有删除操作前调用:

def show_delete_confirmation(parent, item_description="此项目"):
    dialog = QDialog(parent)
    dialog.setWindowTitle("确认删除")
    dialog.setMinimumSize(320, 160)

    layout = QVBoxLayout()
    warning_icon = QLabel("⚠️")
    warning_icon.setAlignment(Qt.AlignmentFlag.AlignCenter)
    warning_icon.setStyleSheet("font-size: 32px;")
    layout.addWidget(warning_icon)

    message_label = QLabel(f"确定要删除{item_description}吗?\n此操作不可恢复!")
    message_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
    layout.addWidget(message_label)

    btn_layout = QHBoxLayout()
    confirm_btn = QPushButton("🗑️ 删除")
    confirm_btn.setStyleSheet("background-color: #e74c3c; color: white;")
    cancel_btn = QPushButton("❌ 取消")

    result = False
    def on_confirm():
        nonlocal result
        result = True
        dialog.accept()

    confirm_btn.clicked.connect(on_confirm)
    cancel_btn.clicked.connect(dialog.reject)

    btn_layout.addWidget(confirm_btn)
    btn_layout.addWidget(cancel_btn)
    layout.addLayout(btn_layout)
    dialog.setLayout(layout)
    dialog.exec()
    return result

# 使用示例
if not show_delete_confirmation(self, "这个日程"):
    return  # 用户取消删除
db_manager.delete_schedule(schedule_id)

全课总结

本学期在王老师的教学带领下,我从零基础开始系统学习Python编程,一步步掌握了完整的基础语法与实操技能,能够独立编写代码、完成课程实验与小型项目。这一学期不仅积累了扎实的编程知识,也培养了基本的编程思维与自主学习能力,收获良多。

课程知识总结

本次课程内容由浅入深、循序渐进,涵盖Python基础语法、数据结构、流程控制、函数、面向对象、文件与异常处理以及各类实战应用,知识点覆盖面广、实用性强,让我建立了完整的Python知识框架。

在基础学习阶段,我了解了Python的语言特点:它是一门解释型、面向对象的高级语言,语法简洁、上手简单,相比C、C++更适合初学者快速入门。我熟练使用PyCharm、IDLE等开发工具,掌握了程序调试、报错排查、基础输入输出等操作,同时熟悉标识符、保留字、代码缩进规则,掌握了变量、常用数据类型、类型转换和基础运算符的使用方法。

在程序逻辑方面,我重点学习了顺序、分支、循环三种程序结构,熟练运用if条件判断、for和while循环语句编写代码。同时了解了死循环、数据溢出等常见问题,学会规避基础代码漏洞,养成规范命名、规范写代码的良好习惯。

我系统掌握了Python四种常用序列结构的特点与区别:列表可灵活增删改查、支持排序遍历;元组内容不可修改、稳定性强;字典以键值对存储数据、查询高效;集合元素唯一,主要用于去重。在字符串处理上,我掌握了切片、格式转换等基础操作,也初步接触正则表达式,能够完成简单的文本匹配、分割与清洗工作。

课程中期,我学习了多项核心进阶知识。通过函数的定义、参数传递与调用,我学会拆分代码功能,简化主程序,提高代码复用性。借助try-except-finally异常处理机制,我可以主动捕获程序报错,通过断点调试排查问题,提升代码稳定性。同时,我掌握了文件操作的基本流程,能够使用open、write、seek、close等函数完成文件的读写与管理。

面向对象编程是本学期的重点内容。我理解了“类是模板、对象是实例”的核心逻辑,能够用class定义类、用__init__方法初始化对象,掌握了封装、继承、多态三大特性。通过学习模块与包的应用,我了解到拆分程序模块可以有效降低项目开发难度,让代码结构更清晰、更易拓展。

在实战应用部分,我接触了Python的多种实用场景,包括网络爬虫、Socket网络通信、GUI界面编程、数据库操作和简易游戏开发。我学会使用爬虫库,了解robots协议和请求头的基本作用,能够完成简单的数据爬取与解析;掌握Socket通信基本原理,实现客户端与服务端的简单交互;通过PyQt5了解图形界面开发基础。此外,我也学会了Gitee、Github等代码平台的基础使用,能够完成代码上传与管理。

课程建议

增加分层课后练习。课程一周一节,间隔时间长,知识点容易遗忘。希望老师可以提供难度梯度的练习题,或者推荐合适的刷题平台,方便我们课后持续练习、巩固基础。

加强实验内容的前后衔接。建议后续实验融入旧知识,例如用面向对象思想重构前期基础代码,串联新旧知识点,避免学后面忘前面,形成连贯的知识体系。

增加实验展示和讲解环节。提交实验后可以让学生简单演示自己的代码思路,既能督促我们独立完成作业,减少对AI的依赖,也能互相交流学习、拓宽编程思路。

提供优秀实验范例参考。开放式实验自由度高,但初期没有参考标准,容易迷茫。希望可以展示往届优秀实验案例和报告模板,明确完成标准,同时强调原创性,更好地引导我们完成实验。

总结与展望

这一学期的Python学习,不仅让我掌握了扎实的编程技能,更培养了我的逻辑思维和自主探索能力。我并非只为学分学习这门课程,未来我会继续练习Python编程,把学到的知识运用到实际学习和项目实践中。

最后,再次感谢王老师一学期的悉心教导与付出!祝愿老师工作顺利、万事顺遂,我也希望今后能继续和老师交流学习,不断提升自己的编程能力。

其他

通过本次 SmartDaily 项目的开发,我获得了以下深刻的感悟和思考:

  1. 理论与实践的差距
    课堂上学到的设计模式和架构理念,在实际应用中需要根据具体场景灵活调整。例如,单例模式虽然简单,但在多线程环境下需要特别注意线程安全;事件总线虽然能解耦模块,但过度使用会导致事件流向难以追踪。

  2. 用户体验至上
    一个优秀的软件不仅要功能完善,更要注重用户体验。异步加载、友好提示、二次确认、错误降级等细节,看似微不足道,却直接影响用户的使用感受。在开发过程中,我不断站在用户角度思考:"如果我是用户,我希望如何操作?"这种思维转变让我意识到,技术开发最终是为了服务人。

  3. 健壮性比功能更重要
    初期我过于追求功能丰富度,忽略了异常处理和边界情况。后来发现,程序崩溃一次带来的负面体验,远超过缺少一个功能的遗憾。现在我更加注重防御性编程,对每个可能失败的环节都做了充分的容错处理。

  4. 文档和注释的价值
    在项目后期回顾代码时,我发现良好的注释和文档能极大提高维护效率。特别是对于复杂的业务逻辑(如定时提醒的时间窗口判断),清晰的注释能帮助自己和他人在几个月后快速理解代码意图。

  5. 持续学习的重要性
    Python 生态庞大,新技术层出不穷。在本次项目中,我首次接触 PyQt6、APScheduler、httpx 等库,通过查阅官方文档、Stack Overflow 和社区教程,逐步掌握了它们的使用方法。这让我认识到,自主学习能力比记忆具体 API 更重要。

  6. 工程思维的养成
    从最初的功能堆砌到后来的架构优化,我逐渐形成了系统性思维。开始时会先画流程图、设计类图,再动手编码;遇到 bug 时会系统地排查日志、分析堆栈,而非盲目试错。这种工程化的思维方式,是我在本实验中获得的最宝贵财富。

我希望能继续完善 SmartDaily,增加数据统计图表、语音提醒、云端同步等功能,并将其打包发布,让更多用户体验到这款小工具带来的便利,不断提升自己的软件工程能力。

参考资料~~~~

  1. PyQt6 官方文档
  2. SQLite 官方文档
  3. APScheduler 用户指南
  4. httpx 文档
  5. DeepSeek API 文档
  6. 和风天气开发文档
  7. 高德地图 Web 服务 API
  8. 全院公选课.Python程序设计
  9. Qoder CN 官方文档
  10. PyQt6 中文教程
posted @ 2026-06-14 11:30  alonep  阅读(6)  评论(0)    收藏  举报