Python结合Flask与PyQt5开发自动排班系统

Elena
Elena
发布时间:2026-10-11 | 3人浏览过
原创

本文详细介绍如何使用Python结合Flask后端与PyQt5桌面框架,快速搭建一款自动排班系统。系统支持员工信息导入、基于规则的智能排班生成与Excel批量导出,将Web界面无缝嵌入本地客户端,为团队周期排班提供轻量级解决方案。

系统架构设计

本项目采用前后端分离的轻量级架构,利用PyQt5的QWebEngineView组件将纯HTML/CSS/JS编写的Web界面直接托管至桌面应用中。整体分层如下:

  • 桌面应用层(PyQt5):负责进程管理、UI渲染与窗口交互
  • Web服务层(Flask):提供RESTful接口,处理排班计算与文件读写
  • 数据层:基于CSV/Excel文本文件进行人员与班次数据的持久化存储
自动排班系统主界面效果图
系统主窗口集成Web视图显示排班表与员工列表的界面布局

第1步:准备运行环境与数据文件

在正式运行前,需确保环境满足依赖要求并放置正确的数据源文件。

  • 安装核心库:执行pip install flask pyqt5 pyqt5-webengine pandas numpy openpyxl
  • 创建目录结构:建立包含main.py、app.py、templates/(存放index.html)的基础工程目录
  • 准备数据源:在同级目录放置员工名单Employee.csv(需包含列名Employee'sName)及历史排班示例表格,用于测试读取逻辑

第2步:初始化桌面主程序与后台服务

main.py作为应用入口,主要职责是拉起Qt窗口并异步启动Flask服务,避免阻塞主线程导致界面卡死。

import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
from flask import Flask
import threading

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("自动排班系统")
        self.setGeometry(100, 100, 1200, 800)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 创建内嵌浏览器控件
        self.web_view = QWebEngineView()
        layout.addWidget(self.web_view)
        
        # 多线程启动后端
        threading.Thread(target=self.run_flask, daemon=True).start()
        time.sleep(1)
        self.web_view.setUrl(QUrl("http://127.0.0.1:3863"))
        
    def run_flask(self):
        from app import app
        app.run(host='127.0.0.1', port=3863)

def main():
    app_exec = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app_exec.exec_())

if __name__ == '__main__':
    main()

第3步:构建Flask路由与排班计算逻辑

app.py承担全部业务运算。系统内置SHIFTS字典映射白班、晚班与休息状态,核心排班函数generate_monthly_schedule通过Pandas构建二维矩阵,并采用随机打散策略分配班次,同时加入每周至少休息两天的硬性校验逻辑。提供/api/generate_schedule与/api/export_schedule接口供前端调用。

from flask import Flask, render_template, request, jsonify, send_file
import pandas as pd
import calendar
import numpy as np

app = Flask(__name__)

SHIFTS = {'白班': 'D', '晚班': 'N', '休息': 'R'}

def load_employee_data():
    try:
        df = pd.read_csv('Employee.csv', encoding='utf-8')
        return pd.DataFrame({'name': df["Employee'sName"]})
    except Exception as e:
        print(f"加载员工数据失败: {e}")
        return pd.DataFrame({'name': []})

def get_month_calendar(year, month):
    return calendar.monthcalendar(year, month)

def generate_monthly_schedule(employees, year, month):
    num_days = calendar.monthrange(year, month)[1]
    employees_array = np.array(employees)
    schedule = pd.DataFrame(index=employees, columns=range(1, num_days + 1))
    schedule.fillna('R', inplace=True)
    
    for day in range(1, num_days + 1):
        np.random.shuffle(employees_array)
        day_shifts = int(len(employees) * 0.4)
        schedule.loc[employees_array[:day_shifts], day] = 'D'
        night_shifts = int(len(employees) * 0.3)
        schedule.loc[employees_array[day_shifts:day_shifts+night_shifts], day] = 'N'
    
    for emp in employees:
        for week_start in range(0, num_days, 7):
            week_end = min(week_start + 7, num_days)
            week_mask = schedule.loc[emp, week_start+1:week_end]
            rest_count = (week_mask == 'R').sum()
            if rest_count < 2:
                work_days = list(week_mask[week_mask != 'R'].index)
                np.random.shuffle(work_days)
                for i in range(min(2 - rest_count, len(work_days))):
                    schedule.loc[emp, work_days[i]] = 'R'
    return schedule

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/employees')
def get_employees():
    return jsonify(load_employee_data().to_dict('records'))

@app.route('/api/calendar//')
def get_calendar(year, month):
    return jsonify(get_month_calendar(year, month))

@app.route('/api/generate_schedule', methods=['POST'])
def generate_schedule():
    try:
        data = request.get_json()
        schedule = generate_monthly_schedule(
            data.get('employees', []),
            data.get('year', 2025),
            data.get('month', 1)
        )
        sched_dict = {emp: schedule.loc[emp].to_dict() for emp in data.get('employees', [])}
        return jsonify({"status": "success", "schedule": sched_dict})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})

@app.route('/api/export_schedule', methods=['POST'])
def export_schedule():
    try:
        data = request.get_json()
        df = pd.DataFrame.from_dict(data.get('schedule', {}), orient='index')
        df.columns = [str(i) for i in range(1, len(df.columns) + 1)]
        df.reset_index(inplace=True)
        df.rename(columns={'index': '姓名'}, inplace=True)
        fname = f'排班表_{data.get("year",2025)}{data.get("month",1):02d}.xlsx'
        return send_file(fname, as_attachment=True, download_name=fname,
                         mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    except Exception as e:
        return jsonify({"status": "error", "message": f"导出失败: {str(e)}"})

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=3863, debug=True)

运行验证与排错建议

  • 端口冲突检查:默认监听3863端口,若被占用可修改app.run中的port参数或提前终止占用进程
  • 前端模板缺失:当前仅提供后端逻辑,实际使用前需自行创建templates/index.html并实现AJAX/Fetch调用/api/系列接口以渲染日历与按钮交互
  • 数据格式规范:Employee.csv必须严格匹配列名Employee'sName,编码设置为UTF-8,否则pandas读取将抛出异常

以上就是Python结合Flask与PyQt5开发自动排班系统的详细内容,更多关于Python本地部署与自动化办公工具的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表