Ethscriptions commited on
Commit
34b21e7
·
verified ·
1 Parent(s): 7704499

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -171
app.py CHANGED
@@ -7,224 +7,240 @@ import base64
7
  import matplotlib.gridspec as gridspec
8
  import math
9
  from matplotlib.backends.backend_pdf import PdfPages
10
- from matplotlib.patches import Rectangle # Replaced FancyBboxPatch
11
 
12
- # --- Constants ---
13
  SPLIT_TIME = "17:30"
14
  BUSINESS_START = "09:30"
15
  BUSINESS_END = "01:30"
16
- BORDER_COLOR = '#A9A9A9'
17
  DATE_COLOR = '#A9A9A9'
18
- SEQ_COLOR = '#A9A9A9' # Color for the new serial number
 
19
 
20
  def process_schedule(file):
21
  """处理上传的 Excel 文件,生成排序和分组后的打印内容"""
22
  try:
23
  # 读取 Excel,跳过前 8 行
24
  df = pd.read_excel(file, skiprows=8)
25
-
26
  # 提取所需列 (G9, H9, J9)
27
  df = df.iloc[:, [6, 7, 9]] # G, H, J 列
28
  df.columns = ['Hall', 'StartTime', 'EndTime']
29
-
30
  # 清理数据
31
  df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
32
-
33
  # 转换影厅格式为 "#号" 格式
34
- df['Hall'] = df['Hall'].str.extract(r'(\d+)').astype(str) + ' '
35
-
36
  # 保存原始时间字符串用于诊断
37
  df['original_end'] = df['EndTime']
38
-
39
  # 转换时间为 datetime 对象
40
  base_date = datetime.today().date()
41
- # Using errors='coerce' will turn unparseable times into NaT (Not a Time)
42
- df['StartTime'] = pd.to_datetime(df['StartTime'], errors='coerce')
43
- df['EndTime'] = pd.to_datetime(df['EndTime'], errors='coerce')
44
- df = df.dropna(subset=['StartTime', 'EndTime']) # Drop rows where time conversion failed
45
-
46
  # 设置基准时间
47
- business_start_time = datetime.strptime(BUSINESS_START, "%H:%M").time()
48
- business_end_time = datetime.strptime(BUSINESS_END, "%H:%M").time()
49
-
50
- # 处理跨天情况:结束时间小于开始时间,则结束时间加一天
51
- # This logic handles cases like 9:30 AM to 1:30 AM (next day)
52
- df['EndTime_adjusted'] = df.apply(
53
- lambda row: row['EndTime'] + timedelta(days=1) if row['EndTime'].time() < row['StartTime'].time() else row['EndTime'],
54
- axis=1
55
- )
56
 
57
- # 按散场时间排序 (using the adjusted time)
58
- df = df.sort_values('EndTime_adjusted')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
 
 
 
 
 
 
 
60
  # 分割数据
61
- split_dt = datetime.strptime(SPLIT_TIME, "%H:%M").time()
62
 
63
- part1 = df[df['EndTime_adjusted'].dt.time <= split_dt].copy()
64
- part2 = df[df['EndTime_adjusted'].dt.time > split_dt].copy()
65
-
66
- # 格式化时间显示 (use original EndTime for display)
67
  for part in [part1, part2]:
68
- part['EndTime_formatted'] = part['EndTime'].dt.strftime('%-I:%M')
69
-
70
- # 读取日期单元格 C6
71
- date_df = pd.read_excel(file, skiprows=5, nrows=1, usecols=[2], header=None)
 
 
 
 
 
 
72
  date_cell = date_df.iloc[0, 0]
73
-
74
  try:
75
  if isinstance(date_cell, str):
76
- # Assuming format like '2023-10-27'
77
  date_str = datetime.strptime(date_cell, '%Y-%m-%d').strftime('%Y-%m-%d')
78
  else:
79
- # Assuming it's a datetime object
80
  date_str = pd.to_datetime(date_cell).strftime('%Y-%m-%d')
81
  except:
82
  date_str = datetime.today().strftime('%Y-%m-%d')
83
-
84
- return part1[['Hall', 'EndTime_formatted']], part2[['Hall', 'EndTime_formatted']], date_str
85
-
86
  except Exception as e:
