Ethscriptions commited on
Commit
c924006
·
verified ·
1 Parent(s): f4e5f5e

Update app3.py

Browse files
Files changed (1) hide show
  1. app3.py +181 -204
app3.py CHANGED
@@ -7,280 +7,259 @@ import base64
7
  import matplotlib.gridspec as gridspec
8
  import math
9
  from matplotlib.backends.backend_pdf import PdfPages
10
- from matplotlib.patches import FancyBboxPatch # 新增导入
11
 
 
12
  SPLIT_TIME = "17:30"
13
  BUSINESS_START = "09:30"
14
  BUSINESS_END = "01:30"
15
  BORDER_COLOR = '#A9A9A9'
16
  DATE_COLOR = '#A9A9A9'
 
17
 
18
  def process_schedule(file):
19
  """处理上传的 Excel 文件,生成排序和分组后的打印内容"""
20
  try:
21
  # 读取 Excel,跳过前 8 行
22
  df = pd.read_excel(file, skiprows=8)
23
-
24
  # 提取所需列 (G9, H9, J9)
25
  df = df.iloc[:, [6, 7, 9]] # G, H, J 列
26
  df.columns = ['Hall', 'StartTime', 'EndTime']
27
-
28
  # 清理数据
29
  df = df.dropna(subset=['Hall', 'StartTime', 'EndTime'])
30
-
31
  # 转换影厅格式为 "#号" 格式
32
  df['Hall'] = df['Hall'].str.extract(r'(\d+)号').astype(str) + ' '
33
-
34
  # 保存原始时间字符串用于诊断
35
  df['original_end'] = df['EndTime']
36
-
37
  # 转换时间为 datetime 对象
38
  base_date = datetime.today().date()
39
- df['StartTime'] = pd.to_datetime(df['StartTime'])
40
- df['EndTime'] = pd.to_datetime(df['EndTime'])
41
-
 
 
42
  # 设置基准时间
43
- business_start = datetime.strptime(f"{base_date} {BUSINESS_START}", "%Y-%m-%d %H:%M")
44
- business_end = datetime.strptime(f"{base_date} {BUSINESS_END}", "%Y-%m-%d %H:%M")
45
-
46
- # 处理跨天情况
47
- if business_end < business_start:
48
- business_end += timedelta(days=1)
49
-
50
- # 标准化所有时间到同一天
51
- for idx, row in df.iterrows():
52
- end_time = row['EndTime']
53
- if end_time.hour < 9:
54
- df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
55
-
56
- if row['StartTime'].hour >= 21 and end_time.hour < 9:
57
- df.at[idx, 'EndTime'] = end_time + timedelta(days=1)
58
-
59
- # 筛选营业时间内的场次
60
- df['time_for_comparison'] = df['EndTime'].apply(
61
- lambda x: datetime.combine(base_date, x.time())
62
- )
63
-
64
- df.loc[df['time_for_comparison'].dt.hour < 9, 'time_for_comparison'] += timedelta(days=1)
65
-
66
- valid_times = (
67
- ((df['time_for_comparison'] >= datetime.combine(base_date, business_start.time())) &
68
- (df['time_for_comparison'] <= datetime.combine(base_date + timedelta(days=1), business_end.time())))
69
  )
70
 
71
- df = df[valid_times]
72
-
73
- # 按散场时间排序
74
- df = df.sort_values('EndTime')
75
-
76
  # 分割数据
77
- split_time = datetime.strptime(f"{base_date} {SPLIT_TIME}", "%Y-%m-%d %H:%M")
78
- split_time_for_comparison = df['time_for_comparison'].apply(
79
- lambda x: datetime.combine(base_date, split_time.time())
80
- )
81
-
82
- part1 = df[df['time_for_comparison'] <= split_time_for_comparison].copy()
83
- part2 = df[df['time_for_comparison'] > split_time_for_comparison].copy()
84
 
85
- # 格式化时间显示
 
 
 
86
  for part in [part1, part2]:
87
- part['EndTime'] = part['EndTime'].dt.strftime('%-I:%M')
88
-
89
- # 关键修改:精确读取C6单元格
90
- date_df = pd.read_excel(
91
- file,
92
- skiprows=5, # 跳过前5行(0-4)
93
- nrows=1, # 只读1行
94
- usecols=[2], # 第三列(C列)
95
- header=None # 无表头
96
- )
97
  date_cell = date_df.iloc[0, 0]
98
-
99
  try:
100
- # 处理不同日期格式
101
  if isinstance(date_cell, str):
 
102
  date_str = datetime.strptime(date_cell, '%Y-%m-%d').strftime('%Y-%m-%d')
103
  else:
 
104
  date_str = pd.to_datetime(date_cell).strftime('%Y-%m-%d')
