end-print / app.py
Ethscriptions's picture
Update app.py
4e99405 verified
raw
history blame
6.56 kB
import pandas as pd
import streamlit as st
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import io
import base64
from matplotlib.patches import Rectangle
import matplotlib.gridspec as gridspec
import math
# 定义全局常量
SPLIT_TIME = "17:30" # 分界线时间
BUSINESS_START = "09:30" # 营业开始时间
BUSINESS_END = "01:30" # 营业结束时间
BORDER_COLOR = '#A9A9A9' # 深灰色边框
def process_schedule(file):
"""处理上传的 Excel 文件,生成排序和分组后的打印内容"""
try:
# 读取 Excel,跳过前 8 行
df = pd.read_excel(file, skiprows=8)
# 提取所需列 (G9, H9, J9)
df = df.iloc[:, [6, 7, 9]] # G, H, J 列
df.columns = ['Hall', 'StartTime', 'EndTime']
# 清理数据
df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
# 转换影厅格式为 "#号" 格式
df['Hall'] = df['Hall'].str.extract(r'(\d+)号').astype(str) + '#'
# 转换时间格式
df['StartTime'] = pd.to_datetime(df['StartTime']).dt.strftime('%H:%M')
df['EndTime'] = pd.to_datetime(df['EndTime']).dt.strftime('%H:%M')
# 将时间转换为 datetime 对象便于比较
base_date = datetime.today().date()
df['StartTime'] = pd.to_datetime(base_date.strftime('%Y-%m-%d ') + df['StartTime'])
df['EndTime'] = pd.to_datetime(base_date.strftime('%Y-%m-%d ') + df['EndTime'])
# 处理跨天情况
df.loc[df['EndTime'] < df['StartTime'], 'EndTime'] += timedelta(days=1)
# 筛选营业时间内的场次
business_start = datetime.strptime(base_date.strftime('%Y-%m-%d ') + BUSINESS_START, '%Y-%m-%d %H:%M')
business_end = datetime.strptime(base_date.strftime('%Y-%m-%d ') + BUSINESS_END, '%Y-%m-%d %H:%M')
if business_end < business_start:
business_end += timedelta(days=1)
mask = (
(df['StartTime'] >= business_start) |
(df['StartTime'] <= business_end)
)
df = df[mask]
# 按散场时间排序
df = df.sort_values('EndTime')
# 分割数据
split_time = datetime.strptime(base_date.strftime('%Y-%m-%d ') + SPLIT_TIME, '%Y-%m-%d %H:%M')
part1 = df[df['EndTime'] <= split_time].copy()
part2 = df[df['EndTime'] > split_time].copy()
# 格式化时间显示
for part in [part1, part2]:
part['EndTime'] = part['EndTime'].dt.strftime('%-I:%M')
return part1[['Hall', 'EndTime']], part2[['Hall', 'EndTime']]
except Exception as e:
st.error(f"处理文件时出错: {str(e)}")
return None, None
def create_print_layout(data, title):
"""创建打印布局"""
if data.empty:
return None
# 设置 A5 纸张竖向尺寸(单位:英寸)
fig = plt.figure(figsize=(5.83, 8.27), dpi=300)
# 减小边距,最大化利用空间
plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.05)
# 计算行数和列数
total_items = len(data)
num_cols = 3 # 固定为3列
num_rows = math.ceil(total_items / num_cols) # 计算需要的行数
# 创建网格,减小间距
gs = gridspec.GridSpec(num_rows, num_cols, hspace=0.1, wspace=0.1)
# 设置字体和样式
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字体
# 计算适合的字体大小(考虑到竖向布局和3列)
base_fontsize = min(50, 280 / num_rows) # 调整基础字体大小
# 重新组织数据为竖向排列
reshaped_data = []
data_values = data.values.tolist()
# 填充空值确保数据长度是3的倍数
while len(data_values) % 3 != 0:
data_values.append(['', ''])
# 按竖向顺序重新排列数据
num_rows = len(data_values) // 3
for col in range(3):
for row in range(num_rows):
idx = row + col * num_rows
if idx < len(data_values):
reshaped_data.append(data_values[idx])
# 填充数据
for idx, (hall, end_time) in enumerate(reshaped_data):
if hall and end_time: # 只处理非空数据
row = idx // 3
col = idx % 3
# 创建子图
ax = plt.subplot(gs[row, col])
# 设置更深的边框颜色
for spine in ax.spines.values():
spine.set_color(BORDER_COLOR)
spine.set_linewidth(0.5)
# 将影厅和时间紧密连接显示
display_text = f"{hall}{end_time}"
ax.text(0.5, 0.5, display_text,
fontsize=base_fontsize,
fontweight='bold',
ha='center',
va='center')
# 设置更小的内边距
ax.set_xlim(-0.02, 1.02)
ax.set_ylim(-0.02, 1.02)
# 移除坐标轴
ax.set_xticks([])
ax.set_yticks([])
# 将图表转换为 base64 字符串
buffer = io.BytesIO()
plt.savefig(buffer, format='png', bbox_inches='tight', pad_inches=0.05)
buffer.seek(0)
image_base64 = base64.b64encode(buffer.getvalue()).decode()
plt.close()
return f'data:image/png;base64,{image_base64}'
# Streamlit 界面
st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
st.title("散厅时间快捷打印")
uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls", "xlsx"])
if uploaded_file:
part1, part2 = process_schedule(uploaded_file)
if part1 is not None and part2 is not None:
# 创建打印布局
part1_image = create_print_layout(part1, "第一部分")
part2_image = create_print_layout(part2, "第二部分")
# 显示预览
col1, col2 = st.columns(2)
with col1:
st.subheader("白班散场预览(时间 ≤ 17:30)")
if part1_image:
st.image(part1_image)
else:
st.info("白班部分没有数据")
with col2:
st.subheader("夜班散场预览(时间 > 17:30)")
if part2_image:
st.image(part2_image)
else:
st.info("夜班部分没有数据")