87
  st.error(f"处理文件时出错: {str(e)}")
88
  return None, None, None
89
 
90
-
91
  def create_print_layout(data, title, date_str):
92
- """
93
- 创建符合新要求的打印布局 (PNG 和 PDF)。
94
- 1. 动态计算边距。
95
- 2. 使用灰色虚线圆点作为单元格边框。
96
- 3. 单元格内容区域为单元格的90%。
97
- 4. 在左上角添加灰色序号。
98
- """
99
  if data.empty:
100
  return None
101
 
102
- # --- Constants ---
103
- A5_WIDTH_IN = 5.83
104
- A5_HEIGHT_IN = 8.27
105
- DPI = 300
106
- NUM_COLS = 3
107
-
108
- # --- Setup Figure ---
109
- fig = plt.figure(figsize=(A5_WIDTH_IN, A5_HEIGHT_IN), dpi=DPI)
110
-
111
- # --- Font Setup ---
112
- plt.rcParams['font.family'] = 'sans-serif'
113
- plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'Heiti TC', 'sans-serif']
114
-
115
-
116
- # --- Data Preparation ---
117
  total_items = len(data)
118
- # Augment data with an original index for numbering
119
- data_values_with_index = [(i, row) for i, row in enumerate(data.values.tolist())]
120
-
121
- # Pad data to be a multiple of NUM_COLS
122
- padded_total = math.ceil(total_items / NUM_COLS) * NUM_COLS
123
- while len(data_values_with_index) < padded_total:
124
- data_values_with_index.append((None, ['', '']))
125
-
126
- num_rows = padded_total // NUM_COLS
127
-
128
- # --- Layout Calculation (Request 1) ---
129
- if num_rows > 0:
130
- # "A5 paper height / num_rows / 4 is the padding for all sides"
131
- padding_in = (A5_HEIGHT_IN / num_rows / 4)
132
- # Cap padding to prevent it from being excessively large
133
- padding_in = min(padding_in, 0.5)
134
- else:
135
- padding_in = 0.25 # Default padding if no rows
136
-
137
- # Convert padding to relative figure coordinates for subplots_adjust
138
- left_margin = padding_in / A5_WIDTH_IN
139
- right_margin = 1 - left_margin
140
- bottom_margin = padding_in / A5_HEIGHT_IN
141
- top_margin = 1 - bottom_margin
142
-
143
- # Adjust overall figure margins
144
- fig.subplots_adjust(left=left_margin, right=right_margin, top=top_margin, bottom=bottom_margin, hspace=0.4, wspace=0.4)
145
-
146
- # --- Grid & Font Size ---
147
- gs = gridspec.GridSpec(num_rows + 1, NUM_COLS, height_ratios=[0.2] + [1] * num_rows, figure=fig)
148
 
149
- if num_rows > 0:
150
- content_area_height_in = A5_HEIGHT_IN * (top_margin - bottom_margin)
151
- cell_height_in = content_area_height_in / num_rows * (1 - fig.subplotpars.hspace)
152
- base_fontsize = min(40, max(10, cell_height_in * 72 * 0.4)) # 72 pt/inch, 40% of cell height
153
- else:
154
- base_fontsize = 20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- # --- Z-Sort (Column-major) Data for Layout ---
157
- rows_per_col_layout = num_rows
158
- sorted_data = [(None, ['',''])] * padded_total
159
- for i, item_tuple in enumerate(data_values_with_index):
160
- if item_tuple[0] is not None:
161
- original_data_index = i # Index from the time-sorted list
162
- row_in_col = original_data_index % rows_per_col_layout
163
- col_idx = original_data_index // rows_per_col_layout
164
- new_grid_index = row_in_col * NUM_COLS + col_idx
165
- if new_grid_index < len(sorted_data):
166
- sorted_data[new_grid_index] = item_tuple
167
 
168
- # --- Drawing Logic ---
169
- for grid_idx, item_tuple in enumerate(sorted_data):
170
- original_index, (hall, end_time) = item_tuple
171
 
