-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathmywidget.py
158 lines (138 loc) · 6.54 KB
/
mywidget.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python
# coding: utf-8
import sys
import tkinter as tk
import tkinter.ttk as ttk
import os
import csv
import M3U8Sites
from tkinter import messagebox
class RedirectConsole(tk.Listbox):
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self._cursor_y = 0
self._old_stdout_write = sys.stdout.write
sys.stdout.write = self._on_stdout_write
def __del__(self):
sys.stdout.write = self._old_stdout_write
def _on_stdout_write(self, msg):
self.configure(state="normal")
try:
msgs1 = msg.partition("\n")
while True:
msgs2 = msgs1[0].rpartition("\r")
if msgs2[1] == '\r':
newline = msgs2[2]
else:
newline = self.get(self._cursor_y) + msgs2[2]
if newline != "":
self.delete(self._cursor_y)
self.insert(self._cursor_y, newline)
self.see(self._cursor_y)
if msgs1[1] != "\n": break
self._cursor_y += 1
self.see(self._cursor_y)
if msgs1[2] == "": break
msgs1 = msgs1[2].partition("\n")
except Exception:
# 輸出到原來的 stdout IO
self._old_stdout_write(msg)
self.configure(state="disabled")
def clear_contents(self):
self.configure(state="normal")
self.delete(0, tk.END)
self.configure(state="disabled")
self._cursor_y = 0
class ScrollTreeView(ttk.Treeview):
""" ttk.Treeview with a vertical scroll bar """
def __init__(self, master, **kwargs):
_frame = tk.Frame(master)
super().__init__(_frame, **kwargs)
self.scrollbar = ttk.Scrollbar(_frame, orient=tk.VERTICAL, command=self.yview)
self.configure(yscrollcommand=self.scrollbar.set)
self.grid(row=0, column=0, sticky='NSEW')
self.scrollbar.grid(row=0, column=1, sticky='NSW')
self._frame = _frame
self._frame.columnconfigure(0, weight=1)
self._frame.rowconfigure(0, weight=1)
self.pack = self._frame.pack
self.grid = self._frame.grid
self.place = self._frame.place
class MyDownloadListView(ScrollTreeView):
_download_state_ = {'': 100, '已下載': 0, '未完成': 1, '下載中': 2, '等待中': 3, '已取消': 4, '網址錯誤': 99}
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.list_modified = False
self._colnames = ['網址', '名稱', '儲存位置', '狀態']
self.config(columns=self._colnames, show="headings")
self.heading(self._colnames[0], text=self._colnames[0], command=lambda:self._sort_column(self._colnames[0], False))
self.heading(self._colnames[1], text=self._colnames[1], command=lambda:self._sort_column(self._colnames[1], False))
self.heading(self._colnames[2], text=self._colnames[2], command=lambda:self._sort_column(self._colnames[2], False))
self.heading(self._colnames[3], text=self._colnames[3], command=lambda:self._sort_column(self._colnames[3], False))
self.column(self._colnames[0], width=80, minwidth=40, stretch=False)
self.column(self._colnames[1], stretch=True)
self.column(self._colnames[2], width=80, minwidth=40, stretch=False)
self.column(self._colnames[3], width=64, minwidth=40, stretch=False)
self.config(displaycolumns=[2, 0, 1, 3])
self.bind("<Delete>", self._on_key_delete_event)
self.bind("<B1-Motion>", self._move_row, add='+')
def _sort_column(self, col, reverse: bool):
l = [(self.set(k, col), k) for k in self.get_children('')]
if col == '狀態':
l = sorted(l, key=lambda s: MyDownloadListView._download_state_.__getitem__(s[0]), reverse=reverse)
else:
l = sorted(l, key=lambda s: s[0].lower(), reverse=reverse)
for index, (val, k) in enumerate(l):
self.move(k, '', index)
self.heading(col, command=lambda:self._sort_column(col, not reverse))
self.list_modified = True
def _on_key_delete_event(self, event):
if len(self.selection()) == 0: return
if not messagebox.askyesno("刪除選項", f"刪除{len(self.selection())}個選取的項目嗎?"): return
for selected_item in self.selection():
self.delete(selected_item)
self.list_modified = True
def _move_row(self, event):
if len(self.selection()) > 1: return
moveto = self.index(self.identify_row(event.y))
for s in self.selection():
self.move(s, '', moveto)
def update_item_state(self, url, state):
urlHash = hash(url)
if super().exists(urlHash):
self.set(urlHash, column=self._colnames[3], value=state)
self.list_modified = True
def isUrlExist(self, url):
return super().exists(hash(url))
def additem(self, url, saveName="", savePath="", state=""):
if M3U8Sites.VaildateUrl(url):
urlHash = hash(url)
if not super().exists(urlHash):
self.insert("", "end", iid=urlHash, values=[url, saveName, savePath, state])
self.list_modified = True
else:
v_old = self.set(urlHash)
if savePath != v_old[2]:
self.set(urlHash, column=self._colnames[2], value=savePath)
self.list_modified = True
if saveName != '' and saveName != v_old[1]:
self.set(urlHash, column=self._colnames[1], value=saveName)
self.list_modified = True
def save_to_csv(self, csvName):
if not self.list_modified : return
with open(csvName, 'w', encoding='utf-8') as f:
csvwriter = csv.DictWriter(f, fieldnames=self._colnames)
csvwriter.writeheader()
items = self.get_children()
for it in items:
data = self.set(it)
csvwriter.writerow(data)
self.list_modified = False
def load_from_csv(self, csvName):
if not os.path.exists(csvName): return
with open(csvName, 'r',encoding='utf-8') as f:
csvreader = csv.DictReader(f)
for rd in csvreader:
vv = [rd.pop(h, '') for h in self._colnames]
self.additem(vv[0], vv[1], vv[2], vv[3])
self.list_modified = False