105
  except:
106
  date_str = datetime.today().strftime('%Y-%m-%d')
107
-
108
- return part1[['Hall', 'EndTime']], part2[['Hall', 'EndTime']], date_str
109
-
110
  except Exception as e:
111
  st.error(f"处理文件时出错: {str(e)}")
112
  return None, None, None
113
 
 
114
  def create_print_layout(data, title, date_str):
115
- """创建打印布局 (PNG 和 PDF)"""
 
 
 
 
 
 
116
  if data.empty:
117
  return None
118
 
119
- # --- 创建 PNG 图形 ---
120
- png_fig = plt.figure(figsize=(5.83, 8.27), dpi=300) # A5 竖向
121
- png_ax_container = png_fig.add_subplot(111) # 创建一个容器轴,用于隐藏外部边框
122
- png_ax_container.set_axis_off()
123
- # 减小边距,例如从 0.05 减小到 0.02
124
- png_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
125
-
126
- # --- 创建 PDF 图形 ---
127
- pdf_fig = plt.figure(figsize=(5.83, 8.27), dpi=300) # A5 竖向
128
- pdf_ax_container = pdf_fig.add_subplot(111)
129
- pdf_ax_container.set_axis_off()
130
- # 减小边距,例如从 0.05 减小到 0.02
131
- pdf_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
132
-
133
- # --- 内部绘图函数 ---
134
- def process_figure(fig, is_pdf=False):
135
- # 设置字体
136
- plt.rcParams['font.family'] = 'sans-serif'
137
- plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 确保字体可用
138
-
139
- # 计算行数和总数
140
- total_items = len(data)
141
- num_cols = 3
142
- num_rows = math.ceil(total_items / num_cols)
143
-
144
- # 创建网格 (在 figure 内部创建)
145
- # 减小子图间距 hspace/wspace,减小日期行高度比例 height_ratios
146
- gs = gridspec.GridSpec(num_rows + 1, num_cols, hspace=0.05, wspace=0.05, height_ratios=[0.1] + [1] * num_rows, figure=fig) # 将日期行放在顶部
147
-
148
- # 调整基础字体大小,避免过大或过小
149
- # A5 宽度大约 1749 像素 @ 300dpi, 高度 2481
150
- # 每列宽度约 1749 * 0.9 / 3 = 525 像素
151
- # 每行高度约 (2481 * 0.9 * (1 / (1.2))) / num_rows
152
- # 字体大小与单元格大小相关,这里用经验值调整
153
- available_height_per_row = (8.27 * 0.9 * (1 / 1.2)) / num_rows if num_rows > 0 else 1
154
- base_fontsize = min(40, max(10, available_height_per_row * 72 * 0.5)) # 72 points per inch, 估算系数
155
-
156
- data_values = data.values.tolist()
157
-
158
- # 补全空位,确保是3的倍数
159
- while len(data_values) % num_cols != 0:
160
- data_values.append(['', ''])
161
-
162
- rows_per_col_layout = math.ceil(len(data_values) / num_cols) # 按列优先排列的行数
163
-
164
- # 按列优先排序数据 (Z字形)
165
- sorted_data = [['', '']] * len(data_values)
166
- for i, item in enumerate(data_values):
167
- if item[0] and item[1]:
168
- row_in_col = i % rows_per_col_layout
169
- col_idx = i // rows_per_col_layout
170
- new_index = row_in_col * num_cols + col_idx
171
- if new_index < len(sorted_data):
172
- sorted_data[new_index] = item
173
-
174
- # 绘制数据单元格
175
- for idx, (hall, end_time) in enumerate(sorted_data):
176
- if hall and end_time:
177
- row_grid = idx // num_cols + 1 # +1 因为日期占了第0行
178
- col_grid = idx % num_cols
179
-
180
- if row_grid < num_rows + 1: # 确保索引在网格内
181
- ax = fig.add_subplot(gs[row_grid, col_grid]) # 使用 fig.add_subplot
182
-
183
- # --- 修改开始:绘制圆角矩形 ---
184
- # 隐藏原始边框
185
- for spine in ax.spines.values():
186
- spine.set_visible(False)
187
-
188
- # 创建圆角矩形 Patch
189
- bbox = FancyBboxPatch(
190
- (0.01, 0.01), # 左下角坐标 (稍微内缩一点避免接触边缘)
191
- 0.98, 0.98, # 宽度和高度 (占满大部分区域)
192
- boxstyle="round,pad=0,rounding_size=0.02", # 圆角样式,rounding_size 控制圆角程度
193
- edgecolor=BORDER_COLOR,
194
- facecolor='none', # 无填充色
195
- linewidth=0.5,
196
- transform=ax.transAxes, # 使用相对坐标
197
- clip_on=False # 避免被裁剪
198
- )
199
- # 添加 Patch Axes
200
- ax.add_patch(bbox)
201
- # --- 修改结束 ---
202
-
203
- display_text = f"{hall}{end_time}"
204
- ax.text(0.5, 0.5, display_text,
205
- fontsize=base_fontsize,
206
- fontweight='bold',
207
- ha='center',
208
- va='center',
209
- transform=ax.transAxes) # 使用相对坐标
210
-
211
- ax.set_xticks([])
212
- ax.set_yticks([])
213
- else:
214
- print(f"Warning: Index out of bounds - idx={idx}, row_grid={row_grid}, col_grid={col_grid}")
215
-
216
-
217
- # 添加日期信息到第一个子图的顶部
218
- ax_date = fig.add_subplot(gs[0, :]) # 跨越第一行的所有列
219
- ax_date.text(0.01, 0.5, f"{date_str} {title}", # 调整位置和对齐
220
- fontsize=base_fontsize * 0.5, # 调整日期字体大小
221
- color=DATE_COLOR,
222
- fontweight='bold',
223
- ha='left',
224
- va='center',
225
- transform=ax_date.transAxes)
226
-
227
- for spine in ax_date.spines.values():
228
- spine.set_visible(False)
229
- ax_date.set_xticks([])
230
- ax_date.set_yticks([])
231
- ax_date.set_facecolor('none') # 使背景透明
232
-
233
-
234
- # --- 处理图形 ---
235
- process_figure(png_fig)
236
- process_figure(pdf_fig, is_pdf=True)
237
-
238
- # --- 保存 PNG ---
239
  png_buffer = io.BytesIO()