172
- if original_index is not None:
173
- row_grid = grid_idx // NUM_COLS + 1 # +1 because date is in row 0
174
- col_grid = grid_idx % NUM_COLS
175
-
176
- ax = fig.add_subplot(gs[row_grid, col_grid])
177
- ax.set_axis_off()
178
-
179
- # --- Cell Border (Request 2) & Content Area (Request 3) ---
180
- # Draw a dotted rectangle. Content will be placed inside this.
181
- # Making the rect slightly smaller creates a visual 90% area.
182
- cell_border = Rectangle((0.05, 0.05), 0.9, 0.9,
183
- edgecolor=BORDER_COLOR,
184
- facecolor='none',
185
- linestyle=(0, (1, 1.5)), # Dotted line with round caps
186
- linewidth=1,
187
- transform=ax.transAxes,
188
- clip_on=False)
189
- ax.add_patch(cell_border)
 
 
190
 
191
- # --- Cell Content ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  display_text = f"{hall}{end_time}"
 
193
  ax.text(0.5, 0.5, display_text,
194
  fontsize=base_fontsize,
195
  fontweight='bold',
196
- ha='center', va='center',
197
- transform=ax.transAxes)
198
-
199
- # --- Cell Numbering (Request 4) ---
200
- # Serial number is original_index + 1
201
- ax.text(0.12, 0.82, str(original_index + 1),
202
- fontsize=base_fontsize * 0.5,
203
- color=SEQ_COLOR,
 
 
 
204
  fontweight='normal',
205
- ha='center', va='center',
 
206
  transform=ax.transAxes)
207
-
208
- # --- Date Header ---
209
- ax_date = fig.add_subplot(gs[0, :])
210
- ax_date.set_axis_off()
211
- ax_date.text(0, 0.5, f"{date_str} {title}",
212
- fontsize=base_fontsize * 0.6,
213
- color=DATE_COLOR,
214
- fontweight='bold',
215
- ha='left', va='center',
216
- transform=ax_date.transAxes)
217
-
218
- # --- Save to Buffers ---
219
- # Save PNG
 
 
 
 
 
 
220
  png_buffer = io.BytesIO()
221
- fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0.02)
222
  png_buffer.seek(0)
223
  png_base64 = base64.b64encode(png_buffer.getvalue()).decode()
224
 
225
- # Save PDF
226
  pdf_buffer = io.BytesIO()
227
- fig.savefig(pdf_buffer, format='pdf', bbox_inches='tight', pad_inches=0.02)
228
  pdf_buffer.seek(0)
229
  pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
230
 
@@ -237,45 +253,43 @@ def create_print_layout(data, title, date_str):
237
 
238
  def display_pdf(base64_pdf):
239
  """在Streamlit中嵌入显示PDF"""
240
- pdf_display = f'<iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
241
  return pdf_display
242
 
243
- # --- Streamlit UI ---
244
  st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
245
  st.title("散厅时间快捷打印")
246
 
247
  uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls", "xlsx"])
248
 
249
  if uploaded_file:
250
- # Use new column name 'EndTime_formatted' for display
251
  part1, part2, date_str = process_schedule(uploaded_file)
 
252
  if part1 is not None and part2 is not None:
253
- part1_data_for_layout = part1[['Hall', 'EndTime_formatted']]
254
- part2_data_for_layout = part2[['Hall', 'EndTime_formatted']]
255
-
256
- part1_output = create_print_layout(part1_data_for_layout, "A", date_str)
257
- part2_output = create_print_layout(part2_data_for_layout, "C", date_str)
258
 
259
  col1, col2 = st.columns(2)
260
 
261
  with col1:
262
- st.subheader("白班散场预览(散场时间 17:30)")
263
  if part1_output:
264
  tab1_1, tab1_2 = st.tabs(["PDF 预览", "PNG 预览"])
265
  with tab1_1:
266
  st.markdown(display_pdf(part1_output['pdf']), unsafe_allow_html=True)
267
  with tab1_2:
268
- st.image(part1_output['png'])
269
  else:
270
  st.info("白班部分没有数据")
271
 
272
  with col2:
273
- st.subheader("夜班散场预览(散场时间 > 17:30)")
274
  if part2_output:
275
  tab2_1, tab2_2 = st.tabs(["PDF 预览", "PNG 预览"])
276
  with tab2_1:
277
  st.markdown(display_pdf(part2_output['pdf']), unsafe_allow_html=True)
