45D--Event Dates

题目

On a history lesson the teacher asked Vasya to name the dates when n famous events took place. He doesn’t remembers the exact dates but he remembers a segment of days [li, ri] (inclusive) on which the event could have taken place. However Vasya also remembers that there was at most one event in one day. Help him choose such n dates of famous events that will fulfill both conditions. It is guaranteed that it is possible.

Input

The first line contains one integer n (1 ≤ n ≤ 100) — the number of known events. Then follow n lines containing two integers li and ri each (1 ≤ li ≤ ri ≤ 107) — the earliest acceptable date and the latest acceptable date of the i-th event.

Output

Print n numbers — the dates on which the events took place. If there are several solutions, print any of them. It is guaranteed that a solution exists.

Examples
input
3
1 2
2 3
3 4
output
1 2 3
input
2
1 3
1 3
output
1 2
题意:老师询问 Vasya历史事件发生的日期,但是 Vasya不记得事件发生的具体事件,但是记得该事件发生的某个事件段,但是每天只能发生一件事件,请你帮组 Vasya算n个事件有可能发生的事件。
解题思路:现将每个事件的发生事件段记录在list集合中,然后按照最迟事件对其进行排序 ,建立一个hashSet集合,再遍历每个事件是时间段,将不存在HashSet的某个时间存入该Set集合中(仅保存其中一个)。并将该时间存入list集合中,然后在将其按照事件序号排序,最后遍历list集合输出每个事件可能发生的时间。
AC–Code
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Scanner;
public class CF45D {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		ArrayList<Date> list = new ArrayList<Date>();
		HashSet<Integer> set = new HashSet<Integer>();
		for(int i =0;i<n;i++)
		{
			Date obj = new Date();
			obj.setL(sc.nextInt());
			obj.setR(sc.nextInt());
			obj.setI(i);
			obj.setAns(0);
			list.add(obj);
		}
		Collections.sort(list);
		for (Date date : list) {
			for(int i=date.getL();i<=date.getR();i++) {
				if(!set.contains(i))
				{
					date.setAns(i);
					set.add(i);
					break;
				}
			}
		}
		Collections.sort(list);
		for (Date date : list) {
			System.out.print(date.getAns()+" ");
		}
		sc.close();
	}

}
class Date implements Comparable<Date>{
	private int l,r,i,ans;

	public int getL() {
		return l;
	}

	public void setL(int l) {
		this.l = l;
	}

	public int getR() {
		return r;
	}

