Ethscriptions commited on
Commit
cd91a84
·
verified ·
1 Parent(s): 79debab

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -166
app.py CHANGED
@@ -5,28 +5,50 @@ import matplotlib.pyplot as plt
5
  import io
6
  import base64
7
  import matplotlib.gridspec as gridspec
8
- import matplotlib.font_manager as fm
9
  import math
 
 
 
10
 
11
- # --- CONSTANTS ---
12
  SPLIT_TIME = "17:30"
13
  BUSINESS_START = "09:30"
14
  BUSINESS_END = "01:30"
15
- BORDER_COLOR = '#A9A9A9'
16
  DATE_COLOR = '#A9A9A9'
17
- A5_WIDTH_IN = 5.83
18
- A5_HEIGHT_IN = 8.27
19
- DPI = 300
20
- NUM_COLS = 3
21
-
22
- # --- FONT SETUP ---
23
- # Attempt to load the specified font, with a safe fallback.
24
- try:
25
- # IMPORTANT: Place 'SimHei.ttf' in the same directory as the script.
26
- FONT_PROP = fm.FontProperties(fname='SimHei.ttf')
27
- except FileNotFoundError:
28
- st.warning("SimHei.ttf not found. Using a default sans-serif font. Chinese characters may not display correctly.")
29
- FONT_PROP = fm.FontProperties(family='sans-serif')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
 
32
  def process_schedule(file):
@@ -34,58 +56,82 @@ def process_schedule(file):
34
  try:
35
  # 读取 Excel,跳过前 8 行
36
  df = pd.read_excel(file, skiprows=8)
37
-
38
  # 提取所需列 (G9, H9, J9)
39
  df = df.iloc[:, [6, 7, 9]] # G, H, J 列
40
  df.columns = ['Hall', 'StartTime', 'EndTime']
41
-
42
  # 清理数据
43
  df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
44
- df = df[df['Hall'].astype(str).str.contains(r'\d', na=False)] # Ensure Hall has a digit
45
-
46
  # 转换影厅格式为 "#号" 格式
47
- df['Hall'] = df['Hall'].astype(str).str.extract(r'(\d+)').astype(str) + ' '
48
-
 
 
 
49
  # 转换时间为 datetime 对象
50
  base_date = datetime.today().date()
51
- df['StartTime'] = pd.to_datetime(df['StartTime'], errors='coerce').dt.time
52
- df['EndTime'] = pd.to_datetime(df['EndTime'], errors='coerce').dt.time
53
-
54
- df = df.dropna(subset=['StartTime', 'EndTime'])
55
-
56
- def combine_date_time(t):
57
- dt = datetime.combine(base_date, t)
58
- # Handle overnight shows (times past midnight like 1:30 AM are for the next day)
59
- if t.hour < int(BUSINESS_START.split(':')[0]) - 1:
60
- return dt + timedelta(days=1)
61
- return dt
62
-
63
- df['StartDateTime'] = df['StartTime'].apply(combine_date_time)
64
- df['EndDateTime'] = df['EndTime'].apply(combine_date_time)
65
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  # 按散场时间排序
67
- df = df.sort_values('EndDateTime')
68
-
69
  # 分割数据
70
- split_dt = datetime.combine(base_date, datetime.strptime(SPLIT_TIME, "%H:%M").time())
 
 
 
 
 
 
71
 
72
- part1 = df[df['EndDateTime'] <= split_dt].copy()
73
- part2 = df[df['EndDateTime'] > split_dt].copy()
74
-
75
  # 格式化时间显示
76
  for part in [part1, part2]:
77
- part['EndTimeStr'] = part['EndDateTime'].dt.strftime('%-I:%M')
78
-
79
  # 关键修改:精确读取C6单元格
80
  date_df = pd.read_excel(
81
  file,
82
- skiprows=5, # 跳过前5行(0-4)
83
- nrows=1, # 只读1行
84
- usecols=[2], # 第三列(C列)
85
- header=None # 无表头
86
  )
87
  date_cell = date_df.iloc[0, 0]
88
-
89
  try:
90
  # 处理不同日期格式
91
  if isinstance(date_cell, str):
@@ -94,133 +140,142 @@ def process_schedule(file):
94
  date_str = pd.to_datetime(date_cell).strftime('%Y-%m-%d')
95
  except:
96
  date_str = datetime.today().strftime('%Y-%m-%d')