278
  with tab2_2:
279
- st.image(part2_output['png'])
280
  else:
281
- st.info("夜班部分没有数据")
 
7
  import matplotlib.gridspec as gridspec
8
  import math
9
  from matplotlib.backends.backend_pdf import PdfPages
 
10
 
11
+ # 常量定义
12
  SPLIT_TIME = "17:30"
13
  BUSINESS_START = "09:30"
14
  BUSINESS_END = "01:30"
15
+ BORDER_COLOR = 'grey' # 边框颜色更新为灰色
16
  DATE_COLOR = '#A9A9A9'
17
+ A5_WIDTH_IN = 5.83 # A5 宽度 (英寸)
18
+ A5_HEIGHT_IN = 8.27 # A5 高度 (英寸)
19
 
20
  def process_schedule(file):
21
  """处理上传的 Excel 文件,生成排序和分组后的打印内容"""
22
  try:
23
  # 读取 Excel,跳过前 8 行
24
  df = pd.read_excel(file, skiprows=8)
25
+
26
  # 提取所需列 (G9, H9, J9)
27
  df = df.iloc[:, [6, 7, 9]] # G, H, J 列
28
  df.columns = ['Hall', 'StartTime', 'EndTime']
29
+
30
  # 清理数据
31
  df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
32
+
33
  # 转换影厅格式为 "#号" 格式
34
+ df['Hall'] = df['Hall'].str.extract(r'(\d+)').astype(str) + '号厅 '
35
+
36
  # 保存原始时间字符串用于诊断
37
  df['original_end'] = df['EndTime']
38
+
39
  # 转换时间为 datetime 对象
40
  base_date = datetime.today().date()
41
+ df['StartTime'] = pd.to_datetime(df['StartTime'])
42
+ df['EndTime'] = pd.to_datetime(df['EndTime'])
43
+
 
 
44
  # 设置基准时间
45
+ business_start = datetime.strptime(f"{base_date} {BUSINESS_START}", "%Y-%m-%d %H:%M")
46
+ business_end = datetime.strptime(f"{base_date} {BUSINESS_END}", "%Y-%m-%d %H:%M")
 
 
 
 
 
 
 
47
 
48
+ # 处理跨天情况
49
+ if business_end < business_start:
50
+ business_end += timedelta(days=1)
51
+
52
+ # 标准化所有时间到同一天
53
+ for idx, row in df.iterrows():
54
+ end_time = row['EndTime']
55
+ # 如果结束时间在凌晨0点到9点之间,则认为是第二天的场次
56
+ if end_time.hour < 9:
57
+ df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
58
+ # 如果开始时间在21点后,结束时间在9点前,也认为是跨天场次
59
+ elif row['StartTime'].hour >= 21 and end_time.hour < 9:
60
+ df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
61
+
62
+ # 为了正确筛选和排序,创建一个统一比较的时间列
63
+ # 该列将所有时间都映射到正确的日期上
64
+ df['time_for_comparison'] = df['EndTime'].apply(
65
+ lambda x: x if x.hour >= 9 else x + timedelta(days=1) if x.date() == base_date else x
66
+ )
67
 
68
+ # 筛选营业时间内的场次
69
+ valid_times = (df['time_for_comparison'] >= business_start) & (df['time_for_comparison'] <= business_end)
70
+ df = df[valid_times]
71
+
72
+ # 按散场时间排序
73
+ df = df.sort_values('time_for_comparison')
74
+
75
  # 分割数据
76
+ split_time = datetime.strptime(f"{base_date} {SPLIT_TIME}", "%Y-%m-%d %H:%M")
77
 
78
+ part1 = df[df['time_for_comparison'] <= split_time].copy()
79
+ part2 = df[df['time_for_comparison'] > split_time].copy()
80
+
81
+ # 格式化时间显示
82
  for part in [part1, part2]:
83
+ part['EndTime'] = part['EndTime'].dt.strftime('%-H:%M')
84
+
85
+ # 精确读取C6单元格的日期
86
+ date_df = pd.read_excel(
87
+ file,
88
+ skiprows=5,
89
+ nrows=1,
90
+ usecols=[2],
91
+ header=None
92
+ )
93
  date_cell = date_df.iloc[0, 0]