	public void setR(int r) {
		this.r = r;
	}

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}

	public int getAns() {
		return ans;
	}

	public void setAns(int ans) {
		this.ans = ans;
	}
	public int compareTo(Date o) {
		if(o.ans==0)
		{
			if (this.r < o.r) {
	            return -1;
	        } else if (this.r > o.r) {
	            return 1;
	        } else {
	            return 0;
	        }
		}
		else {
			if (this.i < o.i) {
	            return -1;
	        } else if (this.i > o.i) {
	            return 1;
	        } else {
	            return 0;
	        }
		}
	}
}
学到的知识:HashSet中保存的数据是唯一不重复的数据,在构建list集合时,先对其类中的ans赋值为0,方便后续进行不同方法的排序。在第一次排序中,所有事件的发生的时间设置为0,则按照最迟事件排序,第二次排序,已经知道了每个事件的发生时间(ans!=0),然后对其按照事件序号排序。
# --------------------------------------------------- 画图 ------------------------------------------------------- # # from matplotlib.dates import DateFormatter def handle_close(event): # 当窗口关闭时,我们不需要额外操作,因为plt.close()会清理资源 # 但是,如果我们在这里什么都不做,系统也会自动清理,但显式调用plt.close()可以确保一致 # 注意:这个事件处理函数是在窗口关闭时自动调用的,我们不需要在函数中再次关闭图形 print(f'Closing figure: {event.canvas.figure}') plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体,Windows系统通常有 plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 plt.ion() fig, ax = plt.subplots(figsize=(14, 6), # inches dpi=120, # dot-per-inch # face color='#BBBBBB', frameon=True, # 画布边框 ) plt.rcParams['font.sans-serif'] = ['KaiTi'] fig.canvas.manager.set_window_title(machine3 + '温度随时间变化图') fig.canvas.mpl_connect('close_event', handle_close) # 绑定关闭事件 ax.set_title(machine3 + '温度随时间变化图', fontsize=16, pad=24) ax.set_xlabel('时间') ax.set_ylabel('温度') ax = plt.gca() ax.xaxis.set_major_formatter(DateFormatter('%Y %m %d %H:%M:%S')) # 设置时间显示格式 # ax.axis.set_major_locator(AutoDateLocator(max-ticks=24)) #设置时间间隔 plt.xticks(rotation=30, ha='center') plt.ioff() # ------------------------------ 绘制灰色保温温度区间以白色标准温度线 ------------------------------------------------- # plt.plot(createTime, [temp1_S] * (len(createTime)), color='white', linewidth=0.8, label='标准温度' + str(temp1_S)) plt.fill_between(createTime, temp1_L, temp1_H, facecolor='lightgrey') # ----------------------------- 绘制各通道温度区间以及保温区间红色划分线 --------------------------------------------- # main_string = list(range(950)) if machine == 'T1': plt.plot(createTime, T_1, label='CT01') plt.plot(createTime, T_2, label='CT02') plt.plot(createTime, T_3, label='CT03') plt.plot(createTime, T_4, label='CT04') plt.plot(createTime, T_5, label='CT05') plt.plot(createTime, T_6, label='CT06') plt.legend() for i in range(0, len(S_main_string)): # 开始划分线 plt.plot([S_main_string[i]] * 950, main_string, color='red', linestyle='--', linewidth=0.8) # 开始时间标 plt.text(S_main_string[i], 950, S_main_string[i], ha='left', va='bottom', fontsize='8') # 开始温度标 plt.text(S_main_string[i], 900, ' CT01:' + str(S_main_string_T_1[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 880, ' CT02:' + str(S_main_string_T_2[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 860, ' CT03:' + str(S_main_string_T_3[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 840, ' CT04:' + str(S_main_string_T_4[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 820, ' CT05:' + str(S_main_string_T_5[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 800, ' CT06:' + str(S_main_string_T_6[i]) + '℃', ha='right', va='bottom', fontsize='8') # 结束划分线 plt.plot([E_main_string[i]] * 950, main_string, color='red', linestyle='--', linewidth=0.8) # 结束时间标 plt.text(E_main_string[i], 928, E_main_string[i], ha='left', va='bottom', fontsize='8') # 结束温度标 plt.text(E_main_string[i], 900, ' CT01:' + str(E_main_string_T_1[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 880, ' CT02:' + str(E_main_string_T_2[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 860, ' CT03:' + str(E_main_string_T_3[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 840, ' CT04:' + str(E_main_string_T_4[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 820, ' CT05:' + str(E_main_string_T_5[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 800, ' CT06:' + str(E_main_string_T_6[i]) + '℃', ha='left', va='bottom', fontsize='8') if machine == 'T2' or machine == 'T3': plt.plot(createTime, T_1, label='CT01') plt.plot(createTime, T_2, label='CT02') plt.plot(createTime, T_3, label='CT03') plt.plot(createTime, T_4, label='CT04') plt.plot(createTime, T_5, label='CT05') plt.legend() for i in range(0, len(S_main_string)): # 开始划分线 plt.plot([S_main_string[i]] * 950, main_string, color='red', linestyle='--', linewidth=0.8) # 开始时间标 plt.text(S_main_string[i], 950, S_main_string[i], ha='left', va='bottom', fontsize='8') # 开始温度标 plt.text(S_main_string[i], 900, ' CT01:' + str(S_main_string_T_1[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 880, ' CT02:' + str(S_main_string_T_2[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 860, ' CT03:' + str(S_main_string_T_3[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 840, ' CT04:' + str(S_main_string_T_4[i]) + '℃', ha='right', va='bottom', fontsize='8') plt.text(S_main_string[i], 820, ' CT05:' + str(S_main_string_T_5[i]) + '℃', ha='right', va='bottom', fontsize='8') # 结束划分线 plt.plot([E_main_string[i]] * 950, main_string, color='red', linestyle='--', linewidth=0.8) # 结束时间标 plt.text(E_main_string[i], 928, E_main_string[i], ha='left', va='bottom', fontsize='8') # 结束温度标 plt.text(E_main_string[i], 900, ' CT01:' + str(E_main_string_T_1[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 880, ' CT02:' + str(E_main_string_T_2[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 860, ' CT03:' + str(E_main_string_T_3[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 840, ' CT04:' + str(E_main_string_T_4[i]) + '℃', ha='left', va='bottom', fontsize='8') plt.text(E_main_string[i], 820, ' CT05:' + str(E_main_string_T_5[i]) + '℃', ha='left', va='bottom', fontsize='8') # --------------------------------------------- 初始化选中的两个点 ----------------------------------------------- # selected_points = [None, None] # 存储两个选中的点 point_markers = [ax.plot([], [], 'go', markersize=5, label='起点')[0], ax.plot([], [], 'ro', markersize=5, label='终点')[0]] connection_line = ax.plot([], [], 'k--', alpha=0.7)[0] # time_diff_text = ax.text(0.6, 0.5, '请选择两个点计算时间差', # transform=ax.transAxes, fontsize=10, # bbox=dict(facecolor='white', alpha=0.5)) time_diff_text = fig.text(0.70, 0.91, '请选择两个点计算时间差', fontsize=9, bbox=dict(facecolor='white', alpha=0.2)) # ------------------------------------------------ 添加重置按钮 -------------------------------------------------- # reset_ax = plt.axes([0.85, 0.90, 0.04, 0.035]) fig.reset_button = plt.Button(reset_ax, '重置选择', color='#FF9800', hovercolor='#e68a00') fig.reset_button.label.set_fontsize(9) # ----------------------------------------------- 添加保存按钮 --------------------------------------------------- # reset_ax2 = plt.axes([0.85, 0.95, 0.04, 0.035]) fig.reset_button2 = plt.Button(reset_ax2, '保存结果', color='#FF9800', hovercolor='#e68a00') fig.reset_button2.label.set_fontsize(9) # ------------------------------------- 添加通道选择按钮,绘制散点图供给手动选择 -------------------------------------- # # scatter = ax.scatter(createTime, H_1, s=30, c='lightcyan', alpha=0.3, picker=True) slider_ax = plt.axes([0.62, 0.89, 0.06, 0.05]) slider_ax.grid(True, alpha=0.8) if machine == "T1": fig.slider = plt.Slider(slider_ax, 'CH0', 0, 6, valstep=1, facecolor='darkorange', # 滑块按钮颜色 edgecolor='darkorange', # 滑块按钮边框颜色 track_color='mistyrose', ) if machine == "T2" or machine == "T3": fig.slider = plt.Slider(slider_ax, 'CH0', 0, 5, valstep=1, facecolor='darkorange', # 滑块按钮颜色 edgecolor='darkorange', # 滑块按钮边框颜色 track_color='mistyrose', ) # -------------------------------------------- 通道选择按钮回调函数 ------------------------------------------------- # def update(val): # from matplotlib.collections import PathCollection global scatter global freq freq = fig.slider.val if freq == 1: scatter = ax.scatter(createTime, T_1, s=30, c='lightcyan', alpha=0.3, picker=True) if freq == 2: scatter = ax.scatter(createTime, T_2, s=30, c='moccasin', alpha=0.3, picker=True) if freq == 3: scatter = ax.scatter(createTime, T_3, s=30, c='honeydew', alpha=0.3, picker=True) if freq == 4: scatter = ax.scatter(createTime, T_4, s=30, c='mistyrose', alpha=0.3, picker=True) if freq == 5: scatter = ax.scatter(createTime, T_5, s=30, c='lavenderblush', alpha=0.3, picker=True) if machine == "T1": if freq == 6: scatter = ax.scatter(createTime, T_6, s=30, c='linen', alpha=0.3, picker=True) if freq == 0: for collection in ax.collections[:]: # 遍历所有图形集合 if isinstance(collection, PathCollection): # 识别散点图类型 collection.remove() # 删除散点图 # fig.canvas.draw_idle() # ----------------------------------------- 选择开始结束点回调函数-------------------------------------------------- # def on_pick(event): """处理点选事件""" global selected_points global scatter global freq global start_time global end_time if event.artist != scatter: return # 获取选中的索引 idx = event.ind[0] selected_time = createTime[idx] if freq == 1: selected_value = T_1[idx] if freq == 2: selected_value = T_2[idx] if freq == 3: selected_value = T_3[idx] if freq == 4: selected_value = T_4[idx] if freq == 5: selected_value = T_5[idx] if machine == "T1": if freq == 6: selected_value = T_6[idx] # 确定是第一个点还是第二个点 if selected_points[0] is None: # 选择第一个点(起点) selected_points[0] = (selected_time, selected_value, idx) point_markers[0].set_data([selected_time], [selected_value]) time_diff_text.set_text(f'已选择起点: {selected_time.strftime("%Y-%m-%d %H:%M:%S")}\n请选择终点') elif selected_points[1] is None: # 选择第二个点(终点) selected_points[1] = (selected_time, selected_value, idx) point_markers[1].set_data([selected_time], [selected_value]) # 绘制连接线 start_time, start_value, _ = selected_points[0] end_time, end_value, _ = selected_points[1] connection_line.set_data([start_time, end_time], [start_value, end_value]) # 计算时间差 time_diff = end_time - start_time total_seconds = time_diff.total_seconds() total_minutes = total_seconds / 60 # 格式化时间差 hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) # 显示结果 time_diff_text.set_text( f'起点: {start_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-4]}\n' f'终点: {end_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-4]}\n' f'时间差: {hours:.0f}小时 {minutes:.0f}分钟 {seconds:.2f}秒\n' f'总分钟: {total_minutes:.2f}分' # f'总秒数: {total_seconds:.3f}秒' ) # 更新图例 handles, labels = ax.get_legend_handles_labels() if '起点' not in labels: ax.legend(loc='upper right') fig.canvas.draw() # -------------------------------------------------- 重置按钮回调函数 --------------------------------------------- # def reset_selection(event): """重置所有选择""" global selected_points selected_points = [None, None] # 清除标记和连接线 for marker in point_markers: marker.set_data([], []) connection_line.set_data([], []) # 重置文本 time_diff_text.set_text('请选择两个点计算时间差') fig.canvas.draw() # -------------------------------------------------- 保存按钮回调函数 --------------------------------------------- # data_container = [] def save_result(event): """ 保存选点后的时间计算结果""" global start_time global end_time global freq if os.path.exists(machine + ' - data.xlsx'): os.remove(machine + ' - data.xlsx') print(start_time) print(end_time) # # 计算时间差 time_diff = end_time - start_time # print(time_diff) total_seconds = time_diff.total_seconds() total_minutes = total_seconds / 60 # # 格式化时间差 hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) time_tiff_change = str(hours) + '小时' + str(minutes) + '分钟' + str(seconds) + '秒' # 通道 channel = "CHO" + str(freq) data_container.append([channel, start_time, end_time, time_tiff_change, total_minutes]) # 2. 创建DataFrame df = pd.DataFrame(data_container, columns=['通道', '起点', '终点', '时间差', '总分钟']) # 3. 追加到Excel文件 try: # 尝试读取现有文件 existing_df = pd.read_excel(machine + ' - data.xlsx') combined_df = pd.concat([existing_df, df], ignore_index=True) except FileNotFoundError: # 文件不存在时创建新文件 combined_df = df # 4. 保存文件 combined_df.to_excel(machine + ' - data.xlsx', index=False) print(f"已追加数据,总记录数: {len(combined_df)}") # # # 显示结果 # time_diff_text.set_text( # f'起点: {start_time.strftime("%Y %m %d %H:%M:%S.%f")[:-3]}\n' # f'终点: {end_time.strftime("%Y %m %d %H:%M:%S.%f")[:-3]}\n' # f'时间差: {hours:.0f}小时 {minutes:.0f}分钟 {seconds:.2f}秒\n' # f'总分钟: {total_minutes:.2f}分' # # f'总秒数: {total_seconds:.3f}秒' # ) # -------------------------------------------------- 绑定事件处理函数 --------------------------------------------- # fig.canvas.mpl_connect('pick_event', on_pick) fig.reset_button.on_clicked(reset_selection) fig.reset_button2.on_clicked(save_result) fig.slider.on_changed(update) # ---------------------------------------------------- 绘图展出 -------------------------------------------------- # ax.legend(loc='upper right') # ax = plt.gca() ax.xaxis.set_major_formatter(DateFormatter('%Y %m %d %H:%M:%S')) # 设置时间显示格式 plt.xticks(rotation=30, ha='center') plt.savefig('./结果/图' + machine3 + '.png') plt.show() 优化这部分代码,加快运行速度
最新发布
09-02
import re import time import json import threading from selenium import webdriver from datetime import datetime, timedelta from selenium.webdriver.edge.service import Service as EdgeService from selenium.webdriver.edge.options import Options as EdgeOptions from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from apscheduler.schedulers.background import BackgroundScheduler from pytz import timezone import FreeSimpleGUI as sg BEIJING_TZ = timezone('Asia/Shanghai') CONFIG_FILE = 'browser_config.json' ELEMENT_TYPES = { "按钮": "button", "复选框": "checkbox", "单选按钮": "radio", "下拉框": "dropdown", "标签页": "tab", "时间": "time", "文本": "text" } WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] SCHEDULER = None def load_config(): default_config = { "order": ["选项1", "选项2", "选项3", "选项4", "选项5", "选项6", "选项7"], "elements": { "target_url": "http://www.igs.gnsswhu.cn/index.php", "选项1": {"type": "标签页", "identifier": "OBS观测值"}, "选项2": {"type": "下拉框", "identifier": "IGS测站列表"}, "选项3": {"type": "复选框", "identifier": "ABMF00GLP"}, "选项4": {"type": "单选按钮", "identifier": "d文件"}, "选项5": {"type": "时间", "identifier": "开始时间"}, "选项6": {"type": "时间", "identifier": "结束时间"}, "选项7": {"type": "按钮", "identifier": "检索"}, }, "start_date": "", "end_date": "", "schedule": { "start_preset": "--", "end_preset": "--", "schedule_type": "--", "schedule_time": "00:00:00" }, "stay_duration": 60 } try: with open(CONFIG_FILE, 'r') as f: user_config = json.load(f) merged_config = default_config.copy() merged_config.update(user_config) # 用户配置覆盖默认 merged_config['order'] = user_config.get('order', default_config['order']) # 确保顺序正确 # 处理日期格式 if 'start_date' in merged_config and ' ' not in merged_config.get('start_date', ''): merged_config['start_date'] += " 00:00:00" if 'end_date' in merged_config and ' ' not in merged_config.get('end_date', ''): merged_config['end_date'] += " 00:00:00" return merged_config except: return default_config def save_config(values, current_order): try: elements = {} for label in current_order: elements[label] = { "type": values[f'-{label}_TYPE-'], "identifier": values[f'-{label}_ID-'] } if values[f'-{label}_TYPE-'] == "文本": elements[label]["text"] = values[f'-{label}_TEXT-'] def get_full_datetime(date_str, h, m, s): if not date_str: return "" # 格式化为两位数 h = f"{int(h):02d}" if h else "00" m = f"{int(m):02d}" if m else "00" s = f"{int(s):02d}" if s else "00" return f"{date_str} {h}:{m}:{s}" start_time = get_full_datetime(values['-START_DATE-'], values['-START_HOUR-'], values['-START_MINUTE-'], values['-START_SECOND-']) end_time = get_full_datetime(values['-END_DATE-'], values['-END_HOUR-'], values['-END_MINUTE-'], values['-END_SECOND-']) config = { "order": current_order, "elements": { "target_url": values['-URL-'], **elements }, "start_identifier": load_config().get('start_identifier', '开始时间'), "end_identifier": load_config().get('end_identifier', '结束时间'), "start_date": start_time, "end_date": end_time, "schedule": load_config().get('schedule', {}), "stay_duration": load_config().get('stay_duration', 60) } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) return True except Exception as e: print(f"保存配置失败: {str(e)}") return False def create_element_row(label, element_type='', identifier='', text_value=''): return [ sg.Text(label, size=(12, 1)), sg.Combo( list(ELEMENT_TYPES.keys()), default_value=element_type, key=f'-{label}_TYPE-', size=(15, 1), enable_events=True ), sg.Input( identifier, key=f'-{label}_ID-', size=(20, 1) ), sg.Input( text_value, key=f'-{label}_TEXT-', size=(20, 1), visible=(element_type == "文本") ), sg.Button('↑', key=f'-{label}_UP-'), sg.Button('↓', key=f'-{label}_DOWN-'), sg.Button('-', key=f'-{label}_DEL-') ] def create_elements_column(current_order): config = load_config() elements = [] for label in current_order: element_config = config['elements'].get(label, {}) elements.append(create_element_row( label, element_config.get('type', ''), element_config.get('identifier', ''), element_config.get('text', '') )) return sg.Column( elements, scrollable=True, vertical_scroll_only=True, size=(800, 300), key='-ELEMENTS_COL-' ) def show_settings_window(parent_window): """显示停留时间设置窗口""" config = load_config() start_id = config.get('start_identifier', '开始时间') end_id = config.get('end_identifier', '结束时间') layout = [ [sg.Text('网页停留时间(秒):'), sg.Input(config.get('stay_duration', 60), key='-STAY_DURATION-')], [sg.HorizontalSeparator()], [sg.Text('开始时间字段标识:'), sg.Input(start_id, key='-START_ID-', size=15)], [sg.Text('结束时间字段标识:'), sg.Input(end_id, key='-END_ID-', size=15)], [sg.Button('保存'), sg.Button('取消')] ] window = sg.Window('设置', layout, modal=True) while True: event, values = window.read() if event in (None, '取消'): break if event == '保存': try: stay_duration = int(values['-STAY_DURATION-']) if stay_duration <= 0: raise ValueError("停留时间必须大于0") config['stay_duration'] = stay_duration config['start_identifier'] = values['-START_ID-'].strip() config['end_identifier'] = values['-END_ID-'].strip() with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) sg.popup('设置已保存!') break except Exception as e: sg.popup_error(f'输入无效: {str(e)}') window.close() def create_gui(): config = load_config() current_order = config["order"].copy() elements_config = config["elements"] schedule_config = config.get('schedule', {}) # 时间处理函数 def split_datetime(dt_str): if not dt_str: return "", "00", "00", "00" if ' ' in dt_str: date_part, time_part = dt_str.split(' ', 1) h, m, s = time_part.split(':')[:3] else: date_part, h, m, s = dt_str, '00', '00', '00' return date_part, h, m, s # 初始化日期时间 start_date, start_h, start_m, start_s = split_datetime(config.get('start_date', '')) end_date, end_h, end_m, end_s = split_datetime(config.get('end_date', '')) # 主布局 layout = [ [sg.Text('目标网址'), sg.Input(config['elements']['target_url'], key='-URL-')], [sg.HorizontalSeparator()], [create_elements_column(current_order)], [sg.Button('+ 添加选项', key='-ADD_OPTION-')], [sg.HorizontalSeparator()], # 开始时间选择 [sg.Frame('开始时间', [ [sg.CalendarButton('选择日期', target='-START_DATE-', format='%Y-%m-%d'), sg.Input(start_date, key='-START_DATE-', size=(12, 1)), sg.Spin([f"{i:02}" for i in range(24)], start_h, key='-START_HOUR-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_m, key='-START_MINUTE-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_s, key='-START_SECOND-', size=3)] ])], # 结束时间选择 [sg.Frame('结束时间', [ [sg.CalendarButton('选择日期', target='-END_DATE-', format='%Y-%m-%d'), sg.Input(end_date, key='-END_DATE-', size=(12, 1)), sg.Spin([f"{i:02}" for i in range(24)], end_h, key='-END_HOUR-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_m, key='-END_MINUTE-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_s, key='-END_SECOND-', size=3)] ])], # 控制按钮 [sg.HorizontalSeparator()], [sg.Button('运行', size=10), sg.Button('定时', key='-SCHEDULE-', size=10), sg.Button('保存配置', size=10), sg.Button('设置', size=10), sg.Button('退出', size=10)], [sg.Output(size=(90, 10), echo_stdout_stderr=True, key='-OUTPUT-')] ] window = sg.Window('浏览器自动化工具', layout, finalize=True) setup_scheduler(window) # 动态更新元素可见性 def update_element_visibility(): for label in current_order: element_type = values.get(f'-{label}_TYPE-', '') window[f'-{label}_TEXT-'].update(visible=(element_type == "文本")) # 主事件循环 while True: event, values = window.read() if event in (None, '退出'): break # 元素类型变化事件 if '_TYPE-' in event: update_element_visibility() # 添加新选项 if event == '-ADD_OPTION-': new_label = f'选项{len(current_order) + 1}' current_order.append(new_label) window.extend_layout(window['-ELEMENTS_COL-'], [create_element_row(new_label)]) window['-ELEMENTS_COL-'].contents_changed() # 删除选项 if '_DEL-' in event: label = event.split('_')[1] if label in current_order: current_order.remove(label) # 删除对应的UI元素 for element in window['-ELEMENTS_COL-'].Widget.winfo_children(): if f'_{label}_' in str(element): element.destroy() # 重新编号剩余选项 new_order = [f"选项{i + 1}" for i in range(len(current_order))] config_changes = {} for old, new in zip(current_order, new_order): config_changes[old] = new # 更新配置和当前顺序 current_order = new_order.copy() window['-ELEMENTS_COL-'].update(visible=False) window['-ELEMENTS_COL-'].update(visible=True) # 移动选项位置 if '_UP-' in event or '_DOWN-' in event: direction = -1 if '_UP-' in event else 1 label = event.split('_')[1] index = current_order.index(label) new_index = index + direction if 0 <= new_index < len(current_order): # 交换顺序 current_order.insert(new_index, current_order.pop(index)) # 重新排列UI元素 elements = [create_element_row(lbl) for lbl in current_order] window['-ELEMENTS_COL-'].update(visible=False) window['-ELEMENTS_COL-'].update(elements) window['-ELEMENTS_COL-'].contents_changed() window['-ELEMENTS_COL-'].update(visible=True) # 保存配置 if event == '保存配置': elements = {} for label in current_order: elements[label] = { "type": values[f'-{label}_TYPE-'], "identifier": values[f'-{label}_ID-'] } if values[f'-{label}_TYPE-'] == "文本": elements[label]["text"] = values[f'-{label}_TEXT-'] # 构建时间字符串 def build_time_str(date_part, h, m, s): return f"{date_part} {h}:{m}:{s}" if date_part else "" new_config = { "order": current_order, "elements": { "target_url": values['-URL-'], ** elements }, "start_date": build_time_str( values['-START_DATE-'], values['-START_HOUR-'], values['-START_MINUTE-'], values['-START_SECOND-'] ), "end_date": build_time_str( values['-END_DATE-'], values['-END_HOUR-'], values['-END_MINUTE-'], values['-END_SECOND-'] ), "schedule": config.get('schedule', {}), "stay_duration": config.get('stay_duration', 60) } try: with open(CONFIG_FILE, 'w') as f: json.dump(new_config, f, indent=4, ensure_ascii=False) sg.popup('配置保存成功!', title='保存结果') except Exception as e: sg.popup_error(f'保存失败: {str(e)}') # 运行自动化 if event == '运行': print("启动浏览器自动化...") threading.Thread( target=browser_automation, args=(values, current_order), daemon=True ).start() # 打开设置窗口 if event == '设置': show_settings_window(window) # 定时设置 if event == '-SCHEDULE-': show_schedule_settings(window, config) # 清理资源 window.close() if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() def calculate_dynamic_date(preset): if not preset or preset == "--": return None week_type, weekday_str = preset[:1], preset[1:] week_offset = -1 if week_type == "上" else 0 try: weekday_index = WEEKDAYS.index(weekday_str) except ValueError: return None today = datetime.now(BEIJING_TZ) current_week_monday = today - timedelta(days=today.weekday()) target_week_monday = current_week_monday + timedelta(weeks=week_offset) return target_week_monday + timedelta(days=weekday_index) def show_schedule_settings(parent_window, config): schedule = config.get('schedule', {}) def split_preset_time(time_str): if time_str and re.match(r"\d{2}:\d{2}:\d{2}", time_str): return time_str.split(":") return ["00", "00", "00"] start_time_parts = split_preset_time(schedule.get('start_preset_time', "00:00:00")) end_time_parts = split_preset_time(schedule.get('end_preset_time', "00:00:00")) layout = [ [sg.Text('开始日期预设:'), sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS], default_value=schedule.get('start_preset', '--'), key='-START_PRESET-', enable_events=True, size=(12, 1)), sg.Text("时间:"), sg.Spin([f"{i:02}" for i in range(24)], start_time_parts[0], key='-START_PRESET_H-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[1], key='-START_PRESET_M-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[2], key='-START_PRESET_S-', size=3), sg.Text("实际日期:", size=(10, 1)), sg.Text("", key='-REAL_START-', size=15)], [sg.Text('结束日期预设:'), sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS], default_value=schedule.get('end_preset', '--'), key='-END_PRESET-', enable_events=True, size=(12, 1)), sg.Text("时间:"), sg.Spin([f"{i:02}" for i in range(24)], end_time_parts[0], key='-END_PRESET_H-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[1], key='-END_PRESET_M-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[2], key='-END_PRESET_S-', size=3), sg.Text("实际日期:", size=(10, 1)), sg.Text("", key='-REAL_END-', size=15)], [sg.Text('定时执行:'), sg.Combo(["--"] + WEEKDAYS, default_value=schedule.get('schedule_type', '--'), key='-SCHEDULE_TYPE-', size=(8, 1)), sg.Input(schedule.get('schedule_time', '00:00:00'), key='-SCHEDULE_TIME-', size=(8, 1), tooltip="格式: HH:MM:SS")], [sg.Button('保存'), sg.Button('清除定时'), sg.Button('取消')], ] window = sg.Window('定时设置', layout, finalize=True) def update_dates(): start_date = calculate_dynamic_date(window['-START_PRESET-'].get()) end_date = calculate_dynamic_date(window['-END_PRESET-'].get()) window['-REAL_START-'].update(start_date.strftime('%Y-%m-%d') if start_date else "") window['-REAL_END-'].update(end_date.strftime('%Y-%m-%d') if end_date else "") update_dates() while True: event, values = window.read(timeout=500) if event in (None, '取消'): break if event == '清除定时': window['-START_PRESET-'].update('--') window['-END_PRESET-'].update('--') window['-SCHEDULE_TYPE-'].update('--') window['-SCHEDULE_TIME-'].update('00:00:00') window['-REAL_START-'].update('') window['-REAL_END-'].update('') config['schedule'] = { "start_preset": "--", "end_preset": "--", "schedule_type": "--", "schedule_time": "00:00:00" } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) global SCHEDULER if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() SCHEDULER = None sg.popup("定时设置已重置") if event in ('-START_PRESET-', '-END_PRESET-', '__TIMEOUT__'): update_dates() if event == '保存': try: start_h = int(values['-START_PRESET_H-']) start_m = int(values['-START_PRESET_M-']) start_s = int(values['-START_PRESET_S-']) end_h = int(values['-END_PRESET_H-']) end_m = int(values['-END_PRESET_M-']) end_s = int(values['-END_PRESET_S-']) if not (0 <= start_h <= 23 and 0 <= end_h <= 23): raise ValueError("小时需在00-23之间") if not (0 <= start_m <= 59 and 0 <= end_m <= 59): raise ValueError("分钟需在00-59之间") if not (0 <= start_s <= 59 and 0 <= end_s <= 59): raise ValueError("秒数需在00-59之间") except ValueError as e: sg.popup_error(f"时间输入错误: {str(e)}") continue config['schedule']['start_preset_time'] = f"{values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}" config['schedule']['end_preset_time'] = f"{values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}" if values['-START_PRESET-'] != "--": parent_window['-START_HOUR-'].update(values['-START_PRESET_H-']) parent_window['-START_MINUTE-'].update(values['-START_PRESET_M-']) parent_window['-START_SECOND-'].update(values['-START_PRESET_S-']) if values['-END_PRESET-'] != "--": parent_window['-END_HOUR-'].update(values['-END_PRESET_H-']) parent_window['-END_MINUTE-'].update(values['-END_PRESET_M-']) parent_window['-END_SECOND-'].update(values['-END_PRESET_S-']) if not re.match(r'^([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$', values['-SCHEDULE_TIME-']): sg.popup_error("时间格式应为HH:MM:SS") continue start_preset_val = values['-START_PRESET-'] end_preset_val = values['-END_PRESET-'] calculated_start = calculate_dynamic_date(start_preset_val) calculated_end = calculate_dynamic_date(end_preset_val) if start_preset_val != "--" and calculated_start: date_part = calculated_start.strftime('%Y-%m-%d') # 更新主窗口的开始日期输入框 parent_window['-START_DATE-'].update(date_part) # 同时更新配置中的日期部分 config[ 'start_date'] = f"{date_part} {values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}" if end_preset_val != "--" and calculated_end: date_part = calculated_end.strftime('%Y-%m-%d') # 更新主窗口的结束日期输入框 parent_window['-END_DATE-'].update(date_part) # 同时更新配置中的日期部分 config[ 'end_date'] = f"{date_part} {values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}" config['schedule'] = { "start_preset": start_preset_val, "end_preset": end_preset_val, "schedule_type": values['-SCHEDULE_TYPE-'], "schedule_time": values['-SCHEDULE_TIME-'] } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) setup_scheduler(parent_window) sg.popup("定时设置已保存!") window.close() def setup_scheduler(parent_window): global SCHEDULER if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() config = load_config() schedule = config.get('schedule', {}) if schedule.get('schedule_type') == "--" or not schedule.get('schedule_time'): return SCHEDULER = BackgroundScheduler(timezone=BEIJING_TZ) def scheduled_task(): try: now = datetime.now(BEIJING_TZ) print(f"\n[{now.strftime('%Y-%m-%d %H:%M:%S')}] 定时任务启动") parent_window.write_event_value('执行定时任务', None) except Exception as e: print(f"定时任务异常: {str(e)}") try: h, m, s = map(int, schedule['schedule_time'].split(':')) weekday_index = WEEKDAYS.index(schedule['schedule_type']) SCHEDULER.add_job( scheduled_task, 'cron', day_of_week=weekday_index, hour=h, minute=m, second=s, misfire_grace_time=60 ) SCHEDULER.start() next_run = SCHEDULER.get_jobs()[0].next_run_time.astimezone(BEIJING_TZ) print(f"定时任务已激活,每周{WEEKDAYS[weekday_index]} {schedule['schedule_time']} 执行") print(f"下次执行时间: {next_run.strftime('%Y-%m-%d %H:%M:%S')}") except Exception as e: sg.popup_error(f"定时设置错误: {str(e)}") def browser_automation(values, current_order): try: if current_order is None: config = load_config() current_order = config["order"] config = load_config() STAY_DURATION = config.get('stay_duration', 60) schedule = config.get('schedule', {}) start_date = values['-START_DATE-'] start_time = f"{values['-START_HOUR-']}:{values['-START_MINUTE-']}:{values['-START_SECOND-']}" full_start = f"{start_date} {start_time}" if start_date else "" end_date = values['-END_DATE-'] end_time = f"{values['-END_HOUR-']}:{values['-END_MINUTE-']}:{values['-END_SECOND-']}" full_end = f"{end_date} {end_time}" if end_date else "" if not start_date or not end_date: print("错误:日期预设配置无效") return print(f"当前日期范围: {full_start} 至 {full_end}") service = EdgeService(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedgedriver.exe") options = EdgeOptions() options.use_chromium = True driver = webdriver.Edge(service=service, options=options) modified_values = values.copy() modified_values['-FULL_START-'] = full_start modified_values['-FULL_END-'] = full_end driver.get(values['-URL-']) time.sleep(2) for label in current_order: element_config = config['elements'].get(label, {}) if element_config and element_config.get('identifier'): handle_element(driver, label, element_config, modified_values) time.sleep(0.5) print("自动化操作成功完成!") print(f"网页将保持打开状态{STAY_DURATION}秒...") time.sleep(STAY_DURATION) except Exception as e: print(f"执行过程中发生错误: {str(e)}") if 'driver' in locals(): driver.save_screenshot(f'error_{int(time.time())}.png') finally: if 'driver' in locals(): driver.quit() print("浏览器已关闭") def handle_element(driver, label, element_config, config): element_type = ELEMENT_TYPES[element_config['type']] identifier = element_config['identifier'] clean_identifier = identifier.replace(' ', '') schedule_config = config start_identifier = schedule_config.get('start_identifier', '开始时间') end_identifier = schedule_config.get('end_identifier', '结束时间') try: if element_type == 'button': xpath = f"//div[translate(normalize-space(), ' ', '') = '{clean_identifier}']" element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath))) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element) driver.execute_script("arguments[0].click();", element) elif element_type == 'checkbox': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/preceding-sibling::input[@type='checkbox']" checkbox = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) if not checkbox.is_selected(): driver.execute_script("arguments[0].click();", checkbox) elif element_type == 'radio': xpath = f"//input[@type='radio']/following-sibling::text()[contains(., '{identifier}')]/preceding::input[1]" radio = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) driver.execute_script("arguments[0].click();", radio) elif element_type == 'dropdown': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::select[1]" select_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) Select(select_element).select_by_index(0) elif element_type == 'tab': xpath = f"//div[contains(@class,'tab')]/span[translate(normalize-space(), ' ', '')='{clean_identifier}']" tab = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath))) driver.execute_script("arguments[0].click();", tab) time.sleep(1) elif element_type == 'time': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::input[1]" field = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) schedule_config = config.get('schedule', {}) start_id = schedule_config.get('start_identifier', '开始时间') end_id = schedule_config.get('end_identifier', '结束时间') if element_config['identifier'] == start_id: full_value = config.get('-FULL_START-', '') elif element_config['identifier'] == end_id: full_value = config.get('-FULL_END-', '') else: print(f"未配置的时间字段: {element_config['identifier']}") return attempts = [] if full_value: attempts.append(full_value) if ' ' in full_value: attempts.append(full_value.split(' ')[0]) attempts.append(full_value.split(' ')[1]) success = False for attempt in attempts: try: field.clear() field.send_keys(attempt) time.sleep(0.5) current_value = field.get_attribute('value') if current_value.strip() == attempt.strip(): success = True break except Exception as e: print(f"尝试输入 '{attempt}' 失败: {str(e)}") continue if not success: raise ValueError(f"无法输入日期时间: {full_value}") elif element_type == 'text': try: # 查找包含标识文本的元素 base_element = WebDriverWait(driver, 20).until( EC.presence_of_element_located( (By.XPATH, f"//*[contains(text(), '{identifier}')]")) ) # 查找最近的输入框(前、后或父级相邻) input_element = base_element.find_element(By.XPATH, "./following-sibling::input | " + "./preceding-sibling::input | " + "../following-sibling::input | " + "ancestor::div/following-sibling::input" ) # 清空并输入文本 input_element.clear() input_element.send_keys(element_config.get('text', '')) print(f"成功输入文本: {element_config['text']}") except Exception as e: print(f"查找输入框失败: {str(e)}") raise return print(f"成功处理: {label}") except Exception as e: print(f"处理 {label} 时出错: {str(e)}") raise if __name__ == "__main__": create_gui() 以上代码的删除功能键点击无效,点击上下移动界面闪退,修改
05-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值