97
-
98
- return part1[['Hall', 'EndTimeStr']], part2[['Hall', 'EndTimeStr']], date_str
99
-
100
  except Exception as e:
101
  st.error(f"处理文件时出错: {str(e)}")
102
  return None, None, None
103
 
104
-
105
  def create_print_layout(data, title, date_str):
106
- """创建符合新要求的打印布局 (PNG 和 PDF)"""
107
  if data.empty:
108
  return None
109
 
110
- # --- 1. 动态字体大小计算 ---
111
- # 目标:让最长的文本占单元格宽度的90%
112
- longest_text = ""
113
- if not data.empty:
114
- data['combined_text'] = data['Hall'] + data['EndTimeStr']
115
- if not data['combined_text'].empty:
116
- longest_text = data.loc[data['combined_text'].str.len().idxmax(), 'combined_text']
117
 
118
- # 估算单元格宽度 (A5纸张,减去边距和间距)
119
- fig_width_in = A5_WIDTH_IN
120
- margin_frac = 0.04 # 4% margin on each side
121
- wspace_frac = 0.05 # 5% space between columns
122
 
123
- # 可用绘图宽度
124
- drawable_width_in = fig_width_in * (1 - 2 * margin_frac)
125
- # 宽度被3个子图和2个间隙瓜分
126
- # 3*cell_w + 2*(cell_w*wspace) = drawable_width
127
- cell_width_in = drawable_width_in / (NUM_COLS + (NUM_COLS - 1) * wspace_frac)
128
 
129
- main_fontsize = 30 # Default start size
130
- if longest_text:
131
- # 估算文本宽度 (这是一个经验法则,0.7是中英混合字体的估算因子)
132
- # 文本宽度(点) ≈ 字符数 * 字体大小(点) * 因子
133
- estimated_text_width_pt = len(longest_text) * main_fontsize * 0.7
134
- target_width_pt = (cell_width_in * 0.9) * 72 # 目标宽度 (英寸*0.9 -> 点)
 
 
 
135
 
136
- if estimated_text_width_pt > 0:
137
- ratio = target_width_pt / estimated_text_width_pt
138
- main_fontsize *= ratio
139
-
140
- main_fontsize = max(10, min(main_fontsize, 50)) # 限制字体大小在合理范围
141
- index_fontsize = main_fontsize * 0.45 # 序号字体大小
142
-
143
- # --- 2. 准备绘图 ---
144
- def generate_figure():
145
- fig = plt.figure(figsize=(A5_WIDTH_IN, A5_HEIGHT_IN), dpi=DPI)
146
- fig.subplots_adjust(left=margin_frac, right=1-margin_frac, top=0.95, bottom=margin_frac, hspace=0.2, wspace=wspace_frac)
147
-
148
- total_items = len(data)
149
- num_rows = math.ceil(total_items / NUM_COLS)
150
-
151
- # 创建网格
152
- gs = gridspec.GridSpec(num_rows + 1, NUM_COLS, height_ratios=[0.2] + [1] * num_rows, figure=fig)
153
-
154
- # 绘制日期标题
155
- ax_date = fig.add_subplot(gs[0, :])
156
- ax_date.text(0, 0.5, f"{date_str} {title}", fontproperties=FONT_PROP,
157
- fontsize=main_fontsize * 0.5, color=DATE_COLOR, fontweight='bold',
158
- ha='left', va='center', transform=ax_date.transAxes)
159
- ax_date.set_axis_off()
160
-
161
- # 准备Z字形排列的数据
162
- data_values = data[['Hall', 'EndTimeStr']].values.tolist()
163
- while len(data_values) % NUM_COLS != 0:
164
- data_values.append(['', ''])
165
 
