|
| 1 | +import tkinter as tk |
| 2 | +from collections import OrderedDict |
| 3 | +from tkinter import ttk, filedialog, messagebox |
| 4 | + |
| 5 | +class LogViewerApp: |
| 6 | + def __init__(self, root): |
| 7 | + self.root = root |
| 8 | + self.root.title("Log Viewer") |
| 9 | + |
| 10 | + self.filter_options_rand = { |
| 11 | + 'okhttp': 'green', |
| 12 | + 'CrashlyticsHelper': 'red', |
| 13 | + 'Stream': 'blue', |
| 14 | + 'ViewModel': 'yellow', |
| 15 | + 'Firebase': 'orange', |
| 16 | + 'Network': 'gray', |
| 17 | + 'Camera': 'cyan' |
| 18 | + } |
| 19 | + |
| 20 | + self.filter_options = OrderedDict(sorted(self.filter_options_rand.items())) |
| 21 | + |
| 22 | + self.log_entries = [] |
| 23 | + |
| 24 | + self.filter_var = tk.StringVar() |
| 25 | + self.filter_var.set('Select Filter') |
| 26 | + |
| 27 | + self.filter_dropdown = tk.OptionMenu(root, self.filter_var, *self.filter_options.keys(), 'Select Filter') |
| 28 | + self.filter_dropdown.pack(pady=10) |
| 29 | + self.filter_var.trace_add('write', self.apply_filter) |
| 30 | + |
| 31 | + self.new_filter_entry = ttk.Entry(root, width=20) |
| 32 | + self.new_filter_entry.pack(pady=10) |
| 33 | + |
| 34 | + self.apply_new_filter_button = tk.Button(root, text="Apply New Filter", command=self.add_new_filter) |
| 35 | + self.apply_new_filter_button.pack(pady=10) |
| 36 | + |
| 37 | + self.canvas = tk.Canvas(root, height=300, width=800) |
| 38 | + self.canvas.pack(expand=True, fill=tk.BOTH) |
| 39 | + |
| 40 | + self.frame = ttk.Frame(self.canvas) |
| 41 | + self.canvas.create_window((0, 0), window=self.frame, anchor='nw') |
| 42 | + |
| 43 | + self.tree = ttk.Treeview(self.frame, columns=('Log',), show='tree', height=30) |
| 44 | + self.tree.heading('#0', text='Log Entries') |
| 45 | + self.tree.column('#0', width=1000, minwidth=500, stretch=tk.YES) |
| 46 | + |
| 47 | + self.x_scrollbar = tk.Scrollbar(self.frame, orient='horizontal', command=self.tree.xview) |
| 48 | + self.x_scrollbar.pack(side='bottom', fill=tk.BOTH, expand=True) |
| 49 | + self.tree.configure(xscrollcommand=self.x_scrollbar.set) |
| 50 | + |
| 51 | + self.y_scrollbar = tk.Scrollbar(self.frame, orient='vertical', command=self.tree.yview) |
| 52 | + self.y_scrollbar.pack(side='right', fill=tk.BOTH, expand=True) |
| 53 | + self.tree.configure(yscrollcommand=self.y_scrollbar.set) |
| 54 | + |
| 55 | + self.tree.pack(expand=True, fill=tk.BOTH) |
| 56 | + |
| 57 | + self.load_button = tk.Button(root, text="Browse Log File", command=self.browse_file) |
| 58 | + self.load_button.pack(pady=10) |
| 59 | + |
| 60 | + # Configure row and column weights for responsiveness |
| 61 | + root.columnconfigure(0, weight=1) |
| 62 | + root.rowconfigure(0, weight=1) |
| 63 | + self.frame.columnconfigure(0, weight=1) |
| 64 | + self.frame.rowconfigure(0, weight=1) |
| 65 | + |
| 66 | + def browse_file(self): |
| 67 | + file_path = filedialog.askopenfilename(filetypes=[("Log Files", "*.log")]) |
| 68 | + if file_path: |
| 69 | + self.log_entries = self.load_log_file(file_path) |
| 70 | + self.display_log() |
| 71 | + |
| 72 | + def load_log_file(self, file_path): |
| 73 | + try: |
| 74 | + with open(file_path, 'r', encoding='utf-8') as file: |
| 75 | + return file.readlines() |
| 76 | + except Exception as e: |
| 77 | + return [f"Error loading file: {str(e)}"] |
| 78 | + |
| 79 | + def apply_filter(self, *args): |
| 80 | + selected_filter = self.filter_var.get() |
| 81 | + if selected_filter != 'Select Filter': |
| 82 | + self.display_log(selected_filter) |
| 83 | + else: |
| 84 | + self.display_log() |
| 85 | + |
| 86 | + def add_new_filter(self): |
| 87 | + new_filter = self.new_filter_entry.get() |
| 88 | + if new_filter and new_filter not in self.filter_options: |
| 89 | + confirmation = messagebox.askquestion("Add Filter", f"Do you want to add '{new_filter}' as a filter?") |
| 90 | + if confirmation == 'yes': |
| 91 | + color = self.get_unused_color() |
| 92 | + self.filter_options[new_filter] = color |
| 93 | + self.filter_dropdown['menu'].add_command(label=new_filter, command=tk._setit(self.filter_var, new_filter)) |
| 94 | + self.filter_var.set(new_filter) |
| 95 | + else: |
| 96 | + self.display_log(new_filter) |
| 97 | + |
| 98 | + def get_unused_color(self): |
| 99 | + used_colors = set(self.filter_options.values()) |
| 100 | + available_colors = ['green', 'red', 'blue', 'gray', 'yellow', 'cyan', 'orange'] |
| 101 | + for color in available_colors: |
| 102 | + if color not in used_colors: |
| 103 | + return color |
| 104 | + return 'white' |
| 105 | + |
| 106 | + def display_log(self, filter_keyword=None): |
| 107 | + self.tree.delete(*self.tree.get_children()) |
| 108 | + |
| 109 | + filter_logs = {} |
| 110 | + |
| 111 | + for line in self.log_entries: |
| 112 | + line_lower = line.lower() |
| 113 | + |
| 114 | + for keyword, color in self.filter_options.items(): |
| 115 | + if keyword.lower() in line_lower and (filter_keyword is None or keyword.lower() == filter_keyword.lower()): |
| 116 | + if keyword not in filter_logs: |
| 117 | + filter_logs[keyword] = [] |
| 118 | + |
| 119 | + filter_logs[keyword].append(line) |
| 120 | + break |
| 121 | + |
| 122 | + for keyword, logs in filter_logs.items(): |
| 123 | + section_id = self.tree.insert('', 'end', text=keyword.title(), open=True) |
| 124 | + for log in logs: |
| 125 | + if filter_keyword and keyword.lower() == filter_keyword.lower(): |
| 126 | + self.tree.insert(section_id, tk.END, text=tk.Text(log, wrap=tk.WORD), tags=('plain_text',)) |
| 127 | + else: |
| 128 | + self.tree.insert(section_id, tk.END, text=tk.Text(log, wrap=tk.WORD), tags=(f'{color}_title',)) |
| 129 | + |
| 130 | + self.tree.tag_configure('green_title', foreground='green', font=('bold')) |
| 131 | + self.tree.tag_configure('red_title', foreground='red', font=('bold')) |
| 132 | + self.tree.tag_configure('blue_title', foreground='blue', font=('bold')) |
| 133 | + self.tree.tag_configure('gray_title', foreground='gray', font=('bold')) |
| 134 | + self.tree.tag_configure('plain_text', foreground='white', font=('bold')) |
| 135 | + |
| 136 | +if __name__ == "__main__": |
| 137 | + root = tk.Tk() |
| 138 | + app = LogViewerApp(root) |
| 139 | + root.mainloop() |
0 commit comments