week_14_ Word Search

本文介绍了一种使用深度优先搜索(DFS)算法解决二维网格中查找指定单词的问题。通过递归方式遍历网格,检查给定单词是否存在。文章详细解释了算法步骤,并提供了完整的C++代码实现。

Description

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

For example,
Given board =

[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.

Solution

这是一道非常基本的图论题,解答方式无非是广搜与深搜。广搜对于深搜来说效率更快,但是对于这道题来说,其每个字符只能被使用一次的限制条件很难在广搜中进行约束,故采取深搜进行实现,思路如下:

① 首先申请一个与board等同大小的二维数组mark,对每个字符的使用情况进行记录。

② 对board进行遍历,对字符串的首字母进行匹配。

③ 匹配成功后,对递归函数DFS进行调用。函数首先对字符总长度与当前匹配下标进行比对,若相等,则字符串存在,否则进入④

④ 以当前的行列,对上下左右四个位置的坐标进行遍历,若坐标合法,字符匹配且尚未被使用,在mark数组中更新使用情况,进入下一层递归。

⑤ 递归返回后,检查是否已经匹配到字符串,若匹配,则返回上一层递归,若不匹配,将当前遍历位置在mark数组中设置为未使用,继续对上下左右中其余位置进行遍历。

代码实现如下:

// 根据给定的字符串map寻找指定的单词
class Solution {
public:
	void DFS(int _row, int _column, bool** mark, vector<vector<char>>& board, string& word, int index) {
		// 判断是否匹配成功
		if (word.size() == index) {
			IsExist = true;
			return;
		}
		// 按方向遍历
		for (int i = 0; i < 4; i++) {
			int new_row = _row + dir[i][0];
			int new_column = _column + dir[i][1];
			// 合法下标
			if (new_row >= 0 && new_row < row && new_column >= 0 && new_column < column) {
				// 未被使用且匹配
				if (mark[new_row][new_column] == false && board[new_row][new_column] == word[index]) {
					mark[new_row][new_column] = true;
					DFS(new_row, new_column, mark, board, word, index + 1);
					if (IsExist == false)
						mark[new_row][new_column] = false;
					else
						return;
				}
			}
		}
	}

	bool exist(vector<vector<char>>& board, string word) {
		// size
		row = board.size();
		column = board[0].size();

		// 申请相应的标记数组,并初始化
		bool **mark = new bool*[row];
		for (int i = 0; i < row; i++) {
			mark[i] = new bool[column];
			for (int j = 0; j < column; j++)
				mark[i][j] = false;
		}

		IsExist = false;

		// 寻找首字母
		for (int i = 0; i < board.size() && IsExist == false; i++) {
			for (int j = 0; j < board[i].size() && IsExist == false; j++) {
				// 首字母匹配,进行深搜
				if (board[i][j] == word[0]) {
					mark[i][j] = true;
					DFS(i, j, mark, board, word, 1);
					if (IsExist == false)
						mark[i][j] = false;
				}
			}
		}
        
		// 释放标记数组
		for (int i = 0; i < row; i++)
			delete [] mark[i];
		delete[] mark;

		return IsExist;
	}
private:
	// 方向
	int dir[4][4] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
	// size
	int row;
	int column;
	// exist
	bool IsExist = false;
};

运行结果如下:


# -*- coding: utf-8 -*- import tkinter as tk from tkinter import ttk, messagebox import json import os from datetime import datetime, timedelta import threading import sys class SmartStickyNote: def __init__(self, root): self.root = root self.root.title("智能便签") # 初始化设置 self.settings = { 'view_mode': 'all', # 视图模式: all/pinned/completed 'theme': 'light', # 主题: light/dark 'opacity': 0.9, # 窗口不透明度: 0.0-1.0 'font_size': 10, # 默认字体大小 'sort_by': 'created' # 排序方式: created/modified/urgency } # 初始化颜色方案 self.init_colors() # 初始化样式 self.setup_styles() # 初始化数据 self.notes = [] self.recycle_bin = [] # 创建UI元素 self.create_widgets() # 加载保存的数据 self.load_data() # 渲染笔记列表 self.render_notes() # 启动后台任务 self.start_background_tasks() def init_colors(self): """初始化颜色方案""" # 浅色主题 self.bg_color = '#FFFFFF' # 背景色 self.secondary_color = '#F5F5F5' # 次要背景色 self.text_color = '#000000' # 文本颜色 self.accent_color = '#42A5F5' # 强调色 # 可以根据主题切换颜色 if hasattr(self, 'settings') and self.settings.get('theme') == 'dark': self.bg_color = '#212121' self.secondary_color = '#424242' self.text_color = '#FFFFFF' self.accent_color = '#BB86FC' def setup_styles(self): """配置自定义样式""" style = ttk.Style() # 基础样式 style.theme_use('clam') # 使用一个支持样式修改的主题 # 紧急程度按钮样式 urgency_colors = { 0: ('#E8F5E9', '低'), # 绿色 1: ('#E3F2FD', '普通'), # 蓝色 2: ('#FFE0B2', '重要'), # 橙色 3: ('#FFCDD2', '紧急') # 红色 } for level, (color, text) in urgency_colors.items(): style.configure( f'Urgency.TButton.{level}', background=color, foreground='black', font=('Microsoft YaHei', 8), padding=2, borderwidth=1, relief='raised' ) style.map( f'Urgency.TButton.{level}', background=[('active', color), ('disabled', '#F5F5F5')], relief=[('pressed', 'sunken'), ('!pressed', 'raised')] ) # 框架样式 style.configure('Note.TFrame', background=self.bg_color, bordercolor='#E0E0E0', lightcolor='#F5F5F5', darkcolor='#E0E0E0') style.configure('Pinned.TFrame', background='#FFF9C4', bordercolor='#FFD54F') style.configure('Completed.TFrame', background='#E8F5E9', bordercolor='#A5D6A7') style.configure('Deleted.TFrame', background='#F5F5F5', bordercolor='#BDBDBD') style.configure('UrgentFlash.TFrame', background='#FFEBEE', bordercolor='#EF9A9A') # 按钮样式 style.configure('Accent.TButton', font=('Microsoft YaHei', 9, 'bold'), foreground='#FFFFFF', background='#42A5F5', padding=5) style.map('Accent.TButton', background=[('active', '#1E88E5'), ('disabled', '#BBDEFB')]) # 标签样式 style.configure('Title.TLabel', font=('Microsoft YaHei', 10, 'bold'), foreground='#212121') style.configure('Subtitle.TLabel', font=('Microsoft YaHei', 8), foreground='#616161') def load_data(self): """加载保存的数据""" try: if os.path.exists(self.data_file): with open(self.data_file, 'r', encoding='utf-8') as f: data = json.load(f) self.notes = data.get('notes', []) self.settings = {**self.settings, **data.get('settings', {})} if os.path.exists(self.recycle_file): with open(self.recycle_file, 'r', encoding='utf-8') as f: self.recycle_bin = json.load(f) except Exception as e: messagebox.showerror("错误", f"加载数据失败: {str(e)}") try: with open('notes_data.json', 'r') as f: data = json.load(f) self.notes = data.get('notes', []) self.recycle_bin = data.get('recycle_bin', []) # 合并设置,保留默认值 self.settings = {**self.settings, **data.get('settings', {})} except FileNotFoundError: # 文件不存在时使用默认设置 pass def save_data(self): """保存数据到文件""" data = { 'notes': self.notes, 'recycle_bin': self.recycle_bin, 'settings': self.settings } try: with open(self.data_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) with open(self.recycle_file, 'w', encoding='utf-8') as f: json.dump(self.recycle_bin, f, ensure_ascii=False, indent=2) except Exception as e: messagebox.showerror("错误", f"保存数据失败: {str(e)}") def create_widgets(self): """创建界面组件""" # 主框架 self.main_frame = ttk.Frame(self.root) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 工具栏 self.create_toolbar() # 笔记列表容器 self.create_notes_list() # 新建笔记表单 (初始隐藏) self.create_new_note_form() # 设置窗口透明度 self.root.attributes('-alpha', self.settings['opacity']) # 设置窗口置顶 self.root.attributes('-topmost', self.settings['always_on_top']) def create_toolbar(self): """创建工具栏""" toolbar = ttk.Frame(self.root, padding=5) toolbar.pack(fill=tk.X) # 不透明度控制 opacity_frame = ttk.Frame(toolbar) opacity_frame.pack(side=tk.RIGHT, padx=5) ttk.Label(opacity_frame, text="不透明度:").pack(side=tk.LEFT) # 使用 get() 方法获取 opacity 设置,提供默认值 self.opacity = tk.DoubleVar(value=self.settings.get('opacity', 0.9)) opacity_slider = ttk.Scale( opacity_frame, from_=0.3, to=1.0, variable=self.opacity, command=lambda v: self.root.attributes('-alpha', float(v)) ) opacity_slider.pack(side=tk.LEFT, padx=5) # 搜索框 search_frame = ttk.Frame(toolbar) search_frame.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) self.search_var = tk.StringVar() search_entry = ttk.Entry( search_frame, textvariable=self.search_var, style='Search.TEntry' ) search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) search_entry.bind('<KeyRelease>', lambda e: self.render_notes()) search_btn = ttk.Button( search_frame, text="🔍", width=3, command=self.show_advanced_search ) search_btn.pack(side=tk.LEFT, padx=(2, 0)) # 视图模式选择 self.view_mode = tk.StringVar(value=self.settings['view_mode']) view_options = ttk.Combobox( toolbar, textvariable=self.view_mode, values=['active', 'pinned', 'all'], width=8, state='readonly' ) view_options.pack(side=tk.LEFT, padx=2) view_options.bind('<<ComboboxSelected>>', self.change_view_mode) # 历史记录按钮 history_btn = ttk.Button( toolbar, text="📅", width=3, command=lambda: self.show_history('week') ) history_btn.pack(side=tk.LEFT, padx=2) # 新建按钮 new_btn = ttk.Button( toolbar, text="+ 新建", style='Accent.TButton', command=self.show_new_note_form ) new_btn.pack(side=tk.RIGHT, padx=2) # 透明度滑块 self.opacity = tk.DoubleVar(value=self.settings['opacity']) opacity_slider = ttk.Scale( toolbar, from_=0.5, to=1.0, variable=self.opacity, command=self.change_opacity, length=80 ) opacity_slider.pack(side=tk.RIGHT, padx=5) ttk.Label(toolbar, text="透明度:").pack(side=tk.RIGHT) # 置顶按钮 self.topmost_btn = ttk.Button( toolbar, text="📌" if self.settings['always_on_top'] else "📋", width=3, command=self.toggle_topmost ) self.topmost_btn.pack(side=tk.RIGHT, padx=2) def create_notes_list(self): """创建笔记列表区域""" # 笔记列表容器 self.notes_canvas = tk.Canvas( self.main_frame, bg=self.bg_color, highlightthickness=0 ) self.notes_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 滚动条 scrollbar = ttk.Scrollbar( self.main_frame, orient=tk.VERTICAL, command=self.notes_canvas.yview ) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.notes_canvas.configure(yscrollcommand=scrollbar.set) self.notes_canvas.bind('<Configure>', lambda e: self.notes_canvas.configure( scrollregion=self.notes_canvas.bbox("all") )) # 笔记框架容器 self.notes_frame = ttk.Frame(self.notes_canvas) self.notes_canvas.create_window((0, 0), window=self.notes_frame, anchor="nw") def create_new_note_form(self): """创建新建笔记表单""" self.new_note_frame = ttk.LabelFrame( self.main_frame, text="新建笔记", padding=10 ) # 标题输入框 ttk.Label(self.new_note_frame, text="标题:").pack(anchor=tk.W, padx=5, pady=2) self.note_title = ttk.Entry(self.new_note_frame) self.note_title.pack(fill=tk.X, padx=5, pady=2) # 内容文本框 ttk.Label(self.new_note_frame, text="内容:").pack(anchor=tk.W, padx=5, pady=2) self.note_content = tk.Text( self.new_note_frame, height=5, wrap=tk.WORD, bg=self.secondary_color, fg=self.text_color, insertbackground=self.text_color, relief=tk.FLAT, padx=5, pady=5 ) self.note_content.pack(fill=tk.BOTH, expand=True, padx=5, pady=2) # 紧急程度选择 urgency_frame = ttk.Frame(self.new_note_frame) urgency_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(urgency_frame, text="紧急程度:").pack(side=tk.LEFT) self.urgency = tk.IntVar(value=1) # 默认普通 urgency_levels = [ ("低", 0), ("普通", 1), ("重要", 2), ("紧急", 3) ] for text, level in urgency_levels: btn = ttk.Radiobutton( urgency_frame, text=text, variable=self.urgency, value=level ) btn.pack(side=tk.LEFT, padx=2) # 按钮区域 btn_frame = ttk.Frame(self.new_note_frame) btn_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Button( btn_frame, text="取消", command=self.hide_new_note_form ).pack(side=tk.RIGHT, padx=2) ttk.Button( btn_frame, text="保存", style='Accent.TButton', command=self.save_new_note ).pack(side=tk.RIGHT, padx=2) def start_timers(self): """启动定时检查任务""" self.root.after(1000, self.check_note_status) self.root.after(600000, self.check_note_status) # 每10分钟检查一次 self.root.after(3600000, self.check_urgent_tasks) # 每小时检查紧急任务 self.root.after(86400000, self.empty_recycle_bin) # 每天检查回收站 def render_notes(self): """渲染笔记列表""" for widget in self.notes_frame.winfo_children(): widget.destroy() filtered_notes = self.filter_notes() if not filtered_notes: ttk.Label( self.notes_frame, text="暂无笔记" if self.view_mode.get() != 'pinned' else "无固定笔记", font=('Microsoft YaHei', 10, 'italic') ).pack(pady=20) return filtered_notes.sort(key=lambda x: ( -x.get('pinned', False), -x['urgency'], x['created_at'] )) for note in filtered_notes: self.create_note_widget(note) self.notes_frame.update_idletasks() self.notes_canvas.config(scrollregion=self.notes_canvas.bbox("all")) def filter_notes(self): """根据视图模式和搜索条件过滤笔记""" view_mode = self.view_mode.get() search_text = self.search_var.get().lower() filtered = [] for note in self.notes: if note['status'] == 'completed' and view_mode != 'all': continue if view_mode == 'pinned' and not note.get('pinned', False): continue if search_text and search_text not in note['title'].lower() and search_text not in note['content'].lower(): continue filtered.append(note) return filtered def create_note_widget(self, note): """创建笔记组件""" urgency_colors = { 0: ('#E8F5E9', '低'), # 绿色 1: ('#E3F2FD', '普通'), # 蓝色 2: ('#FFE0B2', '重要'), # 橙色 3: ('#FFCDD2', '紧急') # 红色 } status_styles = { 'pending': ('○', '#FFFFFF'), 'ongoing': ('◔', '#F8F8F8'), 'completed': ('✓', '#E8F5E9') } status_icon, bg_color = status_styles.get(note['status'], ('?', '#FFFFFF')) urgency_color, urgency_text = urgency_colors.get(note['urgency'], ('#E3F2FD', '普通')) frame_style = 'Pinned.TFrame' if note.get('pinned', False) else 'Note.TFrame' if note['status'] == 'completed': frame_style = 'Completed.TFrame' frame = ttk.Frame( self.notes_frame, borderwidth=2, relief=tk.RAISED, padding=(10, 8), style=frame_style ) frame.pack(fill=tk.X, pady=5, padx=2) # 标题区域 title_frame = ttk.Frame(frame) title_frame.pack(fill=tk.X, pady=(0, 5)) # 状态和紧急程度 status_label = ttk.Label( title_frame, text=status_icon, font=('Arial', 12), width=2 ) status_label.pack(side=tk.LEFT, padx=(0, 5)) urgency_label = ttk.Label( title_frame, text=urgency_text, font=('Microsoft YaHei', 8, 'bold'), background=urgency_color, foreground='black', padding=(3, 0), borderwidth=1, relief=tk.SOLID ) urgency_label.pack(side=tk.LEFT, padx=(0, 5)) # 笔记标题 title_label = ttk.Label( title_frame, text=note['title'] or '无标题', font=('Microsoft YaHei', 10, 'bold'), anchor=tk.W ) title_label.pack(side=tk.LEFT, fill=tk.X, expand=True) # 内容区域 content_frame = ttk.Frame(frame) content_frame.pack(fill=tk.X, pady=(0, 8)) # 动态高度的文本框 content = tk.Text( content_frame, wrap=tk.WORD, font=('Microsoft YaHei', 9), background=bg_color, foreground=self.text_color, borderwidth=0, highlightthickness=0, padx=5, pady=3, height=1 ) content.insert(tk.END, note['content']) content.config(state=tk.DISABLED) content.pack(fill=tk.BOTH, expand=True) # 动态调整高度 line_count = content.count('1.0', 'end', 'displaylines')[0] content.configure(height=min(max(line_count, 1), 10)) # 元信息区域 meta_frame = ttk.Frame(frame) meta_frame.pack(fill=tk.X) # 创建时间 created_time = datetime.fromisoformat(note['created_at']).strftime('%m/%d %H:%M') ttk.Label( meta_frame, text=f"🕒 {created_time}", font=('TkDefaultFont', 8), foreground='#666666' ).pack(side=tk.LEFT) # 操作按钮区域 btn_frame = ttk.Frame(frame) btn_frame.pack(fill=tk.X, pady=(5, 0)) # 左侧按钮组 - 紧急程度调整 urgency_frame = ttk.Frame(btn_frame) urgency_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) for level, (color, text) in urgency_colors.items(): btn = ttk.Button( urgency_frame, text=text, style=f'Urgency.TButton.{level}', command=lambda l=level, nid=note['id']: self.change_urgency(nid, l) ) btn.pack(side=tk.LEFT, padx=1) # 右侧按钮组 - 操作按钮 action_frame = ttk.Frame(btn_frame) action_frame.pack(side=tk.RIGHT) # 固定按钮 pin_btn = ttk.Button( action_frame, text="📍" if note.get('pinned', False) else "📌", width=3, command=lambda nid=note['id']: self.toggle_pin(nid) ) pin_btn.pack(side=tk.LEFT, padx=2) # 状态切换按钮 status_btn = ttk.Button( action_frame, text="✓" if note['status'] != 'completed' else "↩", width=3, command=lambda nid=note['id']: self.toggle_note_status(nid) ) status_btn.pack(side=tk.LEFT, padx=2) # 删除按钮 del_btn = ttk.Button( action_frame, text="🗑", width=3, command=lambda nid=note['id']: self.delete_note(nid) ) del_btn.pack(side=tk.LEFT, padx=2) # 编辑按钮 edit_btn = ttk.Button( action_frame, text="✏️", width=3, command=lambda nid=note['id']: self.edit_note(nid) ) edit_btn.pack(side=tk.LEFT, padx=2) # 为笔记组件添加ID标识 frame.note_id = note['id'] def edit_note(self, note_id): """编辑现有笔记""" note = next((n for n in self.notes if n['id'] == note_id), None) if not note: return # 创建编辑窗口 edit_window = tk.Toplevel(self.root) edit_window.title("编辑笔记") edit_window.geometry("400x500") # 标题输入框 ttk.Label(edit_window, text="标题:").pack(anchor=tk.W, padx=5, pady=2) title_entry = ttk.Entry(edit_window) title_entry.insert(0, note['title']) title_entry.pack(fill=tk.X, padx=5, pady=2) # 内容文本框 ttk.Label(edit_window, text="内容:").pack(anchor=tk.W, padx=5, pady=2) content_text = tk.Text( edit_window, height=10, wrap=tk.WORD, bg=self.secondary_color, fg=self.text_color, insertbackground=self.text_color, padx=5, pady=5 ) content_text.insert(tk.END, note['content']) content_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=2) # 紧急程度选择 urgency_frame = ttk.Frame(edit_window) urgency_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(urgency_frame, text="紧急程度:").pack(side=tk.LEFT) urgency_var = tk.IntVar(value=note['urgency']) urgency_levels = [ ("低", 0), ("普通", 1), ("重要", 2), ("紧急", 3) ] for text, level in urgency_levels: btn = ttk.Radiobutton( urgency_frame, text=text, variable=urgency_var, value=level ) btn.pack(side=tk.LEFT, padx=2) # 按钮区域 btn_frame = ttk.Frame(edit_window) btn_frame.pack(fill=tk.X, padx=5, pady=5) def save_changes(): """保存编辑内容""" note['title'] = title_entry.get().strip() note['content'] = content_text.get("1.0", tk.END).strip() note['urgency'] = urgency_var.get() note['modified_at'] = datetime.now().isoformat() self.save_data() self.render_notes() edit_window.destroy() messagebox.showinfo("提示", "笔记已更新") ttk.Button( btn_frame, text="保存", style='Accent.TButton', command=save_changes ).pack(side=tk.RIGHT, padx=2) ttk.Button( btn_frame, text="取消", command=edit_window.destroy ).pack(side=tk.RIGHT, padx=2) def delete_note(self, note_id): """删除笔记到回收站""" if not messagebox.askyesno("确认", "确定要删除这个笔记吗?"): return for i, note in enumerate(self.notes): if note['id'] == note_id: deleted_note = self.notes.pop(i) deleted_note['deleted_at'] = datetime.now().isoformat() self.recycle_bin.append(deleted_note) break self.save_data() self.render_notes() messagebox.showinfo("提示", "笔记已移到回收站") def show_new_note_form(self): """显示新建笔记表单""" self.new_note_frame.pack(fill=tk.BOTH, expand=True, pady=5) self.note_title.focus() def hide_new_note_form(self): """隐藏新建笔记表单""" self.new_note_frame.pack_forget() self.note_title.delete(0, tk.END) self.note_content.delete('1.0', tk.END) self.urgency.set(1) # 重置为普通 def save_new_note(self): """保存新笔记""" title = self.note_title.get().strip() content = self.note_content.get('1.0', tk.END).strip() urgency = self.urgency.get() if not title and not content: messagebox.showwarning("提示", "请输入标题或内容") return new_note = { 'id': str(datetime.now().timestamp()), 'title': title or "无标题", 'content': content, 'urgency': urgency, 'status': 'pending', 'created_at': datetime.now().isoformat(), 'modified_at': None, 'completed_at': None, 'pinned': False } self.notes.append(new_note) self.save_data() self.hide_new_note_form() self.render_notes() messagebox.showinfo("提示", "笔记已保存") def toggle_pin(self, note_id): """切换固定状态""" for note in self.notes: if note['id'] == note_id: note['pinned'] = not note.get('pinned', False) break self.save_data() self.render_notes() def toggle_note_status(self, note_id): """切换笔记完成状态""" for note in self.notes: if note['id'] == note_id: if note['status'] == 'completed': note['status'] = 'pending' note['completed_at'] = None else: note['status'] = 'completed' note['completed_at'] = datetime.now().isoformat() break self.save_data() self.render_notes() def change_urgency(self, note_id, urgency_level): """修改笔记紧急程度""" for note in self.notes: if note['id'] == note_id: note['urgency'] = urgency_level break self.save_data() self.render_notes() def change_view_mode(self, event=None): """改变视图模式""" self.settings['view_mode'] = self.view_mode.get() self.save_data() self.render_notes() def show_history(self, period='week'): """显示历史记录窗口""" history_window = tk.Toplevel(self.root) history_window.title(f"{period.capitalize()}历史记录") history_window.geometry("500x600") # 创建时间范围 now = datetime.now() if period == 'week': cutoff = now - timedelta(weeks=1) elif period == 'month': cutoff = now - timedelta(days=30) elif period == 'year': cutoff = now - timedelta(days=365) else: cutoff = now - timedelta(weeks=1) # 过滤历史笔记 history_notes = [ n for n in self.notes + self.recycle_bin if n.get('completed_at') or n.get('deleted_at') ] # 按时间排序 history_notes.sort(key=lambda x: x.get('completed_at') or x.get('deleted_at'), reverse=True) # 创建容器 canvas = tk.Canvas(history_window, bg=self.bg_color, highlightthickness=0) canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar = ttk.Scrollbar(history_window, orient=tk.VERTICAL, command=canvas.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) canvas.configure(yscrollcommand=scrollbar.set) canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) history_frame = ttk.Frame(canvas) canvas.create_window((0, 0), window=history_frame, anchor="nw") # 时间段选择 period_frame = ttk.Frame(history_window) period_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Button( period_frame, text="最近一周", command=lambda: self.update_history_view(history_window, 'week') ).pack(side=tk.LEFT, padx=2) ttk.Button( period_frame, text="最近一月", command=lambda: self.update_history_view(history_window, 'month') ).pack(side=tk.LEFT, padx=2) ttk.Button( period_frame, text="最近一年", command=lambda: self.update_history_view(history_window, 'year') ).pack(side=tk.LEFT, padx=2) # 渲染历史笔记 for note in history_notes: self.create_history_note_widget(history_frame, note) def create_history_note_widget(self, parent, note): """创建历史笔记组件""" # 样式配置 bg_color = '#F0F0F0' if note.get('deleted_at') else '#E8F5E9' border_color = '#D0D0D0' if note.get('deleted_at') else '#C8E6C9' frame = ttk.Frame( parent, borderwidth=2, relief=tk.RAISED, padding=5, style='Deleted.TFrame' if note.get('deleted_at') else 'Completed.TFrame' ) frame.pack(fill=tk.X, pady=2, padx=2) # 标题区域 title_frame = ttk.Frame(frame) title_frame.pack(fill=tk.X) # 状态图标 status_icon = "🗑" if note.get('deleted_at') else "✓" ttk.Label( title_frame, text=status_icon, font=('Arial', 12) ).pack(side=tk.LEFT, padx=(0, 5)) # 笔记标题 ttk.Label( title_frame, text=note['title'] or "无标题", font=('Microsoft YaHei', 10, 'bold'), anchor=tk.W ).pack(side=tk.LEFT, fill=tk.X, expand=True) # 时间信息 time_frame = ttk.Frame(frame) time_frame.pack(fill=tk.X, pady=(2, 0)) created_time = datetime.fromisoformat(note['created_at']).strftime('%Y/%m/%d %H:%M') ttk.Label( time_frame, text=f"创建: {created_time}", font=('TkDefaultFont', 8), foreground='#666666' ).pack(side=tk.LEFT) if note.get('completed_at'): completed_time = datetime.fromisoformat(note['completed_at']).strftime('%Y/%m/%d %H:%M') ttk.Label( time_frame, text=f"完成: {completed_time}", font=('TkDefaultFont', 8), foreground='#666666' ).pack(side=tk.LEFT, padx=(10, 0)) if note.get('deleted_at'): deleted_time = datetime.fromisoformat(note['deleted_at']).strftime('%Y/%m/%d %H:%M') ttk.Label( time_frame, text=f"删除: {deleted_time}", font=('TkDefaultFont', 8), foreground='#666666' ).pack(side=tk.LEFT, padx=(10, 0)) # 内容预览 content_preview = note['content'][:100] + "..." if len(note['content']) > 100 else note['content'] ttk.Label( frame, text=content_preview, font=('Microsoft YaHei', 9), wraplength=400, anchor=tk.W ).pack(fill=tk.X, pady=(5, 0)) # 恢复按钮(仅回收站项目) if note.get('deleted_at'): btn_frame = ttk.Frame(frame) btn_frame.pack(fill=tk.X, pady=(5, 0)) ttk.Button( btn_frame, text="恢复", command=lambda nid=note['id']: self.restore_note(nid) ).pack(side=tk.LEFT) ttk.Button( btn_frame, text="永久删除", command=lambda nid=note['id']: self.permanent_delete(nid) ).pack(side=tk.RIGHT) def update_history_view(self, window, period): """更新历史记录视图""" window.destroy() self.show_history(period) def restore_note(self, note_id): """从回收站恢复笔记""" for i, note in enumerate(self.recycle_bin): if note['id'] == note_id: restored_note = self.recycle_bin.pop(i) restored_note.pop('deleted_at', None) self.notes.append(restored_note) break self.save_data() messagebox.showinfo("提示", "笔记已恢复") self.show_history('week') # 刷新历史视图 def permanent_delete(self, note_id): """永久删除笔记""" if not messagebox.askyesno("确认", "确定要永久删除这个笔记吗?此操作不可恢复!"): return for i, note in enumerate(self.recycle_bin): if note['id'] == note_id: self.recycle_bin.pop(i) break self.save_data() messagebox.showinfo("提示", "笔记已永久删除") self.show_history('week') # 刷新历史视图 def empty_recycle_bin(self): """清空超过7天的回收站内容""" cutoff = datetime.now() - timedelta(days=7) self.recycle_bin = [item for item in self.recycle_bin if datetime.fromisoformat(item['deleted_at']) > cutoff] self.save_data() self.root.after(86400000, self.empty_recycle_bin) # 每天检查一次 def check_note_status(self): """检查笔记状态(定时任务)""" def _check(): try: now = datetime.now() for note in self.notes: if note['status'] == 'pending' and note['urgency'] >= 2: created_time = datetime.fromisoformat(note['created_at']) time_diff = now - created_time if time_diff.days >= 1 and note['urgency'] == 2: # 重要任务超过1天 self.root.after(0, lambda: self.notify_urgent_task(note)) elif time_diff.total_seconds() >= 10800 and note['urgency'] == 3: # 紧急任务超过3小时(10800秒) self.root.after(0, lambda: self.notify_urgent_task(note)) except Exception as e: print(f"笔记状态检查出错: {e}") finally: self.root.after(600000, self.check_note_status) # 每十分钟检查一次 threading.Thread(target=_check, daemon=True).start() def check_urgent_tasks(self): """检查紧急任务(定时任务)""" def _check(): try: now = datetime.now() urgent_notes = [] for note in self.notes: if note['status'] == 'pending' and note['urgency'] >= 2: created_time = datetime.fromisoformat(note['created_at']) time_diff = now - created_time if (note['urgency'] == 2 and time_diff.days >= 1) or \ (note['urgency'] == 3 and time_diff.total_seconds() >= 7200): urgent_notes.append(note) if urgent_notes: self.root.after(0, lambda: self.show_reminder_window(urgent_notes)) except Exception as e: print(f"紧急任务检查出错: {e}") finally: self.root.after(3600000, self.check_urgent_tasks) # 每小时检查一次 threading.Thread(target=_check, daemon=True).start() def notify_urgent_task(self, note): """通知紧急任务""" messagebox.showwarning( "待办提醒", f"【{note['title']}】\n\n{note['content'][:100]}...\n\n该任务尚未完成!", parent=self.root ) def show_reminder_window(self, notes): """显示提醒窗口""" if hasattr(self, 'reminder_window') and self.reminder_window.winfo_exists(): return self.reminder_window = tk.Toplevel(self.root) self.reminder_window.title("⚠️ 紧急任务提醒") self.reminder_window.geometry("500x400") self.reminder_window.protocol("WM_DELETE_WINDOW", self.on_reminder_close) # 顶部提示 header = ttk.Frame(self.reminder_window) header.pack(fill=tk.X, padx=10, pady=10) ttk.Label( header, text=f"您有 {len(notes)} 个紧急任务待处理", style='Title.TLabel', font=('Microsoft YaHei', 10, 'bold') ).pack(side=tk.LEFT) ttk.Button( header, text="关闭", command=self.on_reminder_close ).pack(side=tk.RIGHT) # 笔记列表区域 canvas = tk.Canvas(self.reminder_window) scrollbar = ttk.Scrollbar(self.reminder_window, orient="vertical", command=canvas.yview) scrollable_frame = ttk.Frame(canvas) scrollable_frame.bind( "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") # 添加紧急任务列表 for note in notes: frame = ttk.Frame( scrollable_frame, borderwidth=1, relief="solid", padding=10, style='UrgentFlash.TFrame' ) frame.pack(fill="x", padx=5, pady=5, ipady=5) # 标题和紧急程度 title_frame = ttk.Frame(frame) title_frame.pack(fill="x", pady=(0, 5)) ttk.Label( title_frame, text=f"【{note['urgency']}级】{note['title']}", font=('Microsoft YaHei', 10, 'bold'), foreground='#D32F2F' ).pack(side="left") # 创建时间 created_time = datetime.fromisoformat(note['created_at']).strftime('%m/%d %H:%M') ttk.Label( title_frame, text=f"创建于: {created_time}", font=('TkDefaultFont', 8), foreground='#666666' ).pack(side="right") # 内容预览 content_preview = note['content'][:200] + "..." if len(note['content']) > 200 else note['content'] ttk.Label( frame, text=content_preview, font=('Microsoft YaHei', 9), wraplength=400 ).pack(anchor="w") # 操作按钮 btn_frame = ttk.Frame(frame) btn_frame.pack(fill="x", pady=(5, 0)) ttk.Button( btn_frame, text="标记为已完成", command=lambda nid=note['id']: self.complete_and_close(nid) ).pack(side="left", padx=2) ttk.Button( btn_frame, text="查看详情", command=lambda nid=note['id']: self.focus_note(nid) ).pack(side="left", padx=2) ttk.Button( btn_frame, text="稍后提醒", command=lambda: self.reminder_window.after(1800000, self.show_reminder_window, [note]) # 30分钟后再次提醒 ).pack(side="right", padx=2) def complete_and_close(self, note_id): """标记为已完成并关闭提醒窗口""" for note in self.notes: if note['id'] == note_id: note['status'] = 'completed' note['completed_at'] = datetime.now().isoformat() break self.save_data() self.on_reminder_close() self.render_notes() messagebox.showinfo("提示", "任务已标记为已完成") def focus_note(self, note_id): """聚焦到指定笔记""" self.on_reminder_close() self.render_notes() # 确保笔记列表是最新的 # 滚动到指定笔记 for widget in self.notes_frame.winfo_children(): if hasattr(widget, 'note_id') and widget.note_id == note_id: self.notes_canvas.yview_moveto(widget.winfo_y() / self.notes_frame.winfo_height()) # 高亮显示 widget.configure(style='UrgentFlash.TFrame') self.root.after(3000, lambda: widget.configure(style='Note.TFrame')) break def on_reminder_close(self): """关闭提醒窗口""" if hasattr(self, 'reminder_window') and self.reminder_window.winfo_exists(): self.reminder_window.destroy() delattr(self, 'reminder_window') def show_advanced_search(self): """显示高级搜索窗口""" search_window = tk.Toplevel(self.root) search_window.title("高级搜索") search_window.geometry("400x300") # 搜索条件框架 condition_frame = ttk.LabelFrame(search_window, text="搜索条件", padding=10) condition_frame.pack(fill="both", expand=True, padx=10, pady=10) # 关键词搜索 ttk.Label(condition_frame, text="关键词:").grid(row=0, column=0, sticky="w", pady=2) keyword_entry = ttk.Entry(condition_frame) keyword_entry.grid(row=0, column=1, sticky="ew", pady=2) # 紧急程度 ttk.Label(condition_frame, text="紧急程度:").grid(row=1, column=0, sticky="w", pady=2) urgency_var = tk.StringVar(value="any") urgency_options = ttk.Combobox( condition_frame, textvariable=urgency_var, values=["任何", "低", "普通", "重要", "紧急"], state="readonly" ) urgency_options.grid(row=1, column=1, sticky="ew", pady=2) # 时间范围 ttk.Label(condition_frame, text="创建时间:").grid(row=2, column=0, sticky="w", pady=2) time_frame = ttk.Frame(condition_frame) time_frame.grid(row=2, column=1, sticky="ew", pady=2) time_var = tk.StringVar(value="any") ttk.Radiobutton(time_frame, text="任何时间", variable=time_var, value="any").pack(side="left") ttk.Radiobutton(time_frame, text="最近7天", variable=time_var, value="week").pack(side="left") ttk.Radiobutton(time_frame, text="最近30天", variable=time_var, value="month").pack(side="left") # 按钮区域 button_frame = ttk.Frame(search_window) button_frame.pack(fill="x", padx=10, pady=(0, 10)) ttk.Button( button_frame, text="搜索", style='Accent.TButton', command=lambda: self.apply_advanced_search( keyword_entry.get(), urgency_var.get(), time_var.get(), search_window ) ).pack(side="right", padx=5) ttk.Button( button_frame, text="取消", command=search_window.destroy ).pack(side="right", padx=5) def apply_advanced_search(self, keyword, urgency, time_range, window): """应用高级搜索条件""" # 转换紧急程度 urgency_map = {"任何": None, "低": 0, "普通": 1, "重要": 2, "紧急": 3} urgency_level = urgency_map.get(urgency) # 转换时间范围 now = datetime.now() if time_range == "week": cutoff = now - timedelta(days=7) elif time_range == "month": cutoff = now - timedelta(days=30) else: cutoff = None # 构建搜索字符串 search_parts = [] if keyword: search_parts.append(keyword.lower()) if urgency_level is not None: search_parts.append(f"urgency:{urgency_level}") if cutoff: search_parts.append(f"after:{cutoff.strftime('%Y-%m-%d')}") self.search_var.set(" ".join(search_parts)) window.destroy() self.render_notes() def toggle_topmost(self): """切换窗口置顶状态""" self.settings['always_on_top'] = not self.settings['always_on_top'] self.root.attributes('-topmost', self.settings['always_on_top']) self.topmost_btn.config(text="📌" if self.settings['always_on_top'] else "📋") self.save_data() def change_opacity(self, value): """改变窗口透明度""" opacity = float(value) self.root.attributes('-alpha', opacity) self.settings['opacity'] = opacity self.save_data() def run(self): """运行主循环""" self.root.mainloop() if __name__ == "__main__": root = tk.Tk() # 设置DPI感知 if sys.platform == 'win32': from ctypes import windll windll.shcore.SetProcessDpiAwareness(1) app = SmartStickyNote(root) app.run() 再次检查这些代码,包括所有的初始化的值,所有的都能被初始化都能被调用,最重要确保代码能够正确执行,请将修复后的完整的代码给我
05-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值