166
- rows_per_col_layout = math.ceil(len(data_values) / NUM_COLS)
167
- sorted_data = [['', '']] * len(data_values)
168
- for i, item in enumerate(data_values):
169
- if item[0] and item[1]:
170
- row_in_col = i % rows_per_col_layout
171
- col_idx = i // rows_per_col_layout
172
- new_index = row_in_col * NUM_COLS + col_idx
173
- if new_index < len(sorted_data):
174
- sorted_data[new_index] = item
175
-
176
- item_counter = 0
177
- for idx, (hall, end_time) in enumerate(sorted_data):
178
- if hall and end_time:
179
- item_counter += 1
180
- row_grid = idx // NUM_COLS + 1
181
- col_grid = idx % NUM_COLS
182
-
183
- if row_grid < num_rows + 1:
184
- ax = fig.add_subplot(gs[row_grid, col_grid])
185
-
186
- # --- 3. 绘制单元格 ---
187
- # a. 设置点状虚线边框
188
- for spine in ax.spines.values():
189
- spine.set_linestyle((0, (1, 2))) # (offset, (on, off)) tuple for dotted
190
- spine.set_edgecolor(BORDER_COLOR)
191
- spine.set_linewidth(1)
192
-
193
- # b. 居中绘制主要文本
194
- display_text = f"{hall}{end_time}"
195
- ax.text(0.5, 0.5, display_text, fontproperties=FONT_PROP,
196
- fontsize=main_fontsize, fontweight='bold',
197
- ha='center', va='center', transform=ax.transAxes)
198
-
199
- # c. 在左上角添加序号
200
- ax.text(0.05, 0.95, str(item_counter), fontproperties=FONT_PROP,
201
- fontsize=index_fontsize, color='grey',
202
- ha='left', va='top', transform=ax.transAxes)
203
-
204
- ax.set_xticks([])
205
- ax.set_yticks([])
206
- return fig
207
-
208
- # --- 4. 保存为 PNG 和 PDF ---
209
- fig_for_output = generate_figure()
210
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  # 保存 PNG
212
  png_buffer = io.BytesIO()
213
- fig_for_output.savefig(png_buffer, format='png', dpi=DPI)
214
  png_buffer.seek(0)
215
  png_base64 = base64.b64encode(png_buffer.getvalue()).decode()
216
 
217
  # 保存 PDF
218
  pdf_buffer = io.BytesIO()
219
- fig_for_output.savefig(pdf_buffer, format='pdf', dpi=DPI)
220
  pdf_buffer.seek(0)
221
  pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
222
-
223
- plt.close(fig_for_output)
224
 
