我正在用Python制作一个绘画应用程序。它具有通过单击和拖动来制作形状和绘图的功能。
我一直在尝试在我的应用程序中实现撤消按钮。它可以很好地撤消形状,但不能很好地撤消单击和拖动线。
我的职能:
def undo ():
if la[len(la)-1] =='s' :
turtle.undo()
del(la[len(la)-1])
while(turtle.isdown()==True) :
turtle.undo()
turtle.undo()
turtle.undo()
if la[len(la)-1]=='d' :
turtle.undo()
del(la[len(la)-1])
while turtle.xcor()!=xp[len(xp)-1] and turtle.ycor()!=yp[len(yp)-1] :
turtle.undo()
del(xp[len(xp)-1])
del(yp[len(yp)-1])
la 是一个数组,用于记住最后一个操作是画线还是形状。
如果它是一个形状('s'),则在笔放下时会撤消(因为形状是使用海龟动作制作的,所以笔不会在这些动作之间抬起)。
如果是一条线('d'),则应撤消直到海龟的坐标与线开头的坐标相同(存储在 xp 和 yp 中)。
有时候它会撤销比应有的更多的操作,有时它根本不会撤销。问题是大多数时候它工作得很好,所以我不知道是什么原因导致了问题。最常见的是,当线之前有一个形状时,它会出现问题,它也会撤消部分形状。当然,如果你多次越过这条线的拐点,它就会停止撤销,但可以通过计算你越过它的次数来解决这个问题。
编辑:
最小工作程序:
from tkinter import *
from functools import partial
from turtle import TurtleScreen, RawTurtle, Shape
menu = Tk()
frame=Frame(menu, bd=1, bg='#d1c7c7')
frame.grid(row=1, column=6, rowspan=26 )
canvas=Canvas(frame,width=1000, height=700)
canvas.grid(column=6,row=1, rowspan=26)
size=5
la=['n']
xp=[]
yp=[]
# turtle actions
def draw(x, y):
turtle.ondrag(None)
turtle.down()
turtle.goto(x, y)
turtle.up()
screen.update()
turtle.ondrag(draw)
def move(x, y):
global xp
global yp
global la
xp.append(turtle.xcor())
yp.append(turtle.ycor())
la.append('d')
screen.onscreenclick(None)
turtle.goto(x, y)
screen.onclick(move)
screen.update()
def main():
turtle.shape("circle")
polygon = turtle.get_shapepoly()
fixed_color_turtle = Shape("compound")
fixed_color_turtle.addcomponent(polygon, "", "")
screen.register_shape('fixed', fixed_color_turtle)
turtle.shape("fixed")
turtle.penup()
turtle.pensize(5)
turtle.turtlesize(2000,2000)
turtle.ondrag(draw)
screen.onscreenclick(move)
screen.update()
def setcolor (color) :
turtle.pencolor(color)
turtle.fillcolor(color)
bblack = Button(
menu,
bg='black',
width=10,
command=partial(setcolor,'black')
).grid(column=1, row=2)
bred = Button(
menu,
bg='red',
width=10,
command=partial(setcolor,'red')
).grid(column=1, row=3)
bdraw = Button(
menu,
text='pen',
width=10,
command=main
).grid(column=1, row=4)
def square(x, y):
global la
la.append('s')
turtle.turtlesize(1,1)
screen.onclick(None)
turtle.ondrag(None)
turtle.goto(x-size*8.3, y-size*8.3)
turtle.pendown()
turtle.begin_fill()
for i in range(4):
turtle.forward(size*18)
turtle.left(360 / 4)
turtle.end_fill()
turtle.penup()
possqr()
def possqr():
screen.onclick(square)
bsquare = Button(
menu,
text='square',
width=10,
command=possqr
).grid(column=1, row=5)
def triangle(x, y):
global la
la.append('s')
turtle.turtlesize(1,1)
screen.onclick(None)
turtle.ondrag(None)
turtle.goto(x-size*8.5, y-size*6)
turtle.pendown()
turtle.begin_fill()
for i in range(3):
turtle.forward(size*18)
turtle.left(360 / 3)
turtle.end_fill()
turtle.penup()
postriangle()
def postriangle():
screen.onclick(triangle)
btriangle = Button(
menu,
text='triangle',
width=10,
command=postriangle
).grid(column=1, row=6)
Label(menu, text='COLORS').grid(column=1, row=1)
def undo ():
if la[len(la)-1] =='s' :
turtle.undo()
del(la[len(la)-1])
while(turtle.isdown()==True) :
turtle.undo()
turtle.undo()
turtle.undo()
if la[len(la)-1]=='d' :
turtle.undo()
del(la[len(la)-1])
while turtle.xcor()!=xp[len(xp)-1] and turtle.ycor()!=yp[len(yp)-1] :
turtle.undo()
del(xp[len(xp)-1])
del(yp[len(yp)-1])
bundo = Button(
menu,
text='undo',
width=10,
command=undo
).grid(column=2, row=1)
screen = TurtleScreen(canvas)
screen.tracer(False)
pen_color = 'black'
turtle = RawTurtle(screen)
main()
mainloop()
您可以修复代码中的一个问题,以解决
undo()
函数逻辑中的缺陷:
elif la[len(la)-1]=='d' : ### <<< ###
使用单个
if
,您可能会面临两个 if
块都在一个 undo()
操作上执行的风险。
要改进一点行为,您可以添加:
screen.update()
screen.getcanvas().update_idletasks()
在
update()
函数的末尾。
需要提到的是,
turtle
模块的设计是为了简单起见,因此在undo()
操作之后更新图形的可靠性可能在某些场景中,例如在您的应用程序中,它是tkinter
和turtle
的组合
导致屏幕无法正确更新。
如果您在使用
turtle
模块时想要可靠的行为,则需要重写代码并将所有绘图操作存储在数组中,并在撤消时重置整个海龟窗口并从头开始重新绘制,尽管依赖于 undo()
。