最近在用GUI编写一个文件下载程序,其中遇见不少问题。有些问题已经解决,但有很多问题,暂时无法解决,先记录下来,等有解决方案了再更新。
1.Thinker中label文字的对齐方式?
tk.Label(labelframe, text="m3u8_url:", width=8, anchor=E, padx=10, pady=5).grid(column=0, row=0)
anchor 方位属性可以设置文字或者图片在label中放置的位置:e, s, w, n, center, es, en,ws, sn 而justify 用于当label总出行多行文字时,可选参数为: LEFT、 CENTER、RIGHT
2.在Grid布局中如何合并几个单元?
grid中提供columnspan和rowspan可以让插件跨行或列显示;
3.Grid布局中是否能够显示出可见网格,方便布置?
目前还没有找到方案
4.创建一个文本复选框
tk中没有复选框,需要导入from tkinter import ttk.其中需要注意一个问题,就是复选框的布局 如果以下列方式进行赋值,会报TypeError: ‘NoneType’ object does not support item assignment错误。
video_type = ttk.Combobox(labelframe, textvariable=type).grid(row=4, column=5)
video_type['value'] = ('mp4', 'avi')
解决方案: 需要先定义插件,然后在布局,在赋值,这样就不会存在问题。
video_type = ttk.Combobox(labelframe, textvariable=type)
video_type.grid(row=4, column=5)
video_type['value'] = ('mp4', 'avi')
5.ttk.Scrollbar样式调整问题
初始的滚动条比较短小,且在ttk中滚动条和原来经典类型不太一样。 解决方案: 下滚动条设置中让其填满布局格sticky=tk.N + tk.S + tk.E + tk.W 通过
style = ttk.Style()
style.theme_use('classic')
可以改变滚动条样式, 但具体有多少可以样式需要查一下。
content_listbox = Listbox(labelframe, height=20)
content_listbox.grid(row=1, column=8, rowspan=8, columnspan=4, sticky=E)
style = ttk.Style()
# style.theme_use('classic') # 可以改变滚动条样式
# style.configure("Vertical.TScrollbar", background="green", bordercolor="red", arrowcolor="white")
scroll = ttk.Scrollbar(labelframe, orient=VERTICAL, command=content_listbox.yview)
scroll.grid(row=1, column=12, rowspan=8, sticky=N + S + W)
content_listbox['yscrollcommand'] = scroll.set
6. 各类Entry框或者输入框的初始值设定,文字淡显示。
现在字体都是黑色的,而不是灰色的 在网上找的一段如下代码可以解决该问题:
class EntryWithPlaceholder(tk.Entry):
def __init__(self, master=None, placeholder="PLACEHOLDER", color='grey'):
super().__init__(master)
self.placeholder = placeholder
self.placeholder_color = color
self.default_fg_color = self['fg']
self.bind("<FocusIn>", self.foc_in)
self.bind("<FocusOut>", self.foc_out)
self.put_placeholder()
def put_placeholder(self):
self.insert(0, self.placeholder)
self['fg'] = self.placeholder_color
def foc_in(self, *args):
if self['fg'] == self.placeholder_color:
self.delete('0', 'end')
self['fg'] = self.default_fg_color
def foc_out(self, *args):
if not self.get():
self.put_placeholder()
我修改成函数,用在类中。
@staticmethod
def entry_default(entry, initial, color='grey'):
def put_placeholder():
entry.insert(0, initial)
entry['fg'] = color
def foc_in(event):
if entry['fg'] == color:
entry.delete('0', 'end')
print("***********")
entry['fg'] = default_fg_color
def foc_out(event):
if not entry.get():
print(entry.get())
print("++++++")
put_placeholder()
default_fg_color = entry['fg']
# print(default_fg_color)
put_placeholder()
entry.bind("<FocusIn>", foc_in)
entry.bind("<FocusOut>", foc_out)
m3u8_url = Entry(labelframe, textvariable=self.m3u8, width=68)
self.entry_default(m3u8_url, "请输入m3u8文件地址")
m3u8_url.grid(row=1, column=3, columnspan=9, sticky=W)
注意: 1.在tk.Entry中一定是要在布局前去调用更改函数,grid的后就没有bind方法了。 2.鼠标监控函数,需要传一个参数even,不然方法无效; 3.在类方法中,需要self.entry_default才能调用;
7. Entry框中有初始值的情况下,怎么让entry框输入后就进行验证?
目前还没有解决,等提交后,根据初始值进行判断,满足条件在执行主函数;
8. Tkinter当有判断要求重新输入,怎么让函数保留正确值得情况下,刷新需要填写的Entry框?
9.导入自己写的函数时,只能导入main函数,不能用if name = ‘main’:不然GUI会跳过此步奏。
10. 如何实现Listbox自动更新
此问题描述比较复杂,但最终想实现的功能就是listbox能自动更新; 首先:app.py中
Class A()
# 此函数建立了一个listbox
def set_init_window(self, image_path):
self.content_listbox = Listbox(labelframe, height=20)
# self.get_filenames(content_listbox)
self.content_listbox.grid(row=1, column=8, rowspan=8, columnspan=4, sticky=E)
# 此函数是鼠标点击后执行函数,该函数调用另一个模块的主函数
def content_analysis(self):
moveloads.main(data_sets[0], data_sets[1], data_sets[2], data_sets[3],data_sets[4], data_sets[5], data_sets[6])
# 滚动显示文件内容
def get_filenames(self, dirpath):
if len(dirpath) != 0:
self.filenames = os.listdir(dirpath)
for filename in self.filenames:
self.content_listbox.insert(END, filename) # 这个content_listbox
root.after(1000, self.get_filenames)
if __name__ == '__main__':
root = tk.Tk()
windows = Downloads(root)
root.mainloop()
另一个函数moveloads.py的主函数
def main(m3u8_url, key_url, save_path):
save_path = save_path.replace("/", "\\")
save_video_path = save_path + "\\" + video_name # 文件夹地址
if not os.path.exists(save_video_path): # 判读文件夹是否存在,不存在创建
os.makedirs(save_video_path)
****************************************
# app.Downloads.get_filenames()
****************************************
本想在*号中启动本函数,然后通过after函数调取自身。 问题: 1.app.Downloads.get_filenames()调取时,需要先实例化Class A,然后才能传入dirpath参数,不然会显示self参数没传入。但ClassA,我已经实例化了一个对象了,不能在实例化了。但如果直接调实例化后的windows
app.windows.get_filenames(save_video_path)
会报module ‘app’ has no attribute ‘windows’,意思是在另一个模块调用类实例化对象,还是需要在实例化。但这样就会重新刷新root了,肯定是不行的。
最后的暂行的笨方案: 将main函数的路径处理提前到app.py模块中
Class A()
# 此函数建立了一个listbox
def set_init_window(self, image_path):
self.content_listbox = Listbox(labelframe, height=20)
# self.get_filenames(content_listbox)
self.content_listbox.grid(row=1, column=8, rowspan=8, columnspan=4, sticky=E)
# 此函数是鼠标点击后执行函数,该函数调用另一个模块的主函数
def content_analysis(self):
******************************************
save_path = data_sets[2].replace("/", "\\")
save_video_path = save_path + "\\" + data_sets[3] # 文件夹地址
if not os.path.exists(save_video_path): # 判读文件夹是否存在,不存在创建
os.makedirs(save_video_path)
self.get_filenamaes(save_video_path)
*******************************************
当鼠标点击按钮后就先合成需要的路径,然后在执插入内容,根据自身不断调用自己,最后在去另一个模块只想main函数
moveloads.main(data_sets[0], data_sets[1], data_sets[2], data_sets[3],data_sets[4], data_sets[5], data_sets[6])
# 滚动显示文件内容
def get_filenames(self, dirpath):
if len(dirpath) != 0:
self.filenames = os.listdir(dirpath)
for filename in self.filenames:
self.content_listbox.insert(END, filename) # 这个content_listbox
root.after(1000, self.get_filenames)
if __name__ == '__main__':
root = tk.Tk()
windows = Downloads(root)
root.mainloop()
另一个函数moveloads.py的主函数
def main(m3u8_url, key_url, save_path):
****************************************
# app.Downloads.get_filenames()
****************************************
但无法调app模块中类实例化对象,这也导致后面下载进度更新成为难点。
11.另外一个模块的下载进度条如何更新。
App中class类的进度条程序,主要是记录总的下载量,然后查询本地下载量,最后计算进度。
def progress(self, total_length):
# 填充进度条
fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="green")
# total_length = glv.get('total_length')
loaded_length = [ts_file for ts_file in self.filenames if ts_file.endswith('ts')]
print(len(loaded_length), total_length)
for i in range(total_length):
n = len(loaded_length) / total_length * 460
self.canvas.coords(fill_line, (0, 0, n, 23))
root.update()
time.sleep(0.02) # 控制进度条流动的速度
此代码应该是在下载函数开始前启动,直到达到100%后停止更新。但根据第10条问题,这个在class类中的方法,无法在另一个模块中导入,所以暂时无法解决。不可能向第10条问题一样,把所有代码拷贝到类下,然后启动progress方法。
目前设计的GUI界面 还需完成功能: 1.背景图片,点击就能更换; 2.点击listbox中内容,就能直接打开; 3.测试其他网址的m3u8; 4.这个目前没有头绪的,如何从网址中直接分析出m3u8文件,这样就不用去找m3u8地址; 5.更多视频网站下载,广泛性。
|