225
  return {
226
  'png': f'data:image/png;base64,{png_base64}',
@@ -229,28 +284,30 @@ def create_print_layout(data, title, date_str):
229
 
230
  def display_pdf(base64_pdf):
231
  """在Streamlit中嵌入显示PDF"""
232
- pdf_display = f'<iframe src="data:application/pdf;base64,{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
233
  return pdf_display
234
 
235
- # --- Streamlit UI ---
236
  st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
237
  st.title("散厅时间快捷打印")
238
 
239
- uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls", "xlsx"])
 
 
 
240
 
241
  if uploaded_file:
242
- # Rename columns in process_schedule call to match new names
243
- part1_data, part2_data, date_str = process_schedule(uploaded_file)
244
 
245
- if part1_data is not None and part2_data is not None:
246
- # Pass the dataframes with renamed 'EndTimeStr' column
247
- part1_output = create_print_layout(part1_data, "A", date_str)
248
- part2_output = create_print_layout(part2_data, "C", date_str)
249
 
250
  col1, col2 = st.columns(2)
251
 
252
  with col1:
253
- st.subheader("白班散场预览(结束时间 ≤ 17:30)")
254
  if part1_output:
255
  tab1_1, tab1_2 = st.tabs(["PDF 预览", "PNG 预览"])
256
  with tab1_1:
@@ -261,7 +318,7 @@ if uploaded_file:
261
  st.info("白班部分没有数据")
262
 
263
  with col2:
264
- st.subheader("夜班散场预览(结束时间 > 17:30)")
265
  if part2_output:
266
  tab2_1, tab2_2 = st.tabs(["PDF 预览", "PNG 预览"])
267
  with tab2_1:
 
5
  import io
6
  import base64
7
  import matplotlib.gridspec as gridspec
 
8
  import math
9
+ from matplotlib.backends.backend_pdf import PdfPages
10
+ import matplotlib.font_manager as fm # 新增导入
11
+ import os # 新增导入
12
 
13
+ # --- 全局常量 ---
14
  SPLIT_TIME = "17:30"
15
  BUSINESS_START = "09:30"
16
  BUSINESS_END = "01:30"
17
+ BORDER_COLOR = 'grey'
18
  DATE_COLOR = '#A9A9A9'
19
+ FONT_PATH = 'SimHei.ttf' # 字体文件路径
20
+
21
+ # --- 字体设置 ---
22
+ FONT_BOLD = None
23
+ FONT_REGULAR = None
24
+ FONT_WARNING_ISSUED = False
25
+
26
+ def get_font_properties():
27
+ """加载字体文件并返回 FontProperties 对象"""
28
+ global FONT_BOLD, FONT_REGULAR, FONT_WARNING_ISSUED
29
+ if FONT_BOLD is None: # 只加载一次
30
+ if os.path.exists(FONT_PATH):
31
+ try:
32
+ FONT_BOLD = fm.FontProperties(fname=FONT_PATH, weight='bold')
33
+ FONT_REGULAR = fm.FontProperties(fname=FONT_PATH, weight='normal')
34
+ except Exception as e:
35
+ if not FONT_WARNING_ISSUED:
36
+ st.warning(f"加载字体 '{FONT_PATH}' 失败: {e}. 将使用默认字体。")
37
+ FONT_WARNING_ISSUED = True
38
+ else:
39
+ if not FONT_WARNING_ISSUED:
40
+ st.warning(f"字体文件 '{FONT_PATH}' 未找到。请将其放置在代码同目录下。将使用默认字体。")
41
+ FONT_WARNING_ISSUED = True
42
+
43
+ def get_font_args(style='regular'):
44
+ """根据请求的样式返回字体参数字典"""
45
+ get_font_properties() # 确保字体已加载
46
+ if style == 'bold' and FONT_BOLD:
47
+ return {'fontproperties': FONT_BOLD}
48
+ if style == 'regular' and FONT_REGULAR:
49
+ return {'fontproperties': FONT_REGULAR}
50
+ # Fallback
51
+ return {'fontfamily': 'sans-serif', 'weight': 'bold' if style == 'bold' else 'normal'}
52
 
53
 
54
  def process_schedule(file):
 
56
  try:
57
  # 读取 Excel,跳过前 8 行
58
  df = pd.read_excel(file, skiprows=8)
59
+
60
  # 提取所需列 (G9, H9, J9)
61
  df = df.iloc[:, [6, 7, 9]] # G, H, J 列
62
  df.columns = ['Hall', 'StartTime', 'EndTime']
63
+
64
  # 清理数据
65
  df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
66
+
 
67
  # 转换影厅格式为 "#号" 格式
68
+ df['Hall'] = df['Hall'].str.extract(r'(\d+)').astype(str) + ' '
69
+
70
+ # 保存原始时间字符串用于诊断
71
+ df['original_end'] = df['EndTime']
72
+
73
  # 转换时间为 datetime 对象
74
  base_date = datetime.today().date()
75
+ df['StartTime'] = pd.to_datetime(df['StartTime'])
76
+ df['EndTime'] = pd.to_datetime(df['EndTime'])
77
+
78
+ # 设置基准时间
79
+ business_start = datetime.strptime(f"{base_date} {BUSINESS_START}", "%Y-%m-%d %H:%M")
80
+ business_end = datetime.strptime(f"{base_date} {BUSINESS_END}", "%Y-%m-%d %H:%M")
81
+
82
+ # 处理跨天情况
83
+ if business_end < business_start:
84
+ business_end += timedelta(days=1)
85
+
86
+ # 标准化所有时间到同一天
87
+ for idx, row in df.iterrows():
88
+ end_time = row['EndTime']
89
+ if end_time.hour < 9:
90
+ df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
91
+
92
+ if row['StartTime'].hour >= 21 and end_time.hour < 9:
93
+ df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
94
+
95
+ # 筛选营业时间内的场次
96
+ df['time_for_comparison'] = df['EndTime'].apply(
97
+ lambda x: datetime.combine(base_date, x.time())
98
+ )
99
+
100
+ df.loc[df['time_for_comparison'].dt.hour < 9, 'time_for_comparison'] += timedelta(days=1)
101
+
102
+ valid_times = (
103
+ ((df['time_for_comparison'] >= datetime.combine(base_date, business_start.time())) &
104
+ (df['time_for_comparison'] <= datetime.combine(base_date + timedelta(days=1), business_end.time())))
105
+ )
106
+
107
+ df = df[valid_times]
108
+
109
  # 按散场时间排序
110
+ df = df.sort_values('EndTime')
111
+
112
  # 分割数据
113
+ split_time = datetime.strptime(f"{base_date} {SPLIT_TIME}", "%Y-%m-%d %H:%M")
114
+ split_time_for_comparison = df['time_for_comparison'].apply(
115
+ lambda x: datetime.combine(base_date, split_time.time())
116
+ )
117
+
118
+ part1 = df[df['time_for_comparison'] <= split_time_for_comparison].copy()
119
+ part2 = df[df['time_for_comparison'] > split_time_for_comparison].copy()
120
 
 
 
 
121
  # 格式化时间显示
122
  for part in [part1, part2]:
123
+ part['EndTime'] = part['EndTime'].dt.strftime('%-H:%M')
124
+
125
  # 关键修改:精确读取C6单元格
126
  date_df = pd.read_excel(
127
  file,
128
+ skiprows=5, # 跳过前5行(0-4)
129
+ nrows=1, # 只读1行
130
+ usecols=[2], # 第三列(C列)
131
+ header=None # 无表头
132
  )
133
  date_cell = date_df.iloc[0, 0]
134
+
135
  try:
136
  # 处理不同日期格式
137
  if isinstance(date_cell, str):
 
140
  date_str = pd.to_datetime(date_cell).strftime('%Y-%m-%d')
141
  except:
142
  date_str = datetime.today().strftime('%Y-%m-%d')
143
+
144
+ return part1[['Hall', 'EndTime']], part2[['Hall', 'EndTime']], date_str
145
+
146
  except Exception as e:
147
  st.error(f"处理文件时出错: {str(e)}")
148
  return None, None, None
149
 
 
150
  def create_print_layout(data, title, date_str):
151
+ """创建基于精确表格布局的打印页面 (PNG 和 PDF)"""
152
  if data.empty:
153
  return None
154
 
155
+ # --- 1. 布局和尺寸计算 ---
156
+ A5_WIDTH_IN, A5_HEIGHT_IN = 5.83, 8.27
157
+ DPI = 300
158
+ MARGIN = 0.25 # 页边距 (英寸)
159
+ HEADER_HEIGHT_IN = 0.3 # 日期标题栏高度 (英寸)
 
 
160
 
161
+ content_width = A5_WIDTH_IN - (2 * MARGIN)
162
+ content_height = A5_HEIGHT_IN - (2 * MARGIN)
 
 
163
 
164
+ total_items = len(data)
165
+ num_cols = 3
166
+ num_rows = math.ceil(total_items / num_cols)
 
 
167
 
168
+ grid_height = content_height - HEADER_HEIGHT_IN
169
+ cell_width = content_width / num_cols
170
+ cell_height = grid_height / num_rows if num_rows > 0 else 0
171
+
172
+ # --- 2. 动态字体大小计算 ---
173
+ def calculate_font_size(fig):
174
+ # 使用一个典型的宽字符串来估算
175
+ sample_text = "8 88:88"
176
+ target_width = cell_width * 0.9 # 目标宽度为单元格宽度的90%
177
 
178
+ # 用初始字号10来测量
179
+ initial_fontsize = 10
180
+ t = fig.text(0.5, 0.5, sample_text, fontsize=initial_fontsize, **get_font_args('bold'))
181
+ # 渲染并获取边界框(以英寸为单位)
182
+ bbox = t.get_window_extent(renderer=fig.canvas.get_renderer())
183
+ text_width_in = bbox.width / fig.get_dpi()
184
+ t.remove() # 移除临时文本
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
+ # 根据宽度比例调整字体大小
187
+ if text_width_in > 0:
188
+ scale_factor = target_width / text_width_in
189
+ return initial_fontsize * scale_factor
190
+ return initial_fontsize # 备用
191
+
192
+ # --- 3. 数据准备 (Z字形排序) ---
193
+ data_values = data.values.tolist()
194
+ while len(data_values) % num_cols != 0:
195
+ data_values.append(['', ''])
196
+
197
+ rows_per_col_layout = math.ceil(len(data_values) / num_cols)
198
+ sorted_data = [['', '']] * len(data_values)
199
+ for i, item in enumerate(data_values):
200
+ if item[0] and item[1]:
201
+ row_in_col = i % rows_per_col_layout
202
+ col_idx = i // rows_per_col_layout
203
+ new_index = row_in_col * num_cols + col_idx
204
+ if new_index < len(sorted_data):
205
+ sorted_data[new_index] = item
206
+
207
+ # --- 4. 绘图 ---
208
+ fig = plt.figure(figsize=(A5_WIDTH_IN, A5_HEIGHT_IN), dpi=DPI)
209
+ final_fontsize = calculate_font_size(fig)
210
+ index_fontsize = final_fontsize * 0.35
211
+ date_fontsize = final_fontsize * 0.5
212
+
213
+ # 绘制日期头
214
+ header_ax = fig.add_axes([MARGIN / A5_WIDTH_IN, (A5_HEIGHT_IN - MARGIN - HEADER_HEIGHT_IN) / A5_HEIGHT_IN, content_width / A5_WIDTH_IN, HEADER_HEIGHT_IN / A5_HEIGHT_IN])
215
+ header_ax.text(0.01, 0.5, f"{date_str} {title}",
216
+ fontsize=date_fontsize,
217
+ color=DATE_COLOR,
218
+ ha='left',
219
+ va='center',
220
+ transform=header_ax.transAxes,
221
+ **get_font_args('bold'))
222
+ header_ax.set_axis_off()
223
+
224
+ # 绘制每个单元格
225
+ for idx, (hall, end_time) in enumerate(sorted_data):
226
+ if hall and end_time:
227
+ row_grid = idx // num_cols
228
+ col_grid = idx % num_cols
229
+
230
+ # 计算单元格的绝对位置(从左下角开始,单位:英寸)
231
+ x_pos_in = MARGIN + (col_grid * cell_width)
232
+ y_pos_in = MARGIN + ( (num_rows - 1 - row_grid) * cell_height )
233
+
234
+ # 转换为 Figure 的相对坐标 [left, bottom, width, height]
235
+ ax_rect = [x_pos_in / A5_WIDTH_IN, y_pos_in / A5_HEIGHT_IN, cell_width / A5_WIDTH_IN, cell_height / A5_HEIGHT_IN]
236
+ ax = fig.add_axes(ax_rect)
237
+
238
+ # 绘制主要文本
239
+ display_text = f"{hall}{end_time}"
240
+ ax.text(0.5, 0.5, display_text,
241
+ fontsize=final_fontsize,
242
+ ha='center',
243
+ va='center',
244
+ transform=ax.transAxes,
245
+ **get_font_args('bold'))
246
+
247
+ # 绘制左上角序号
248
+ ax.text(0.08, 0.92, str(idx + 1),
249
+ fontsize=index_fontsize,
250
+ color=BORDER_COLOR,
251
+ ha='left',
252
+ va='top',
253
+ transform=ax.transAxes,
254
+ **get_font_args('regular'))
255
+
256
+ # 设置边框为灰色点状虚线
257
+ for spine in ax.spines.values():
258
+ spine.set_color(BORDER_COLOR)
259
+ spine.set_linestyle(':')
260
+ spine.set_linewidth(1)
261
+
262
+ ax.set_xticks([])
263
+ ax.set_yticks([])
264
+
265
+ # --- 5. 保存到内存 ---
266
  # 保存 PNG
267
  png_buffer = io.BytesIO()
268
+ fig.savefig(png_buffer, format='png', pad_inches=0)
269
  png_buffer.seek(0)
270
  png_base64 = base64.b64encode(png_buffer.getvalue()).decode()
271
 
272
  # 保存 PDF
273
  pdf_buffer = io.BytesIO()
274
+ fig.savefig(pdf_buffer, format='pdf', pad_inches=0)
275
  pdf_buffer.seek(0)
276
  pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
277
+
278
+ plt.close(fig)
279
 
280
  return {
281
  'png': f'data:image/png;base64,{png_base64}',
 
284
 
285
  def display_pdf(base64_pdf):
286
  """在Streamlit中嵌入显示PDF"""
287
+ pdf_display = f'<iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
288
  return pdf_display
289
 
290
+ # --- Streamlit 界面 ---
291
  st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
292
  st.title("散厅时间快捷打印")
293
 
294
+ # 检查并加载字体,如有需要会显示警告
295
+ get_font_properties()
296
+
297
+ uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls"])
298
 
299
  if uploaded_file:
300
+ part1, part2, date_str = process_schedule(uploaded_file)
 
301
 
302
+ if part1 is not None and part2 is not None:
303
+ # 生成包含 PNG PDF 的字典
304
+ part1_output = create_print_layout(part1, "A", date_str)
305
+ part2_output = create_print_layout(part2, "C", date_str)
306
 
307
  col1, col2 = st.columns(2)
308
 
309
  with col1:
310
+ st.subheader("白班散场预览(时间 ≤ 17:30)")
311
  if part1_output:
312
  tab1_1, tab1_2 = st.tabs(["PDF 预览", "PNG 预览"])
313
  with tab1_1:
 
318
  st.info("白班部分没有数据")
319
 
320
  with col2:
321
+ st.subheader("夜班散场预览(时间 > 17:30)")
322
  if part2_output:
323
  tab2_1, tab2_2 = st.tabs(["PDF 预览", "PNG 预览"])
324
  with tab2_1: