带滚动条的窗口
由于滚动条不能绑定窗口或框架,只能在窗口下创建frame,在frame下创建canvas,滚动条绑定canvas
- 创建窗口,固定其尺寸,不允许修改
- 创建画布 - 创建滚动条 - 画布和滚动条放在同一个parent下
- 创建frame,其尺寸占画布宽、高的90%
- 结合窗口、画布、frame,创建带滚动条的窗口
- 为滚动条设置滚动范围(canvas绑定滚动条要设置scrollregion才能生效)
from tkinter import *
def create_win(title=None, size_str=None):
win = Tk()
win.title(title)
win.geometry(size_str)
win.resizable(0,0)
return win
def create_canvas(parent):
canvas = Canvas(parent)
canvas.pack(fill=Y, side=LEFT)
bar = Scrollbar(canvas)
bar.place(relx=0.95, relheight=1.0)
bar.configure(command=canvas.yview)
canvas.config(yscrollcommand=bar.set)
return canvas
def create_frame(canvas):
w = canvas.winfo_reqwidth()
h = canvas.winfo_reqheight()
frame = Frame(canvas, width=w*0.9, height=h*0.9)
return frame, w, h
def win_with_bar(title=None, size_str=None):
win = create_win(title=title, size_str=size_str) #创建窗口
canvas = create_canvas(win) #创建与滚动条绑定的canvas
frame, w, h = create_frame(canvas) #创建frame
#把frame放在canvas的中心位置,受滚动条影响,中心点横坐标略有偏移
canvas.create_window((w/2-10, h/2), window=frame)
return win, canvas, frame, h
def set_scrollregion(canvas, frame, h):
L = [i.winfo_reqheight() for i in frame.winfo_children()] #组件高度累加
frame_h = sum(L) + 10 #部件总高度,加上边距10,作为frame的高度
y1 = (h - frame_h)/2 #滚动范围的开始位置:(画布高度 - frame高度) / 2
y2 = y1 + frame_h #滚动范围的结束位置:开始位置 + frame高度
canvas.config(scrollregion=(0, y1, 0, y2))
'''
#应用程序:创建窗口、画布、frame——在frame中创建部件——根据部件高度,设置滚动范围
win, canvas, frame, h = win_with_bar(title='标题', size_str=None)
for i in range(50): Label(frame, text='占位标签'+str(i)).pack()
set_scrollregion(canvas, frame, h)
'''
常用窗口:标签 + 滚动文本框 + 按钮
- 此函数不带参数
- 仅在函数内部导入模块
- 窗口、框架、标签、文本框、按钮都有名称,并全部返回
- 后期可以调用所有组件,也可以调用部分组件
def label_text_button_win():
import tkinter
win = tkinter.Tk()
win.title("标题")
frame = tkinter.Frame(win)
frame.pack(padx=20, pady=20)
label = tkinter.Label(frame, text="文本行", anchor='sw')
label.pack(fill='x')
from tkinter import scrolledtext
text = scrolledtext.ScrolledText(frame, width=30, height=15)
text.pack(pady=10)
button = tkinter.Button(frame, text='确认', width=12)
button.pack()
return win, frame, label, text, button
button = label_text_button_win()[-1]
常用Frame:标题行 + 多列组件
- 需要指定标题、组件类型、列宽、行数
- 标题外的组件全部放在字典D里,由行号和列号组成key,以组件为value
- 返回frame和批量组件的字典,方便后期调用
- 注意:由于标题行的存在,部件的grid位置 = 组件名中的行号 + 1
- 局限:行数和列数都不能超过9。如需更多行数,组件命名时,用分隔符分离行号、列号
def frame_table(parent, labels, commands, widths, row=1):
frame = Frame(win)
frame.pack(padx=20, pady=20)
D = {}
for c in range(len(labels)):
Label(frame, text=labels[c]).grid(row=0, column=c, padx=1, pady=1)
for r in range(row):
name = f"{r}{c}"
D[name] = commands[c](frame, width=widths[c])
D[name].grid(row=1+r, column=c, pady=1, padx=1)
return frame, D
labels = ["标题1", "标题2", "标题3"]
comms = [Entry, Button, Button]
widths = [15, 15, 15]
frame, D = frame_table(parent, labels, commands, widths, row=1)
借字典提取两列表差异项
- 【for i in L1: if i not in L2】效率较慢,列表较长时,用字典中转可提高效率
- 流程:以L1中的元素为key创建字典 - 按L2中的元素改写字典 - 只保留字典中未改写的key
def for_not_in(L1, L2):
D = {i:0 for i in L1}
for i in L2: D[i] = 1
L = [i for i in D if D[i] == 0]
return L
常用Frame:treeview
Treeview + 滚动条 + 适应列宽 + 排序
- 参数列表:父组件、标题列表、数据列表、高度
- 创建frame,【expand=True】使fill生效,【fill=BOTH】
- 创建基本的tree,不显示第一列(空列),按列名设置标题、居中,放入数据
- 创建滚动条 - 绑定组件与滚动条 - 放置滚动条
- 放置tree
- 设置列宽与内容相关联
- tups重组,获取每列内容的最大长度。各内容求长度前先转str以兼容日期、数字
- 字符长度*10或250中取最小值,避免单列内容过宽
- 添加排序功能
def frame_tree(parent, titles, tups, height):
f = Frame(parent)
f.pack(expand=True, fill=BOTH, padx=10, pady=10)
tree = ttk.Treeview(f, show="headings", columns=titles, height=height)
for title in titles: tree.heading(title, text=title)
for title in titles: tree.column(title, anchor=CENTER)
for tup in tups: tree.insert('', END, value=tup)
bar = Scrollbar(parent, command=widget.yview)
tree.config(yscrollcommand=bar.set)
bar.pack(side='right', fill=Y)
tree.pack(expand=True, fill=BOTH)
lens = [max([len(str(i)) for i in tup]) for tup in zip_list(tups)]
for i in range(len(lens)):
tree.column(titles[i], width=min(lens[i]*10, 250))
tree_sort(tree, titles, tups)
return tree
tree标题绑定排序功能
tree排序 -源数据
- 避免遇到无法排序的大元组,统一把数据转化为大列表格式。
- 定义函数sort_,给tree的一个标题添加排序功能。参数:tree,title,数据,reverse值
- 获取标题位置p
- 原始数据按指定位置p排序,reverse值为【r】
- tree表清空旧数据 - 放入新数据
- 标题绑定sort_命令,reverse值为【not r】——保证排序在正序、倒序之间切换
- 把sort_函数应用到所有标题。reverse初始值为False,后续在False和not False之间切换
def tree_sort(tree, titles, tups):
tups = [list(tup) for tup in tups]
def sort_(tree, title, tups, r):
p = titles.index(title)
tups.sort(key=lambda tup:tup[p], reverse=r)
tree.delete(*tree.get_children())
for tup in tups: tree.insert('', END, value=tup)
command = lambda:sort_(tree, title, tups, not r)
tree.heading(title, command=command)
for title in titles:
command = lambda t=title: sort_(tree, t, tups, False)
tree.heading(title, command=command)
tree排序 -tree数据
直接从tree中获取tups - tups重新排序 - 更新tree。
优点:无需tups参数,无需考虑元组问题
缺点:从tree中提取的数据都是文本格式
def tree_sort(tree, titles):
def sort_(tree, title, r):
p = titles.index(title)
tups = [tree.item(i,"values") for i in tree.get_children()]
tups.sort(key=lambda tup:tup[p], reverse=r)
tree.delete(*tree.get_children())
for tup in tups: tree.insert('', END, value=tup)
tree.heading(title, command=lambda:sort_(tree, title, not r))
for title in titles:
tree.heading(title, command=lambda t=title: sort_(tree, t, False))
复制tree内容
- 获取行号i,根据行号获得整行内容
- 用分隔符连接各元素,用换行符连接各行,最后加上标题
- 用pyperclip(只能复制文本),把内容复制到剪贴板,自行粘贴到Excel即可
def copy_all():
full = [tree.item(i,"values") for i in tree.get_children()]
full = ['\t'.join(tup) for tup in full]
full = '\n'.join(full)
import pyperclip
pyperclip.copy(full)
工作簿转字典
- 获取工作簿 - 遍历工作表 - 按行获取表中数据。
- 以工作表名称为key,以表中数据为value,储存在字典D中
def get_datas(file):
from openpyxl import load_workbook
D = {}
book = load_workbook(file)
for table in book.sheetnames:
sheet = [[cell.value for cell in row] for row in book[table]]
D[table] = [tuple(L) for L in sheet]
return D
字典转SQLITE数据表
- 连接数据库
- 逐个工作表处理
- 拆分工作表中的标题和具体数据
- 以工作表名为数据表名,以标题为字段,创建数据表
- 构建sql语句,其参数字段由若干个问号组成,问号的数量 = 标题的数量
- 批量录入数据
- 提交
def create_sql_database(D):
conn, cur = connect()
for table in D:
titles, *tups = D[table]
cur.execute(f"create table {table}{titles}")
value = ["?" for i in tups[0]]
value = ",".join(value)
sql = f'insert into {table} values ({value});'
cur.executemany(sql, tups)
conn.commit()
cur.close()
解码邮件单行内容
- 导入库
- 创建一个空字符串,用于存放结果
- 从原内容中拆分出具体内容和编码方式[(content1, code1), (content2, code2), …]
- 用for循环对逐个元组处理
- 内容有可能是str或bytes格式,仅bytes格式需要处理
- 编码方式为None,用默认方式解码
- 编码方式非None,用给出的编码方式解码
- 内容有可能是str或bytes格式,仅bytes格式需要处理
from email import header
def deal_line(line):
new_line = ""
L = header.decode_header(line)
for content, code in L:
if type(content) is bytes:
if code is None: content = content.decode()
else: content = content.decode(code)
new_line = new_line + (content)
return new_line
下载邮件
完美解析邮件各部件是非常困难的事情,下载字节串保存,使用时用foxmail等软件读取即可。(备注:conn为POP3连接)
conn_uids = [i.decode().split(' ') for i in conn.uidl()[1]] #大列表:['1', 'POPSPAM_20201125']
D = {L[1]:L[0] for L in conn_uids} #创建字典:{uid:num}
for uid in D:
msg = conn.retr(D[uid])[1]
filename= open(f'{uid}.eml', 'wb').write(b'\r\n'.join(msg))
按行获取文本框内容
- 获取所有内容,按换行符切割,得到内容列表
- 只保留长度大于0的行,并且去除行首、行尾的空白字符,以列表形式返回
lines = text.get(0.0, END).split('\n')
lines = [i.strip() for i in lines if len(i)>0]
None转空字符串
先把内容统一成字符串格式,然后用替换改变字符串的内容
L = [[str(j).replace('None', '') for j in i] for i in L]
子列表重新组合
条件:所有子列表的长度一致
步骤:
- 以原始大列表中,第一个子列表的长度为step
- 大列表展开
- 按步长提取元素组成新的大列表
- 返回新的大列表
- 重组2次可以得到原始大列表
def zip_list(L):
step = len(L[0])
L = [j for i in L for j in i]
L = [L[i:len(L):step] for i in range(step)]
return L
SQL语句
构建where条件下,in 后面的列表,对格式有几个要求:
- 必需是圆括号,不能是中括号,括号里面的内容必须要用引号——不宜用join
- 括号里一定要有内容——列表为空时,用空字符串占位
- 最后一个内容后面没有逗号——列表长度为1时,不能直接转元组
def in_clause(L):
if len(L) == 0: clause = "('')"
elif len(L) == 1: clause = f"('{L[0]}')"
else: clause = tuple(set(L))
return clause
selenium 显式等待
- 引入等待模块、条件模块
- 等待时间为30秒,每0.5秒检查一次条件是否满足
- 条件:以指定方式定位指定元素。方式和元素由tup指定,如:(By.ID, “multiSearch”)
- 获取元素并返回
def my_wait(driver, name, value):
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 30, 0.5)
condition = (name, value)
condition = EC.presence_of_element_located(condition)
element = wait.until(condition)
return element
- 使用示例。By的可选项有:CLASS_NAME,CSS_SELECTOR,ID,LINK_TEXT,NAME,PARTIAL_LINK_TEXT,TAG_NAME,XPATH
from selenium.webdriver.common.by import By
my_wait(driver, By.ID, "multiSearch")
Selenium伪装
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
本文介绍了Python中创建带滚动条的窗口及其组件的使用,包括常用窗口、Frame和treeview的实现。同时,文章还探讨了列表差异比较、treeview排序、邮件内容解码以及Selenium的显式等待和网页元素操作。这些内容对于Python GUI编程和自动化测试具有实用价值。
1578

被折叠的 条评论
为什么被折叠?