240
- # 可以尝试减小 pad_inches, even set to 0
241
- png_fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0.02)
242
  png_buffer.seek(0)
243
  png_base64 = base64.b64encode(png_buffer.getvalue()).decode()
244
- plt.close(png_fig)
245
 
246
- # --- 保存 PDF ---
247
  pdf_buffer = io.BytesIO()
248
- with PdfPages(pdf_buffer) as pdf:
249
- # 可以尝试减小 pad_inches, even set to 0
250
- pdf.savefig(pdf_fig, bbox_inches='tight', pad_inches=0.02)
251
  pdf_buffer.seek(0)
252
  pdf_base64 = base64.b64encode(pdf_buffer.getvalue()).decode()
253
- plt.close(pdf_fig)
 
254
 
255
  return {
256
  'png': f'data:image/png;base64,{png_base64}',
257
  'pdf': f'data:application/pdf;base64,{pdf_base64}'
258
  }
259
 
260
- # --- 新增 PDF 显示函数 ---
261
  def display_pdf(base64_pdf):
262
  """在Streamlit中嵌入显示PDF"""
263
  pdf_display = f'<iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
264
  return pdf_display
265
 
266
- # Streamlit 界面
267
  st.set_page_config(page_title="散厅时间快捷打印", layout="wide")
268
  st.title("散厅时间快捷打印")
269
 
270
- uploaded_file = st.file_uploader("上传【放映场次核对表.xls】文件", type=["xls"])
271
 
272
  if uploaded_file:
 
273
  part1, part2, date_str = process_schedule(uploaded_file)
274
-
275
  if part1 is not None and part2 is not None:
276
- # 生成包含 PNG 和 PDF 的字典
277
- part1_output = create_print_layout(part1, "A", date_str)
278
- part2_output = create_print_layout(part2, "C", date_str)
 
 
279
 
280
  col1, col2 = st.columns(2)
281
 
282
  with col1:
283
- st.subheader("白班散场预览(时间 ≤ 17:30)")
284
  if part1_output:
285
  tab1_1, tab1_2 = st.tabs(["PDF 预览", "PNG 预览"])
286
  with tab1_1:
@@ -291,7 +270,7 @@ if uploaded_file:
291
  st.info("白班部分没有数据")
292
 
293
  with col2:
294
- st.subheader("夜班散场预览(时间 > 17:30)")
295
  if part2_output:
296
  tab2_1, tab2_2 = st.tabs(["PDF 预览", "PNG 预览"])
297
  with tab2_1:
@@ -299,6 +278,4 @@ if uploaded_file:
299
  with tab2_2:
300
  st.image(part2_output['png'])
301
  else:
302
- st.info("夜班部分没有数据")
303
-
304
-
 
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
+
231
+ plt.close(fig)
232
 
233
  return {
234
  'png': f'data:image/png;base64,{png_base64}',
235
  'pdf': f'data:application/pdf;base64,{pdf_base64}'
236
  }
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:
 
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:
 
278
  with tab2_2:
279
  st.image(part2_output['png'])
280
  else:
281
+ st.info("夜班部分没有数据")