(1)Python所有方向的学习路线(新版)
这是我花了几天的时间去把Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
最近我才对这些路线做了一下新的更新,知识体系更全面了。
(2)Python学习视频
包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。
(3)100多个练手项目
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
self.msg['text']='重新打开或保存即可生效'
self.msg.after(2500,clear)
def clear():self.msg['text']=''
coding.bind('<<ComboboxSelected>>',tip)
coding["value"]=self.encodings
coding.pack(side=LEFT)
self.msg=Label(frame)
self.msg.pack(side=LEFT)
self.contents=ScrolledText(self,undo=True, width=75, height=24,
font = (self.NORMAL_FONT,self.NORMAL_FONTSIZE,"normal"),
wrap=self.AUTOWRAP, bg=self.TEXT_BG,fg=self.TEXT_FG)
self.contents.pack(expand=True,fill=BOTH)
self.contents.bind("<Key>",self.text_change)
self.contents.bind("<B1-ButtonRelease>",self.update_status)
order = self.contents.bindtags() # 修复无法获取选定的文本的bug
self.contents.bindtags((order[1], order[0])+order[2:])
self.update_offset()
self.create_menu()
def create\_menu(self):
menu=Menu(self)
filemenu=Menu(self,tearoff=False)
filemenu.add_command(label="新建",
command=self.new,accelerator="Ctrl+N")
filemenu.add_command(label="新建二进制文件",command=self.new_binary)
filemenu.add_command(label="打开",
command=self.open,accelerator="Ctrl+O")
filemenu.add_command(label="打开二进制文件",command=self.open_as_binary)
filemenu.add_command(label="保存",
command=self.save,accelerator="Ctrl+S")
filemenu.add_command(label="另存为",command=self.save_as)
filemenu.add_separator()
filemenu.add_command(label="退出",command=self.ask_for_save)
self.editmenu=Menu(self.contents,tearoff=False)
master = self.contents
self.editmenu.add_command(label="剪切 ",
command=lambda:self.text_change()\
==master.event_generate("<<Cut>>"))
self.editmenu.add_command(label="复制 ",
command=lambda:master.event_generate("<<Copy>>"))
self.editmenu.add_command(label="粘贴 ",
command=lambda:self.text_change()\
==master.event_generate("<<Paste>>"))
self.editmenu.add_separator()
self.editmenu.add_command(label="查找",accelerator="Ctrl+F",
command=lambda:self.show_dialog(SearchDialog))
self.editmenu.add_command(label="查找下一个",accelerator="F3",
command=self.findnext)
self.editmenu.add_command(label="替换",accelerator="Ctrl+H",
command=lambda:self.show_dialog(ReplaceDialog))
self.editmenu.add_separator()
self.editmenu.add_command(label="插入十六进制数据",state=DISABLED,
command=self.insert_hex)
view=Menu(self.contents,tearoff=False)
self.is_autowrap=IntVar(self.contents) # 是否自动换行
self.is_autowrap.set(1 if self.AUTOWRAP!=NONE else 0)
view.add_checkbutton(label="自动换行", command=self.set_wrap,
variable=self.is_autowrap)
fontsize=Menu(self.contents,tearoff=False)
fontsize.add_command(label="选择字体",
command=self.choose_font)
fontsize.add_separator()
fontsize.add_command(label="增大字体 ",accelerator='Ctrl+ "+"',
command=self.increase_font)
fontsize.add_command(label="减小字体 ",accelerator='Ctrl+ "-"',
command=self.decrease_font)
fontsize.add_separator()
for i in range(len(self.FONTSIZES)):
def resize(index=i):
self.set_fontsize(index)
fontsize.add_command(label=self.FONTSIZES[i],command=resize)
self.contents.bind("<Button-3>",
lambda event:self.editmenu.post(event.x_root,event.y_root))
view.add_cascade(label="字体",menu=fontsize)
theme_menu=Menu(self,tearoff=False)
theme_menu.add_command(label="选择前景色",command=self.select_fg)
theme_menu.add_command(label="选择背景色",command=self.select_bg)
theme_menu.add_command(label="重置",command=self.reset_theme)
view.add_cascade(label="主题",menu=theme_menu)
self._show_status=IntVar(self)
self._show_status.set(1 if self.SHOW_STATUS else 0)
view.add_checkbutton(label="显示状态栏",command=self.show_statusbar,
variable=self._show_status)
helpmenu=Menu(self,tearoff=False)
helpmenu.add_command(label="关于",command=self.about)
helpmenu.add_command(label="反馈",command=self.feedback)
menu.add_cascade(label="文件",menu=filemenu)
menu.add_cascade(label="编辑",menu=self.editmenu)
menu.add_cascade(label="查看",menu=view)
menu.add_cascade(label="帮助",menu=helpmenu)
# 创建弹出在self.txt\_decoded和self.hexdata的菜单
popup1=Menu(self.txt_decoded,tearoff=False)
def \_cut():
self.txt_decoded.event_generate("<<Cut>>")
self._edit_decoded_event()
def \_paste():
self.txt_decoded.event_generate("<<Paste>>")
self._edit_decoded_event()
popup1.add_command(label="剪切",command=_cut)
popup1.add_command(
label="复制",command=lambda:self.txt_decoded.event_generate("<<Copy>>"))
popup1.add_command(label="粘贴",command=_paste)
popup2=Menu(self.hexdata,tearoff=False)
popup2.add_command(
label="复制",command=lambda:self.hexdata.event_generate("<<Copy>>"))
self.txt_decoded.bind("<Button-3>",
lambda event:popup1.post(event.x_root,event.y_root))
self.txt_decoded.bind("<Key>",self._edit_decoded_event)
self.hexdata.bind("<Button-3>",
lambda event:popup2.post(event.x_root,event.y_root))
# 显示菜单
self.config(menu=menu)
def create\_binarytools(self): # 用于二进制文件
if self.isbinary: # self.txt\_decoded 用于显示解码的转义字符
self.txt_decoded.pack(side=LEFT,expand=True,fill=BOTH)
self.hexdata.pack(fill=Y) # hexdata 用于显示转义字符的十六进制
self.status.pack_forget()
self.status.pack(fill=X)
self.editmenu.entryconfig(8,state=NORMAL)
else: # 隐藏工具
if self.txt_decoded:
self.txt_decoded.pack_forget()
if self.hexdata:
self.hexdata.pack_forget()
self.status.pack(side=RIGHT)
self.editmenu.entryconfig(8,state=DISABLED) # 禁止插入
以下是部分控件事件的处理, 包含了实现快捷键、改变字体和显示/隐藏状态栏功能:
def \_get\_fontname(self):
font=' '.join(self.contents["font"].split(' ')[:-2])
# tkinter会将带空格的字体名称用{}括起来
if '{' in font:
font = font[1:-1]
return font
def set\_fontsize(self,index):
newsize=self.FONTSIZES[index]
fontname = self._get_fontname()
self.contents["font"]=(fontname,newsize,"normal")
def choose\_font(self):
def ok():
self.contents["font"]=[opt.get()] + \
self.contents["font"].split(' ')[-2:] # 保留原先大小、样式
dialog.destroy()
dialog = Toplevel(self)
dialog.title('选择字体')
dialog.resizable(False,False)
dialog.attributes('-toolwindow',True)
opt = ttk.Combobox(dialog)
# tkinter.font.families() 获取所有字体名称, 注意root参数
opt['values']=sorted(font.families(root=self))
opt.grid(row=0,column=0,columnspan=2,padx=15,pady=20)
ttk.Button(dialog,text='确定',command=ok).grid(row=1,column=0)
ttk.Button(dialog,text='取消',command=dialog.destroy).grid(row=1,column=1)
oldfont = self._get_fontname()
opt.set(oldfont)
dialog.grab_set() # 对话框打开时, 不允许用户操作主窗口
dialog.focus_force()
def increase\_font(self):
# 增大字体
fontsize=int(self.contents["font"].split(' ')[1])
index=self.FONTSIZES.index(fontsize)+1
if 0<=index<len(self.FONTSIZES): self.set_fontsize(index)
def decrease\_font(self):
# 减小字体
fontsize=int(self.contents["font"].split(' ')[1])
index=self.FONTSIZES.index(fontsize)-1
if 0<=index<len(self.FONTSIZES): self.set_fontsize(index)
def set\_wrap(self):
if self.is_autowrap.get():
self.contents['wrap'] = CHAR
else:
self.contents['wrap'] = NONE
# 注意:由于tkinter会自动设置菜单复选框的变量, 所以不需要此行代码
self.is_autowrap.set(int(not self.is_autowrap.get()))
def show\_statusbar(self):
if self._show_status.get():
if self.isbinary:
self.statusbar.pack(side=BOTTOM,fill=X)
else:
self.statusbar.pack(side=BOTTOM,fill=X)
else:
self.statusbar.pack_forget()
def window\_onkey(self,event):
# 实现快捷键的部分
# 如果按下Ctrl键
if event.state in (4,6,12,14,36,38,44,46): # 适应多种按键情况(Num,Caps,Scroll)
key=event.keysym.lower()
if key=='o':#按下Ctrl+O键
self.open()
elif key=='s':#Ctrl+S键
self.save()
elif key=='n':
self.new()
elif key=='f':
self.show_dialog(SearchDialog)
elif key=='h':
self.show_dialog(ReplaceDialog)
elif key=='equal':#Ctrl+ "+" 增大字体
self.increase_font()
elif key=='minus':#Ctrl+ "-" 减小字体
self.decrease_font()
elif event.keysym.lower()=='f3':
self.findnext()
elif event.keycode == 93: # 按下了菜单键
self.editmenu.post(self.winfo_x()+self.winfo_width(),
self.winfo_y()+self.winfo_height())
def about(self):
msgbox.showinfo("关于","版本: %s\n作者: %s"%(__version__, __author__), parent=self)
## 2.文本打开, 保存
打开文件有两种方法, 一种是在当前窗口中打开, 第二种是新建一个`Editor`实例, 在新窗口中打开。这里在新窗口中打开文件。
`ask_for_save()`的部分有一些复杂, 需要多次判断, 如判断用户是否取消操作、询问打开文件前是否需要保存等。
如: 用户在"是否保存"中选择了"是", 但在输入文件名的对话框中点击了取消, 应该如何处理?具体可看注释。
关于使用`chardet`库自动检测编码:
`chardet.detect()`函数返回一个字典, 包含检测结果。其中`encoding`键即为检测到的编码。
在之前的**Editor类中**加入以下代码:
def new(self): # 新建一个Editor实例
try:self.saveconfig() # 保存配置,使新的窗口加载修改后的配置
except OSError:pass # 忽略写入文件可能产生的异常
window=Editor()
window.focus_force()
return window
def new\_binary(self): # 创建新二进制文件
try:self.saveconfig()
except OSError:pass
window=Editor()
window.isbinary=True
window.create_binarytools()
window.change_title()
window.change_mode()
window.contents.edit_reset()
window.focus_force()
return window
def open(self):
#加载一个文件
filename=filediag.askopenfilename(master=self,title='打开',
initialdir=os.path.split(self.filename)[0],
filetypes=self.FILETYPES)
if not filename:return
if not self.filename and not self.file_modified: # 如果是刚新建的, 在当前窗口中打开
self.load(filename)
else:self.new().load(filename)
def open\_as\_binary(self):
filename=filediag.askopenfilename(master=self,title='打开二进制文件',
initialdir=os.path.split(self.filename)[0],
filetypes=self.FILETYPES)
if not filename:return
if not self.filename and not self.file_modified: # 如果是刚新建的
self.load(filename,binary=True)
else:self.new().load(filename,binary=True)
def load(self,filename,binary=False):
# 加载文件
self.isbinary=binary
try:
data=self._load_data(filename)
if data==0:return
self.filename=filename
self.contents.delete('1.0', END)
if self.isbinary:
self.contents.insert(INSERT,data)
else:
for char in data:
try:
self.contents.insert(INSERT,char)
except TclError:self.contents.insert(INSERT,' ')
self.contents.mark_set(INSERT,"1.0")
self.create_binarytools()
self.file_modified=False
self.change_title()
self.change_mode()
self.contents.edit_reset() # 重置文本框的撤销功能
self.contents.focus_force()
except Exception as err:handle(err,parent=self)
def \_load\_data(self,filename):
# 从文件加载数据
f=open(filename,"rb")
if self.isbinary:
data=to_escape_str(f.read())
return data
else:
try:
#读取文件,并对文件内容进行编码
raw=f.read()
if self.coding.get()=="自动":
# 调用chardet库
encoding=chardet.detect(raw[:100000])['encoding']
if encoding is None:
encoding='utf-8'
self.coding.set(encoding)
data=str(raw,encoding=self.coding.get())
except UnicodeDecodeError:
f.seek(0)
result=msgbox.askyesnocancel("PyNotepad","""%s编码无法解码此文件,
是否使用二进制模式打开?“”“%self.coding.get(),parent=self)
if result:
self.isbinary=True
data=to_escape_str(f.read())
elif result is not None:
self.isbinary=False
data=str(f.read(),encoding=self.coding.get(),errors=“replace”)
else:
return 0 # 表示取消
return data
def ask_for_save(self,quit=True):
my_ret=None
if self.file_modified:
retval=msgbox.askyesnocancel(“文件尚未保存”,
“是否保存{}的更改?”.format(
os.path.split(self.filename)[1] or “当前文件”)
,parent=self)
# retval 为 None表示取消, False为否, True为是
if not retval is None:
if retvalTrue:
# 是
ret=self.save()
# 在保存对话框中取消
if ret0:
my_ret=0;quit=False
# 否
else:
# 取消
my_ret=0;quit=False # 0表示cancel
if quit:
Editor.windows.remove(self)
try:self.saveconfig() # 保存配置, 见附
except OSError:pass
self.destroy() # tkinter不会自动关闭窗口, 需调用函数手动关闭
return my_ret
def save(self):
#保存文件
if not self.filename:
self.filename=filediag.asksaveasfilename(master=self,
initialdir=os.path.split(self.filename)[0],
filetypes=self.FILETYPES)
filename=self.filename
if filename.strip():
text=self.contents.get(‘1.0’, END)[:-1] # [:-1]: 去除末尾换行符
if self.isbinary:
data=to_bytes(text)
else:
data=bytes(text,encoding=self.coding.get(),errors=‘replace’)
# Text文本框的bug:避免多余的\r换行符
# 如:输入文字foobar, data中变成\rfoobar
# -感谢文章末尾用户评论的反馈-
data=data.replace(b’\r’,b’')
with open(filename, ‘wb’) as f:
f.write(data)
self.filename=filename
self.file_modified=False
self.change_title()
self.change_mode()
else:
return 0 # 0表示cancel
def save_as(self):
filename=filediag.asksaveasfilename(master=self,
initialdir=os.path.split(self.filename)[0],
filetypes=self.FILETYPES)
if filename: # 如果未选择取消
self.filename=filename
self.save()
def change_title(self):
file = os.path.split(self.filename)[1] or “未命名”
newtitle=“PyNotepad - “+ file +
(” (二进制模式)” if self.isbinary else ‘’)
if self.file_modified:
newtitle=”*%s*"%newtitle
self.title(newtitle)
## 3.文本编辑
其中, `text_change()`在文本被修改时调用, `update_status()`和`update_offset()`用于更新状态栏中的数据。
在二进制模式中, update\_status获取用户选择的文本, 更新解码的数据和十六进制值。update\_offset更新偏移量。在文本模式中, update\_offset更新当前的行数和列数。
在ScrolledText控件中,
获取选择的文本: `text.get(SEL_FIRST,SEL_LAST)`
获取当前光标位置: `text.index(INSERT)`
在之前的**Editor类中**加入以下代码:
def text\_change(self,event=None):
self.file_modified=True
self.update_status();self.change_title()
def update\_status(self,event=None):
if not self._show_status.get():return
if self.isbinary:
# 用于二进制文件
try:
selected=self.contents.get(SEL_FIRST,SEL_LAST) # 获取从开头到光标处的文本
raw=to_bytes(selected)
coding=self.coding.get()
# 调用chardet库
if coding=="自动":
coding=chardet.detect(raw[:100000])['encoding']
if coding is None:coding='utf-8'
try:text=str(raw,encoding=coding,
errors="backslashreplace")
except TypeError:
# 修复Python 3.4中的bug: don't know how to handle
# UnicodeDecodeError in error callback
text=str(raw,encoding=coding,
errors="replace")
except LookupError as err: # 未知编码
handle(err,parent=self);return
self.txt_decoded.delete("1.0",END)
self.txt_decoded.insert(INSERT,text)
self.hexdata.delete("1.0",END)
self.hexdata.insert(INSERT,view_hex(raw))
self.status["text"]="选区长度: %d (Bytes)"%len(raw)
except (TclError,SyntaxError): #忽略未选取内容, 或格式不正确
self.txt_decoded.delete("1.0",END)
self.hexdata.delete("1.0",END)
self.update_offset()
else:self.update_offset()
def update\_offset(self,event=None):
if self.isbinary:
prev=self.contents.get("1.0",INSERT) # 获取从开头到光标处的文本
try:
data=to_bytes(prev)
except SyntaxError:
sep='\\'
prev=sep.join(prev.split(sep)[0:-1])
try:data=to_bytes(prev)
except SyntaxError:data=None
if data is not None:
self.status["text"]="偏移量: {} ({})"\
.format(len(data),hex(len(data)))
else:
offset=self.contents.index(INSERT).split('.') # 不能用CURRENT
self.status["text"]="Ln: {} Col: {}".format(\*offset)
## 4.编辑二进制文件
在Python中, bytes数据可用转义序列形式表示, 如\x00\x01\x02\n属于转义序列,
可通过`repr(bytes)[2:-1]`,`eval('b"""'+str+'"""')` 实现转义序列与bytes类型的转换。
这次, 在**Editor类外部**加入以下代码:
def to_escape_str(byte):
# 将字节(bytes)转换为转义字符串
str=‘’;length=1024
for i in range(0,len(byte),length):
str+=repr( byte[i: i+length] ) [2:-1]
str+=‘\n’
return str
def to_bytes(escape_str):
# 将转义字符串转换为字节
# -*****- 1.2.5版更新: 忽略二进制模式中文字的换行符
escape_str=escape_str.replace(‘\n’,‘’)
escape_str=escape_str.replace(‘“”"’,‘\“\”\"’) # 避免引号导致的SyntaxError
escape_str=escape_str.replace(“‘’'”,“\‘\’\'”)
try:
return eval(‘b"“”’+escape_str+‘“”"’)
except SyntaxError:
return eval(“b’‘’”+escape_str+“‘’'”)
以下代码用于兼容WinHex等软件的十六进制数据, 使用`bytes`对象的`fromhex`、`hex`方法。
知识点: `bytes`对象有`fromhex()`和`hex()`方法, 可实现十六进制数据和`bytes`对象之间的**相互转换**。`fromhex()`方法会忽略参数中的空格, 换行符等字符。
在Editor类外部加入以下代码:
def view_hex(byte):
result=‘’
for i in range(0,len(byte)):
result+= byte[i:i+1].hex().zfill(2) + ’ ’
if (i+1) % 4 == 0:result+=‘\n’
return result
在**Editor类中**加入以下代码:
def insert\_hex(self):
hex = simpledialog.askstring('',
"输入WinHex十六进制数据(如:00 1a 3d ff) :",parent=self)
if hex is None:return
try:
data=bytes.fromhex(hex)
self.contents.insert('insert',to_escape_str(data))
except Exception as err:
handle(err,parent=self)
# 以下代码用于直接在self.txt\_decoded中编辑数据
def \_edit\_decoded\_event(self,event=None):
self.after(20,self.edit_decoded) # 如果不使用after(),self.txt\_decoded.get不会返回最新的值
def edit\_decoded(self):
range_=self.contents.tag_ranges(SEL) # 获取选区
if range_:
start,end=range_[0].string,range_[1].string # 转换为字符串
else:start=self.contents.index(INSERT);end=None
try:
coding=self.coding.get()
if coding=="自动":
msgbox.showinfo('','不支持自动编码, 请选择或输入其他编码',parent=self)
return
byte = self.txt_decoded.get('1.0',END)[:-1].encode(coding)
esc_char = to_escape_str(byte,linesep=False)
self.file_modified=True;self.change_title()
if range_:
self.contents.delete(start,end)
self.contents.insert(start,esc_char)
end = '%s+%dc'%(start, len(esc_char))
self.contents.tag_add(SEL,start,end)
except Exception as err:handle(err,parent=self)
## 5.查找、替换对话框
这里主要用到文本框Text的`search`方法,
该函数接收2个必选参数, 分别是pattern和index, index为起始索引, search 方法返回起始索引处或之后的第一个匹配项的索引。
search方法还有一些可选参数:
`regexp`: 是否使用正则表达式查找 (比自己编写代码查找要快)。
`nocase`: 是否不区分大小写。
显示对话框时, 还需调用Tk,Toplevel的focus方法, 用于使对象获得焦点。
注意: 创建`IntVar()`, `StringVar()`时, 需指定参数`master`, 避免创建的变量无法使用。
\* 使对话框跟随父窗口最小化、恢复显示的方法: 调用`transient()`方法。
在**Editor类外部**加入以下代码:
class SearchDialog(Toplevel):
#查找对话框
def __init__(self,master):
self.master=master
self.coding=self.master.coding.get()
def init_window(self,title=“查找”):
Toplevel.init(self,self.master)
self.title(title)
self.attributes(“-toolwindow”,True)
self.attributes(“-topmost”,True)
# 当父窗口隐藏后,窗口也跟随父窗口隐藏
self.transient(self.master)
self.wm_protocol(“WM_DELETE_WINDOW”,self.onquit)
def show(self):
self.init_window()
frame=Frame(self)
ttk.Button(frame,text=“查找下一个”,command=self.search).pack()
ttk.Button(frame,text=“退出”,command=self.onquit).pack()
frame.pack(side=RIGHT,fill=Y)
inputbox=Frame(self)
Label(inputbox,text=“查找内容:”).pack(side=LEFT)
self.keyword=StringVar(self.master)
keyword=ttk.Entry(inputbox,textvariable=self.keyword)
keyword.pack(side=LEFT,expand=True,fill=X)
keyword.bind(“”,self.search)
keyword.focus_force()
inputbox.pack(fill=X)
options=Frame(self)
self.create_options(options)
options.pack(fill=X)
def create_options(self,master):
Label(master,text="选项: ").pack(side=LEFT)
self.use_regexpr=IntVar(self.master)
ttk.Checkbutton(master,text=“使用正则表达式”,variable=self.use_regexpr)
.pack(side=LEFT)
self.match_case=IntVar(self.master)
ttk.Checkbutton(master,text=“区分大小写”,variable=self.match_case)
.pack(side=LEFT)
self.use_escape_char=IntVar(self.master)
self.use_escape_char.set(self.master.isbinary)
ttk.Checkbutton(master,text=“使用转义字符”,variable=self.use_escape_char)
.pack(side=LEFT)
def search(self,event=None,mark=True,bell=True):
text=self.master.contents
key=self.keyword.get()
if not key:return
# 验证用户输入是否正常
if self.use_escape_char.get():
try:key=str(to_bytes(key),encoding=self.coding)
except Exception as err:
handle(err,parent=self);return
if self.use_regexpr.get():
try:re.compile(key)
except re.error as err:
handle(err,parent=self);return
# 默认从当前光标位置开始查找
pos=text.search(key,INSERT,'end-1c',# end-1c:忽略末尾换行符
regexp=self.use_regexpr.get(),
nocase=not self.match_case.get())
if not pos:
# 尝试从开头循环查找
pos=text.search(key,'1.0','end-1c',
regexp=self.use_regexpr.get(),
nocase=not self.match_case.get())
if pos:
if self.use_regexpr.get(): # 获取正则表达式匹配的字符串长度
text_after = text.get(pos,END)
flag = re.IGNORECASE if not self.match_case.get() else 0
length = re.match(key,text_after,flag).span()[1]
else:
length = len(key)
newpos="%s+%dc"%(pos,length)
text.mark_set(INSERT,newpos)
if mark:self.mark_text(pos,newpos)
return pos,newpos
elif bell: # 未找到,返回None
bell_(widget=self)
def findnext(self,cursor_pos='end',mark=True,bell=True):
# cursor\_pos:标记文本后将光标放在找到文本开头还是末尾
# 因为search()默认从当前光标位置开始查找
# end 用于查找下一个操作, start 用于替换操作
result=self.search(mark=mark,bell=bell)
if not result:return
if cursor_pos=='end':
self.master.contents.mark_set('insert',result[1])
elif cursor_pos=='start':
self.master.contents.mark_set('insert',result[0])
return result
def mark\_text(self,start_pos,end_pos):
text=self.master.contents
text.tag_remove("sel","1.0",END) # 移除旧的tag
# 已知问题: 代码高亮显示时, 无法突出显示找到的文字
text.tag_add("sel", start_pos,end_pos) # 添加新的tag
lines=text.get('1.0',END)[:-1].count(os.linesep) + 1
lineno=int(start_pos.split('.')[0])
# 滚动文本框, 使被找到的内容显示 ( 由于只判断行数, 已知有bug); 另外, text['height']不会随文本框缩放而变化
text.yview('moveto', str((lineno-text['height'])/lines))
text.focus_force()
self.master.update_status()
def onquit(self):
self.withdraw()
class ReplaceDialog(SearchDialog):
#替换对话框
def show(self):
self.init_window(title=“替换”)
frame=Frame(self)
ttk.Button(frame,text=“查找下一个”, command=self._findnext).pack()
ttk.Button(frame,text=“替换”, command=self.replace).pack()
ttk.Button(frame,text=“全部替换”, command=self.replace_all).pack()
ttk.Button(frame,text=“退出”, command=self.onquit).pack()
frame.pack(side=RIGHT,fill=Y)
inputbox=Frame(self)
Label(inputbox,text="查找内容:").pack(side=LEFT)
self.keyword=StringVar(self.master)
keyword=ttk.Entry(inputbox,textvariable=self.keyword)
keyword.pack(side=LEFT,expand=True,fill=X)
keyword.focus_force()
inputbox.pack(fill=X)
最后
🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!