python - 在使用 Python 和一些 tcl 制作的 Canvas 和 Text 小部件中创建新类对象时出现问题?
问题描述
我找到了一个文本编辑器,在该编辑器中tkinter.Canvas
添加了行号,并且tkinter.Text
小部件以及tkinter.Canvas
在 a 内tkinter.Frame
,并且该 Frame 将被添加到tkinter.ttk.Notebook
选项卡中。这是我从我试图添加的笔记本选项卡中删除的代码:
from tkinter import *
import tkinter as tk
class LineNumberCanvas(Canvas):
def __init__(self, *args, **kwargs):
Canvas.__init__(self, *args, **kwargs)
self.text_widget = None
self.breakpoints = []
def connect(self, text_widget):
self.text_widget = text_widget
def re_render(self):
"""Re-render the line canvas"""
self.delete('all') # To prevent drawing over the previous canvas
temp = self.text_widget.index("@0, 0")
while True :
dline= self.text_widget.dlineinfo(temp)
if dline is None:
break
y = dline[1]
x = dline[0]
linenum = str(temp).split(".")[0]
id = self.create_text(2, y, anchor="nw", text=linenum, font='Consolas 13')
if int(linenum) in self.breakpoints:
x1, y1, x2, y2 = self.bbox(id)
self.create_oval(x1, y1, x2, y2, fill='red')
self.tag_raise(id)
temp = self.text_widget.index("%s+1line" % temp)
def get_breakpoint_number(self,event):
if self.find_withtag('current'):
i = self.find_withtag('current')[0]
linenum = int(self.itemcget(i,'text'))
if linenum in self.breakpoints:
self.breakpoints.remove(linenum)
else:
self.breakpoints.append(linenum)
self.re_render()
class CustomText:
def __init__(self, text):
self.text = text
self.master = text.master
self.mechanise()
self._set_()
self.binding_keys()
def mechanise(self):
self.text.tk.eval('''
proc widget_interceptor {widget command args} {
set orig_call [uplevel [linsert $args 0 $command]]
if {
([lindex $args 0] == "insert") ||
([lindex $args 0] == "delete") ||
([lindex $args 0] == "replace") ||
([lrange $args 0 2] == {mark set insert}) ||
([lrange $args 0 1] == {xview moveto}) ||
([lrange $args 0 1] == {xview scroll}) ||
([lrange $args 0 1] == {yview moveto}) ||
([lrange $args 0 1] == {yview scroll})} {
event generate $widget <<Changed>>
}
#return original command
return $orig_call
}
''')
self.text.tk.eval('''
rename {widget} new
interp alias {{}} ::{widget} {{}} widget_interceptor {widget} new
'''.format(widget=str(self.text)))
return
def binding_keys(self):
for key in ['<Down>', '<Up>', "<<Changed>>", "<Configure>"]:
self.text.bind(key, self.changed)
self.linenumbers.bind('<Button-1>', self.linenumbers.get_breakpoint_number)
return
def changed(self, event):
self.linenumbers.re_render()
#print "render"
return
def _set_(self):
self.linenumbers = LineNumberCanvas(self.master, width=30)
self.linenumbers.connect(self.text)
self.linenumbers.pack(side="left", fill="y")
return
if __name__ == '__main__':
root = Tk()
l = Text(root, font='Consolas 13')
CustomText(l)
l.pack(expand=TRUE, fill=BOTH)
root.mainloop()
现在这里的问题是我不知道tcl语言并且程序生成的错误是在 CustomText 类的机械功能中,其中有' new '关键字。它说:
File "C:\Users\Prerak\AppData\Local\Programs\Python\Python37\EZ_PY\ColorText.py", line 590, in mechanise
'''.format(widget=str(self)))
_tkinter.TclError: can't rename to "new": command already exists
任何人都可以帮助我解决这个问题......我所做的就是在单击将 CustomText 对象添加到 tkinter.Notebook 选项卡的按钮后添加新选项卡。
解决方案
问题是它所说的:Tcl 解释器中已经有一个命令叫做new
. 它不是基本 Tcl 命令集的一部分,因此它可能来自默认加载的某个包。不管它是什么,如果它不是你的,它可能需要留在原处,否则其他东西会坏掉。
处理这个问题的最简单的方法是使用一个唯一的计数器,这样每个被拦截的小部件都会得到一些唯一的东西(这类似于gensym
在 Lisp 中所做的)。由于这个问题出现在 Tcl 端,您可以将该计数器保留在该端,并且这样做的方便方法涉及创建第二个过程,我将其称为install_widget_interceptor
. 作为奖励,使用它的调用变得更简单,您可以一次拥有两个CustomText
实例。
(......省略了不会改变的代码,我已经整理了widget_interceptor
程序以使其更加惯用......)
# You probably shouldn't repeat this bit every time you create a widget
self.text.tk.eval('''
proc widget_interceptor {widget command args} {
set orig_call [uplevel 1 [linsert $args 0 $command]]
if {
[lindex $args 0] in {insert delete replace} ||
([lrange $args 0 2] == {mark set insert}) ||
([lrange $args 0 1] == {xview moveto}) ||
([lrange $args 0 1] == {xview scroll}) ||
([lrange $args 0 1] == {yview moveto}) ||
([lrange $args 0 1] == {yview scroll})
} then {
event generate $widget <<Changed>>
}
#return original command
return $orig_call
}
proc install_widget_interceptor {widget} {
global unique_widget_id
set handle ::_intercepted_widget_[incr unique_widget_id]
rename $widget $handle
interp alias {} ::$widget {} widget_interceptor $widget $handle
}
''')
# This bit you absolutely need each time
self.text.tk.eval('''
install_widget_interceptor {widget}
'''.format(widget=str(self.text)))
正如代码中所指出的,在 Tcl 端创建过程可能只应该在创建 Python 类时完成一次。每次都这样做并不重要;只是浪费。
此外,Tk 本身<<Modified>>
会在修改 Text 小部件时为其生成事件。它使用对您而言重要的不同意义,专注于对其管理的模型的更改,而不是对该模型上的视图的更改(因此移动光标或滚动不会触发它)。
推荐阅读
- python - 如何实现功能?
- flutter - Fluter 小部件未运行 initState 并将其正确传递给 Widget
- html - 按钮内的 Gif (HTML, CSS)
- c++ - 遍历资源字符串表
- javascript - 如何在当前浏览器中添加对新“可插入流”功能的支持?
- json - 从 API json 中删除 password_digest?
- java - Java 8 ZonedDateTime 格式日期
- pywin32 - 有人知道如何在虚拟环境中使用 pywin32 吗?
- php - PHP dom解析器:只有在另一个元素之后才获取元素计数?
- r - 在同一数据框中总结多个组的更好方法