Ethscriptions commited on
Commit
156968c
·
verified ·
1 Parent(s): 3222004

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -0
app.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import streamlit as st
3
+ import matplotlib.pyplot as plt
4
+ import matplotlib.font_manager as font_manager
5
+ import io
6
+ import base64
7
+ import os
8
+ from datetime import datetime, timedelta
9
+ import math
10
+ from matplotlib.patches import FancyBboxPatch
11
+ from pypinyin import lazy_pinyin, Style
12
+ from matplotlib.backends.backend_pdf import PdfPages
13
+
14
+ def get_font(size=14):
15
+ font_path = "simHei.ttc"
16
+ if not os.path.exists(font_path):
17
+ font_path = "SimHei.ttf"
18
+ return font_manager.FontProperties(fname=font_path, size=size)
19
+
20
+ def get_pinyin_abbr(text):
21
+ """获取文本前两个汉字的拼音首字母"""
22
+ if not text:
23
+ return ""
24
+ # 提取前两个汉字
25
+ chars = [c for c in text if '\u4e00' <= c <= '\u9fff']
26
+ if len(chars) < 2:
27
+ chars = chars + [''] * (2 - len(chars))
28
+ else:
29
+ chars = chars[:2]
30
+ # 获取拼音首字母
31
+ pinyin_list = lazy_pinyin(chars, style=Style.FIRST_LETTER)
32
+ return ''.join(pinyin_list).upper()
33
+
34
+ def process_schedule(file):
35
+ try:
36
+ date_df = pd.read_excel(file, header=None, skiprows=7, nrows=1, usecols=[3])
37
+ date_str = pd.to_datetime(date_df.iloc[0, 0]).strftime('%Y-%m-%d')
38
+ base_date = pd.to_datetime(date_str).date()
39
+ except:
40
+ date_str = datetime.today().strftime('%Y-%m-%d')
41
+ base_date = datetime.today().date()
42
+
43
+ try:
44
+ df = pd.read_excel(file, header=9, usecols=[1, 2, 4, 5])
45
+ df.columns = ['Hall', 'StartTime', 'EndTime', 'Movie']
46
+ df['Hall'] = df['Hall'].ffill()
47
+ df.dropna(subset=['StartTime', 'EndTime', 'Movie'], inplace=True)
48
+ df['Hall'] = df['Hall'].astype(str).str.extract(r'(\d+号)')
49
+ df['StartTime_dt'] = pd.to_datetime(df['StartTime'], format='%H:%M', errors='coerce').apply(
50
+ lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
51
+ )
52
+ df['EndTime_dt'] = pd.to_datetime(df['EndTime'], format='%H:%M', errors='coerce').apply(
53
+ lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
54
+ )
55
+ df.loc[df['EndTime_dt'] < df['StartTime_dt'], 'EndTime_dt'] += timedelta(days=1)
56
+ df = df.sort_values(['Hall', 'StartTime_dt'])
57
+
58
+ merged_rows = []
59
+ for hall, group in df.groupby('Hall'):
60
+ group = group.sort_values('StartTime_dt')
61
+ current = None
62
+ for _, row in group.iterrows():
63
+ if current is None:
64
+ current = row.copy()
65
+ else:
66
+ if row['Movie'] == current['Movie']:
67
+ current['EndTime_dt'] = row['EndTime_dt']
68
+ else:
69
+ merged_rows.append(current)
70
+ current = row.copy()
71
+ if current is not None:
72
+ merged_rows.append(current)
73
+
74
+ merged_df = pd.DataFrame(merged_rows)
75
+
76
+ # 将开始时间统一提前10分钟,结束时间统一提前5分钟
77
+ merged_df['StartTime_dt'] = merged_df['StartTime_dt'] - timedelta(minutes=10)
78
+ merged_df['EndTime_dt'] = merged_df['EndTime_dt'] - timedelta(minutes=5)
79
+
80
+ merged_df['StartTime_str'] = merged_df['StartTime_dt'].dt.strftime('%H:%M')
81
+ merged_df['EndTime_str'] = merged_df['EndTime_dt'].dt.strftime('%H:%M')
82
+
83
+ return merged_df[['Hall', 'Movie', 'StartTime_str', 'EndTime_str']], date_str
84
+ except:
85
+ return None, date_str
86
+
87
+ def create_print_layout(data, date_str):
88
+ if data is None or data.empty:
89
+ return None
90
+
91
+ # 创建PNG图像
92
+ png_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
93
+ png_ax = png_fig.add_subplot(111)
94
+ png_ax.set_axis_off()
95
+
96
+ # 创建PDF图像 - 使用A4纸张大小 (210mm x 297mm)
97
+ # 增加图形大小以最大化利用A4纸张空间
98
+ pdf_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
99
+ pdf_ax = pdf_fig.add_subplot(111)
100
+ pdf_ax.set_axis_off()
101
+
102
+ # 为PDF设置更小的边距
103
+ pdf_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
104
+
105
+ # 处理两个图形的共同函数
106
+ def process_figure(fig, ax, is_pdf=False):
107
+ # Pre-load fonts
108
+ date_font = get_font(12)
109
+ # PDF版本使用稍大的字体
110
+ font_size_multiplier = 1.1 if is_pdf else 1.0
111
+ movie_font_size = 14 * font_size_multiplier
112
+ hall_font_size = movie_font_size * 0.8
113
+ hall_font = get_font(hall_font_size)
114
+ movie_font = get_font(movie_font_size)
115
+
116
+ ax.text(0.00, 1.00, date_str, fontsize=12 * font_size_multiplier, color='#A9A9A9',
117
+ ha='left', va='top', fontproperties=date_font, transform=ax.transAxes, zorder=2)
118
+
119
+ halls = sorted(data['Hall'].unique(), key=lambda h: int(h.replace('号','')) if h else 0)
120
+
121
+ total_lines = sum(len(data[data['Hall'] == hall]) for hall in halls) + (len(halls) - 1)
122
+ available_height = 0.98 - 0.05
123
+ line_spacing = available_height / total_lines if total_lines > 0 else 0.04
124
+ y_position = 0.98
125
+
126
+ for hall in halls:
127
+ hall_data = data[data['Hall'] == hall]
128
+ y_block_top = y_position
129
+ hall_num = hall.replace("号", "")
130
+ hall_text = f"${hall_num}^{{\\#}}$"
131
+ movie_count = 1
132
+
133
+ for _, row in hall_data.iterrows():
134
+ ax.text(0.03, y_position, hall_text if movie_count == 1 else "",
135
+ fontsize=hall_font_size, fontweight='bold',
136
+ ha='left', va='top', fontproperties=hall_font,
137
+ transform=ax.transAxes, zorder=2)
138
+
139
+ # 获取电影名前两个字的拼音首字母
140
+ pinyin_abbr = get_pinyin_abbr(row['Movie'])
141
+
142
+ # 电影名称左对齐,限制在0.2到0.6的区域内
143
+ ax.text(0.20, y_position, f"{movie_count}. {pinyin_abbr} {row['Movie']}",
144
+ fontsize=movie_font_size, ha='left', va='top', fontproperties=movie_font,
145
+ transform=ax.transAxes, zorder=2, clip_on=True,
146
+ bbox=dict(boxstyle="square,pad=0.0", fc="none", ec="none", alpha=0))
147
+
148
+ # 时间信息右对齐,固定在0.95位置
149
+ ax.text(0.95, y_position, f"{row['StartTime_str']} - {row['EndTime_str']}",
150
+ fontsize=movie_font_size, ha='right', va='top', fontproperties=movie_font,
151
+ transform=ax.transAxes, zorder=2)
152
+
153
+ y_position -= line_spacing
154
+ movie_count += 1
155
+
156
+ y_block_bottom = y_position
157
+ y_position -= line_spacing
158
+ rect = FancyBboxPatch((0.03, y_block_bottom), 0.94, y_block_top - y_block_bottom,
159
+ boxstyle="round,pad=0.005,rounding_size=0.005",
160
+ edgecolor='gray', facecolor='white',
161
+ linewidth=0.8, zorder=1, transform=ax.transAxes)
162
+ ax.add_patch(rect)
163
+
164
+ # 处理PNG图形
165
+ process_figure(png_fig, png_ax)
166
+
167
+ # 处理PDF图形
168
+ process_figure(pdf_fig, pdf_ax, is_pdf=True)
169
+
170
+ # 保存PNG图像
171
+ png_buffer = io.BytesIO()
172
+ png_fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0.1)
173
+ png_buffer.seek(0)
174
+ image_base64 = base64.b64encode(png_buffer.getvalue()).decode()
175
+ plt.close(png_fig)
176
+
177
+ # 保存PDF文件 - 使用最小边距
178
+ pdf_buffer = io.BytesIO()
179
+ with PdfPages(pdf_buffer) as pdf:
180
+ pdf.savefig(pdf_fig, bbox_inches='tight', pad_inches=0.05)
181
+ pdf_buffer.seek(0)
182
+ pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
183
+ plt.close(pdf_fig)
184
+
185
+ return {
186
+ 'png': f"data:image/png;base64,{image_base64}",
187
+ 'pdf': f"data:application/pdf;base64,{pdf_base64}"
188
+ }
189
+
190
+ def display_pdf(base64_pdf):
191
+ # 在Streamlit中显示PDF
192
+ pdf_display = f"""
193
+ <iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>
194
+ """
195
+ return pdf_display
196
+
197
+ def get_download_link(base64_data, file_type, filename):
198
+ # 创建下载链接
199
+ href = f'<a href="{base64_data}" download="{filename}">下载{file_type}文件</a>'
200
+ return href
201
+
202
+ st.set_page_config(page_title="LED 屏幕时间表打印", layout="wide")
203
+ st.title("LED 屏幕时间表打印")
204
+
205
+ uploaded_file = st.file_uploader("选择打开【放映时间核对表.xls】文件", type=["xls", "xlsx"])
206
+
207
+ if uploaded_file:
208
+ with st.spinner("正在处理中,请稍候..."):
209
+ schedule, date_str = process_schedule(uploaded_file)
210
+ if schedule is not None:
211
+ output = create_print_layout(schedule, date_str)
212
+
213
+ # 创建选项卡以切换PNG和PDF视图
214
+ tab1, tab2 = st.tabs(["PNG预览", "PDF预览"])
215
+
216
+ with tab1:
217
+ st.image(output['png'], use_container_width=True)
218
+ st.markdown(get_download_link(output['png'], "PNG", f"LED屏幕时间表_{date_str}.png"), unsafe_allow_html=True)
219
+
220
+ with tab2:
221
+ st.markdown(display_pdf(output['pdf']), unsafe_allow_html=True)
222
+ st.markdown(get_download_link(output['pdf'], "PDF", f"LED屏幕时间表_{date_str}.pdf"), unsafe_allow_html=True)
223
+ else:
224
+ st.error("无法处理文件,请检查文件格式或内容是否正确。")
225
+
226
+
227
+