94
+
95
  try:
96
  if isinstance(date_cell, str):
 
97
  date_str = datetime.strptime(date_cell, '%Y-%m-%d').strftime('%Y-%m-%d')
98
  else:
 
99
  date_str = pd.to_datetime(date_cell).strftime('%Y-%m-%d')
100
  except:
101
  date_str = datetime.today().strftime('%Y-%m-%d')
102
+
103
+ return part1[['Hall', 'EndTime']], part2[['Hall', 'EndTime']], date_str
104
+
105
  except Exception as e:
106
  st.error(f"处理文件时出错: {str(e)}")
107
  return None, None, None
108
 
 
109
  def create_print_layout(data, title, date_str):
110
+ """创建符合新布局要求的打印视图 (PNG 和 PDF)"""
 
 
 
 
 
 
111
  if data.empty:
112
  return None
113
 
114
+ # --- 布局计算 (要求1) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  total_items = len(data)
116
+ num_cols = 3
117
+ # 计算行数,小数位进一
118
+ num_rows = math.ceil(total_items / num_cols)
119
+ if num_rows == 0: # 避免除以零
120
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ # 计算留空距离
123
+ # A5高度除以行数,再除以4,得到上下左右留空
124
+ margin_y = (A5_HEIGHT_IN / num_rows) / 4
125
+ margin_x = margin_y # 保持长宽比
126
+
127
+ # 计算绘图区域在figure中的相对位置
128
+ left_margin = margin_x / A5_WIDTH_IN
129
+ right_margin = 1 - (margin_x / A5_WIDTH_IN)
130
+ bottom_margin = margin_y / A5_HEIGHT_IN
131
+ top_margin = 1 - (margin_y / A5_HEIGHT_IN)
132
+
133
+ # --- 创建图形 ---
134
+ # 使用相同的 figure 和函数来确保 PNG 和 PDF 的一致性
135
+ fig = plt.figure(figsize=(A5_WIDTH_IN, A5_HEIGHT_IN), dpi=300)
136
+
137
+ # --- 内部绘图函数 ---
138
+ def process_figure(figure):
139
+ plt.rcParams['font.family'] = 'sans-serif'
140
+ plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'Heiti TC', 'SimHei'] # 添加备用中文字体
141
+
142
+ # 创建网格布局
143
+ gs = gridspec.GridSpec(
144
+ num_rows + 1, num_cols,
145
+ figure=figure,
146
+ left=left_margin, right=right_margin,
147
+ bottom=bottom_margin, top=top_margin,
148
+ hspace=0, wspace=0, # 单元格之间无间距,边框会自己形成网格
149
+ height_ratios=[0.2] + [1] * num_rows # 日期行高度较小
150
+ )
151
 
152
+ # 动态计算字体大小
153
+ cell_height_in = (A5_HEIGHT_IN - 2 * margin_y) / num_rows
154
+ # 字体大小与单元格高度关联,0.35为经验系数,可以微调
155
+ base_fontsize = cell_height_in * 72 * 0.35
 
 
 
 
 
 
 
156
 
157
+ data_values = data.values.tolist()
 
 
158
 
159
+ # 补全空位,以对齐网格
160
+ while len(data_values) % num_cols != 0:
161
+ data_values.append(['', ''])
162
+
163
+ rows_per_col_layout = math.ceil(len(data_values) / num_cols)
164
+
165
+ # 按列优先(Z字形)排序数据
166
+ sorted_data = [['', '']] * len(data_values)
167
+ item_count = 0
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
+ item_count += 1
176
+
177
+ # 重新整理数据,移除空位,方便添加序号
178
+ final_data = [item for item in sorted_data if item[0] and item[1]]
179
 
180
+ # 绘制数据单元格
181
+ for idx, (hall, end_time) in enumerate(final_data):
182
+ row_grid = idx // num_cols + 1
183
+ col_grid = idx % num_cols
184
+
185
+ ax = figure.add_subplot(gs[row_grid, col_grid])
186
+
187
+ # --- 修改开始:绘制单元格 (要求2) ---
188
+ # 设置所有边框为灰色点状虚线
189
+ for spine in ax.spines.values():
190
+ spine.set_visible(True)
191
+ spine.set_linestyle((0, (1, 2))) # 点状线样式
192
+ spine.set_edgecolor(BORDER_COLOR)
193
+ spine.set_linewidth(1)
194
+
195
+ # --- 内容与序号 (要求3 & 4) ---
196
+ # 主内容
197
  display_text = f"{hall}{end_time}"
