开头感谢Roger D. Serwy University of Illinois(写LineNumbers.py的大佬)
以及 感谢大佬们的介绍,以下是主要参考博客网址
1. https://blog.csdn.net/gfs258/article/details/107535027?ops_request_misc=%7B%22request%5Fid%22%3A%22160173762319724836741631%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=160173762319724836741631&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-3-107535027.pc_search_result_cache&utm_term=Python3.8+IDLE行号&spm=1018.2118.3001.4187
2. https://jingyan.baidu.com/article/915fc414a8734051394b20eb.html
那么,我们开始吧
Step 1. 首先我们需要下载一个.py的文件,是用来解决行号的
下载地址:svwh.dl.sourceforge.net/project/sourcetrac/tmp/IDLE/idlexlib/extensions/LineNumbers.py
这个网址需要科学上网,所以我把.py的内容直接粘贴到这里了,
不方便的童鞋可以建一个名字为“LineNumbers.py”的文件,其内容如下
# IDLEX EXTENSION ## """ ## Copyright(C) 2011 The Board of Trustees of the University of Illinois. ## All rights reserved. ## ## Developed by: Roger D. Serwy ## University of Illinois ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal with the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## + Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimers. ## + Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimers in the ## documentation and/or other materials provided with the distribution. ## + Neither the names of Roger D. Serwy, the University of Illinois, nor ## the names of its contributors may be used to endorse or promote ## products derived from this Software without specific prior written ## permission. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH ## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. ## ## ## ## LineNumbers Extension ## ## Provides line numbers to the left of the source code. ## ## The width of the line numbers adapts. Limit of 99,999 lines (for proper display). ## ## """ config_extension_def = """ [LineNumbers] enable=1 enable_shell=0 visible=True [LineNumbers_cfgBindings] linenumbers-show= """ import sys if sys.version < '3': from Tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X else: from tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X from idlelib.configHandler import idleConf from idlelib.Delegator import Delegator from idlelib.Percolator import Percolator FONTUPDATEINTERVAL = 1000 # milliseconds _AFTER_UNDO = True # Flag to have the LineNumberDelegator inserted after the undo delegator jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates sp = lambda x: map(int, x.split('.')) # convert tkinter Text coordinate to a line and column tuple def dbprint(func): # A decorator for debugging def f(*args, **kwargs): print(func, args, kwargs) return func(*args, **kwargs) return f class LineNumbers(object): menudefs = [('options', [('!Show Line Numbers', '<<linenumbers-show>>')])] def __init__(self, editwin): self.editwin = editwin self.text = self.editwin.text self.textfont = None self.width = 2 self.after_id = None self.create_linenumbers() e = idleConf.GetOption("extensions", "LineNumbers", "visible", type="bool", default=True) self.set_visible(e) self.code_context_fix() def close(self): if self.after_id: self.text.after_cancel(self.after_id) self.visible = False def adjust_font(self): try: # taken from CodeContext.py newtextfont = self.editwin.text["font"] if self.textln and newtextfont != self.textfont: self.textfont = newtextfont self.textln["font"] = self.textfont if self._cc_text: self._cc_text["font"] = self.textfont self.update_numbers() except Exception as err: import traceback; traceback.print_exc() def font_timer(self): if not self.visible: return self.adjust_font() if self.after_id: self.text.after_cancel(self.after_id) self.after_id = self.text.after(FONTUPDATEINTERVAL, self.font_timer) if not _AFTER_UNDO: self.update_numbers() # fixes a bug due to this percolator being ahead of undo percolator. def set_visible(self, b=True): self.visible = b if self.visible: self.text.after(1, self.font_timer) # avoid a start-up bug self.show() # use .after to avoid a start-up error caused by update_idletasks in update_numbers self.text.after(1, self.update_numbers) else: self.hide() idleConf.SetOption("extensions", "LineNumbers", "visible", '%s' % self.visible) self.editwin.setvar("<<linenumbers-show>>", self.visible) def linenumbers_show_event(self, ev=None): self.set_visible(not self.visible) self._code_context_toggle() def create_linenumbers(self): """ Create the widget for displaying line numbers. """ editwin = self.editwin text = self.text text_frame = editwin.text_frame self.textln = textln = Text(text_frame, width=self.width, height=1, wrap=NONE) # adjust font textln.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), idleConf.GetOption('main', 'EditorWindow', 'font-size'))) textln.bind("<FocusIn>", self.focus_in_event) textln.bind('<Button-1>', self.button_ignore) textln.bind('<Button-2>', self.button_ignore) textln.bind('<Button-3>', self.button_ignore) textln.bind('<B1-Motion>', self.button_ignore) textln.bind('<B2-Motion>', self.button_ignore) textln.bind('<B3-Motion>', self.button_ignore) textln.bind("<Button-4>", self.button4) textln.bind("<Button-5>", self.button5) textln.tag_config('LINE', justify=RIGHT) textln.insert(END, '1') textln.tag_add('LINE', '1.0', END) # start the line numbers self.per = per = Percolator(textln) self.line_delegator = LineDelegator() per.insertfilter(self.line_delegator) textln._insert = self.line_delegator.delegate.insert textln._delete = self.line_delegator.delegate.delete lines = LineNumberDelegator(self) if _AFTER_UNDO: # Percolator.py's .insertfilter should have an "after=" argument lines.setdelegate(editwin.undo.delegate) editwin.undo.setdelegate(lines) else: editwin.per.insertfilter(lines) editwin.vbar['command'] = self.vbar_split editwin.text['yscrollcommand'] = self.yscroll_split def button4(self, ev=None): self.text.event_generate("<Button-4>") return "break" def button5(self, ev=None): self.text.event_generate("<Button-5>") return "break" def button_ignore(self, ev=None): return "break" def show(self): self.textln.pack(side=LEFT, fill=Y, before=self.editwin.text) def hide(self): self.textln.pack_forget() def focus_in_event(self, event=None): self.editwin.text.focus_set() self.textln.tag_remove('sel', '1.0', 'end') #self.editwin.text.event_generate("<<goto-line>>") def generate_goto_event(self, event=None): self.editwin.text.event_generate("<<goto-line>>") return "break" def vbar_split(self, *args, **kwargs): """ split scrollbar commands to the editor text widget and the line number widget """ self.textln.yview(*args, **kwargs) self.text.yview(*args, **kwargs) def yscroll_split(self, *args, **kwargs): """ send yview commands to both the scroll bar and line number listing """ #import traceback; traceback.print_stack() self.editwin.vbar.set(*args) self.textln.yview_moveto(args[0]) def update_numbers(self, add=None, remove=None): if not self.visible: return textln = self.textln text = self.editwin.text endline1, col1 = sp(text.index(END)) endline2, col2 = sp(textln.index(END)) if endline1 < endline2: # delete numbers textln._delete('%i.0' % endline1, END) elif endline1 > endline2: # add numbers q = range(endline2, endline1) r = map(lambda x: '%i' % x, q) s = '\n' + '\n'.join(r) textln._insert(END, s) textln.tag_add('LINE', '1.0', END) # adjust width of textln, if needed. (counts from 1, not zero) if endline1 <= 100: width = 2 elif endline1 <= 1000: width = 3 elif endline1 <= 10000: width = 4 else: width = 5 # more than 9999 lines in IDLE? Really? # XXX: If your code requires width>5, i.e > 100,000 lines of code, # you probably should not be using IDLE. if width > self.width: # 2011-12-18 - only grow, not shrink self.width = width textln.configure(width=width) if self._cc_text: # adjust CC width self._cc_text.configure(width=width) self.textln.update_idletasks() a = self.text.yview() self.textln.yview_moveto(a[0]) def code_context_fix(self): self._cc_text = None self._cc_frame = None def f(): self.text.bind('<<toggle-code-context>>', self._code_context_toggle, '+') self._code_context_toggle() self.text.after(10, f) def _code_context_toggle(self, event=None): cc = self.editwin.extensions.get('CodeContext', None) if cc is None: return if not self.visible: if self._cc_frame: L = cc.label L.pack_forget() self._cc_frame.destroy() L.pack(side=TOP, fill=X, expand=False, before=self.editwin.text_frame) return editwin = self.editwin text = self.text text_frame = editwin.text_frame # repack the Label in a frame if cc.label: cc.label.pack_forget() F = Frame(self.editwin.top) F.lower() # fix Z-order t = Text(F, width=self.width, height=1, takefocus=0) t.bind("<FocusIn>", lambda x: self.text.focus()) t["font"] = self.textln.cget('font') t.pack(side=LEFT, fill=Y) cc.label.pack(in_=F, fill=X, expand=False) F.pack(side=TOP, before=text_frame, fill=X, expand=False) self._cc_frame = F self._cc_text = t else: if self._cc_frame: self._cc_frame.destroy() self._cc_frame = None self._cc_text = None class LineNumberDelegator(Delegator): # for editwin.text def __init__(self, line_number_instance): Delegator.__init__(self) self.ext = line_number_instance def insert(self, index, chars, tags=None): self.delegate.insert(index, chars, tags) if '\n' in chars: self.ext.update_numbers()#add=chars.count('\n')) def delete(self, index1, index2=None): t = self.get(index1, index2) self.delegate.delete(index1, index2) if '\n' in t: self.ext.update_numbers()#remove=t.count('\n')) class LineDelegator(Delegator): # for textln def insert(self, *args, **kargs): pass def delete(self, *args, **kargs): passStep 2. 接着,我们要找到安装目录下的Lib\idlelib目录,复制LineNumber到这个目录
这里有个问题,就是(至少对我来说,在MacOS下找安装目录不是很方便),建议的方法是,直接在Finder(Finder的Macintosh HD)下搜索idlelib,理论上会搜到两个,因为Mac会自带一个Python2的环境,找咱们下的Python3环境,我电脑上的路径长这样:
这个就是对应(目前大多数Windows系统下的所在idlelib目录)
然后就是复制LineNumber到这个目录就可以
Step 3. 再接着,打开LineNumber.py文件,把文件中的下述这三行代码
from idlelib.configHandler import idleConf from idlelib.Delegator import Delegator from idlelib.Percolator import Percolator改为:
from idlelib.config import idleConf from idlelib.delegator import Delegator from idlelib.percolator import Percolator改完记得保存下
Step 4. 之后,开始配置extensions.def:
还是在刚刚的那个idlelib目录下,找到config-extensions.def文件,往config-extensions.def文件配置如下数据:
[LineNumbers] enable=True #开启扩展 enable_editor=True #开启idle编辑器支持 enable_shell=True #开启idle shell支持 visible=True #扩展可见我添加的位置不知道规不规范,但是亲测可行
我加在了这里:如图示,第20-28行的位置
至此,配置工作结束。
重启后,在Options选项中有Show Line Numbers,点击勾选就可以拥有行号了
(但是有两个,下面的那个不好使,不知道为什么,但是上面那个好使就可以了啊-- )
至于还有其他问题详见这位大佬的博客 以及他的参考博客:
https://blog.csdn.net/gfs258/article/details/107535027?ops_request_misc=%7B%22request%5Fid%22%3A%22160173762319724836741631%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=160173762319724836741631&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-3-107535027.pc_search_result_cache&utm_term=Python3.8+IDLE行号&spm=1018.2118.3001.4187