198
+ # 使用 text 函数并设置 bbox 来控制内容宽度为90%
199
  ax.text(0.5, 0.5, display_text,
200
  fontsize=base_fontsize,
201
  fontweight='bold',
202
+ ha='center',
203
+ va='center',
204
+ transform=ax.transAxes,
205
+ bbox=dict(boxstyle='square,pad=0', fc='none', ec='none',
206
+ width=0.9, height=0.9) # 隐形 bbox 控制宽度
207
+ )
208
+
209
+ # 左上角序号
210
+ ax.text(0.08, 0.92, str(idx + 1),
211
+ fontsize=base_fontsize * 0.4, # 序号字体较小
212
+ color=DATE_COLOR,
213
  fontweight='normal',
214
+ ha='left',
215
+ va='top',
216
  transform=ax.transAxes)
217
+
218
+ ax.set_xticks([])
219
+ ax.set_yticks([])
220
+
221
+ # 添加日期和标��
222
+ ax_date = figure.add_subplot(gs[0, :])
223
+ ax_date.text(0.01, 0.5, f"{date_str} {title}",
224
+ fontsize=base_fontsize * 0.6,
225
+ color=DATE_COLOR,
226
+ fontweight='bold',
227
+ ha='left',
228
+ va='center',
229
+ transform=ax_date.transAxes)
230
+ ax_date.set_axis_off()
231
+
232
+ # --- 处理和保存图形 ---
233
+ process_figure(fig)
234
+
235
+ # 保存为 PNG
236
  png_buffer = io.BytesIO()
237
+ fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0)
238
  png_buffer.seek(0)
239
  png_base64 = base64.b64encode(png_buffer.getvalue()).decode()
240
 
241
+ # 保存为 PDF
242
  pdf_buffer = io.BytesIO()
243
+ fig.savefig(pdf_buffer, format='pdf', bbox_inches='tight', pad_inches=0)
244
  pdf_buffer.seek(0)
245
  pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
246
 
 
253
 
254
  def display_pdf(base64_pdf):
255
  """在Streamlit中嵌入显示PDF"""
256
+ pdf_display = f'<iframe src="data:application/pdf;base64,{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
257
  return pdf_display
258
 
259
+ # --- Streamlit 界面 ---
260
  st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
261
  st.title("散厅时间快捷打印")
262
 
263
  uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls", "xlsx"])
264
 
265
  if uploaded_file:
 
266
  part1, part2, date_str = process_schedule(uploaded_file)
267
+
268
  if part1 is not None and part2 is not None:
269
+ with st.spinner('正在生成预览图...'):
270
+ part1_output = create_print_layout(part1, "白班", date_str)
271
+ part2_output = create_print_layout(part2, "晚班", date_str)
 
 
272
 
273
  col1, col2 = st.columns(2)
274
 
275
  with col1:
276
+ st.subheader(f"白班散场预览 ({SPLIT_TIME})")
277
  if part1_output:
278
  tab1_1, tab1_2 = st.tabs(["PDF 预览", "PNG 预览"])
279
  with tab1_1:
280
  st.markdown(display_pdf(part1_output['pdf']), unsafe_allow_html=True)
281
  with tab1_2:
282
+ st.image(f"data:image/png;base64,{part1_output['png']}")
283
  else:
284
  st.info("白班部分没有数据")
285
 
286
  with col2:
287
+ st.subheader(f"晚班散场预览 (>{SPLIT_TIME})")
288
  if part2_output:
289
  tab2_1, tab2_2 = st.tabs(["PDF 预览", "PNG 预览"])
290
  with tab2_1:
291
  st.markdown(display_pdf(part2_output['pdf']), unsafe_allow_html=True)
292
  with tab2_2:
293
+ st.image(f"data:image/png;base64,{part2_output['png']}")
294
  else:
295
+ st.info("晚班部分没有数据")