移动椭圆的板绘图代码
Posted
技术标签:
【中文标题】移动椭圆的板绘图代码【英文标题】:board-drawing code to move an oval 【发布时间】:2011-10-08 02:59:28 【问题描述】:我正在为大学开发一个 Python 跳棋游戏。我使用 tk 绘制了棋盘,但我似乎无法实现棋子的移动功能。如果有人在我的代码中看到任何错误,或者可以提供帮助,我将不胜感激。这是完整的来源。提前致谢。
我知道这会画出棋子。我不知道如何重绘这些片段,而不删除其他片段。我在网上查看了移动功能,并尝试了有效的简单测试,但我无法在我的代码中使用它。
我知道递归,但是,在我实现更多功能之前,我需要基本功能才能工作,即在屏幕上实际移动一块。
lst2 = []
#counter variable
i=0
#board variable is what stores the X/O/- values.
# It's a 2D list. We iterate over it, looking to see
# if there is a value that is X or O. If so, we draw
# text to the screen in the appropriate spot (based on
# i and j.
while i < len(board):
j=0
while j < len(board[i]):
if board[i][j] == 2:
lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
(j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
(j+1)*width + width/2 - 15, fill="Red",outline='Black'))
elif board[i][j] == 4:
lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
(j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
(j+1)*width + width/2 - 15, fill="Red",outline='Black'))
elif board[i][j] == 1:
lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
(j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
(j+1)*width + width/2 - 15, fill="Black",outline='Black'))
elif board[i][j] == 3:
lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
(j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
(j+1)*width + width/2 - 15, fill="Black",outline='Black'))
j+=1
i+=1
【问题讨论】:
这不是问题。您不只是转储整个程序并寻求帮助。你能告诉我们你遇到的问题吗?如果有的话,请将您的代码段缩小到与移动一块相关的部分,因为我没有时间全部筛选。 @Tim Post,至少给他一两天时间来编辑问题以解决它? @Tim Post,我反对突然关闭而没有给他时间回复,我raised this for discussion on MSO。 关闭后可以编辑问题。如果它得到修复,请将其标记为重新打开。 @smci:我不明白你的意思。那条评论是五年半前的,我发表评论后唯一的编辑就是你刚刚做出的编辑。在其中一次编辑后,我也删除了我的反对票,尽管我不知道是哪个。 【参考方案1】:您可以使用coords 和/或move 方法在画布上移动项目,将坐标从它们的位置更改为您想要的位置。
这是一个简单的示例,展示了如何在画布上创建和移动项目:
import tkinter as tk # python 3
# import Tkinter as tk # python 2
class Example(tk.Frame):
"""Illustrate how to drag items on a Tkinter canvas"""
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# create a canvas
self.canvas = tk.Canvas(width=400, height=400, background="bisque")
self.canvas.pack(fill="both", expand=True)
# this data is used to keep track of an
# item being dragged
self._drag_data = "x": 0, "y": 0, "item": None
# create a couple of movable objects
self.create_token(100, 100, "white")
self.create_token(200, 100, "black")
# add bindings for clicking, dragging and releasing over
# any object with the "token" tag
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_oval(
x - 25,
y - 25,
x + 25,
y + 25,
outline=color,
fill=color,
tags=("token",),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
delta_x = event.x - self._drag_data["x"]
delta_y = event.y - self._drag_data["y"]
# move the object the appropriate amount
self.canvas.move(self._drag_data["item"], delta_x, delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
【讨论】:
我知道这是过去的事了,但如果你还在使用这种风格,我想提一下,我认为“On...”函数名称并不好.首先,它们是有上限的,所以大多数人会立即认为上课,你打破了传统,如果你问我这有助于更快地理解。我明白你为什么要这样做,但最大的问题是你在重复自己。 tag_bind 函数会告诉你所有这些信息,最好解释一下函数在做什么,它封装了什么,而不是调用它,因为它可以改变。 @GRAYgoose124:您对函数名使用大写字母是绝对正确的。感谢您指出。我不知道为什么我选择了那个命名方案。我更新了答案以使用所有小写字母。我不太明白你试图提出的另一点。我看不出你认为我在哪里重复自己。所有代码对于一个功能齐全的示例都是必需的,我认为在这种情况下最好使用一个功能齐全的示例。 我快达到评论限制了,如果我不清楚,我很抱歉。让我详细说明。...tag_bind("token", "<ButtonPress-1>"...
传达了名称 on_token_press
传达的所有关于它是如何被激活的信息。相反,我认为根据函数的作用而不是调用方式来命名函数更为谨慎。在这种情况下,我认为drag_object
对正在发生的事情更有启发性。阅读您当前的tag_bind
声明。然后将其与此进行比较:...tag_bind("token", "<ButtonPress-1>", self.drag_object)...
在我的例子中,绑定链接到什么按钮的动作是显而易见的。在您的情况下,它表示正在链接哪个绑定,然后该函数不会传达有关它正在做什么的信息。如果您曾经更改绑定,则需要更改函数名称以匹配 onX
样式。这就是我重复自己的意思,我不觉得代码能够以这种方式自我记录,因为现在函数名称不依赖于函数的内部结构,而是如何调用它,一个外部的混淆分离的因素。
我在一个新问题中引用了您的答案:***.com/questions/59463144/…【参考方案2】:
第 6 次编辑:这里有两个解决方案:
-
(正如 Bryan 建议的那样)要么记住已移动部分的旧位置,然后在此处取消绘制(=> 以背景颜色绘制),然后在新位置重新绘制它
更简单:清除并重绘整个棋盘
第 5 次编辑:好的,感谢您删除代码。
准确解释您的绘图代码有什么问题? '移动的部分没有从旧位置删除'? '所有作品都以错误的坐标或颜色绘制'? ...? 继续转储代码并说“此代码不起作用”是不可接受的。
“我不知道如何在不删除其他部分的情况下重新绘制这些部分。” 我认为那是你的问题。如果你声明并调用
redrawBoard()
,它应该重绘所有的棋子(!),而不仅仅是移动的棋子。同意?即您必须遍历所有 board[][] 并在每一块上调用 drawPiece() 。但是您的代码似乎已经这样做了?
让我建议你如何清理现有的绘图代码,在这个过程中你几乎肯定会找到你的错误。
显然,每次有移动(或升级)时,您都需要清除并重新绘制屏幕,您真的这样做吗?为此声明一个 fn redrawBoard()
。如果您不清除,那么在移动后,该棋子将显示在其旧位置和新位置,这显然是错误的?
(关于帧率的评论是画布每秒更新的频率。让我想知道当你重绘时,你不需要每秒重绘 10 次,除非你还有时钟或其他变化数据。但是,嘿,这也有效。)
首先强烈建议你use an enum to self-document the values用在board[][]
class Checkers():
EMPTY=0
RED_PIECE=1
RED_KING=2
BLACK_PIECE=3
BLACK_KING=4
接下来,你可以大大清理画板代码。 由于所有 4 个绘图案例都称为普通案例,因此将其设为 fn,并使 fn 整洁:
def drawPiece(i,j,fillColor,outlineColor):
"""Draw single piece on screen."""
x = (i+1)*width + width/2
y = (j+1)*height + height/2
lst2.append(canvas.create_oval(x+15,y+15,x-15,y-15,fill=fillColor,outline=outlineColor))
现在,严格调用这些的绘图代码实际上只有两种情况:(2,4) 或 (1,3) 假设你的枚举是正确的:
顺便说一句,永远不要使用更清晰的 for 循环可以使用的 while 循环:
for i in range(len(board)):
for j in range(len(board[i])):
if board[i][j] in (RED_PIECE,RED_KING):
drawPiece(i,j,'Red','Black')
elif board[i][j] in (BLACK_PIECE,BLACK_KING):
drawPiece(i,j,'Black','Black')
这种分解不是更容易阅读和调试吗?它是自我记录的。现在你的 bug 应该会突然出现在你身上。
(顺便说一句,你现在画的国王和棋子一模一样,但我想你以后会解决这个问题的。)
第 4 次编辑:您让我们查看了错误的 fns,grr...您说您的错误实际上在 board-drawing 代码 中。请您更正仍然显示“实现移动功能”的标题?
原始回复: 什么机器渴望说,这不是一个问题 - 还没有:告诉我们你目前正在尝试什么,以及为什么它不起作用。另外,删除所有不相关的代码。
看起来您在使用 moveTo(i,j)
函数时遇到了困难 - 但究竟是什么?
(全局的 secondPass、secondPosition 表示您可能遇到了麻烦……您知道递归吗?如果不知道,不用担心。)
另外,作为一个风格的东西,为了让你的生活更轻松,这个实现不是面向对象的,全局变量尖叫糟糕的分解。尝试重写为 Checkers
的类,让 board 等成为成员,编写 init()
方法。我会将函数 grid(x,y)
重命名为 initialize(nrows,ncols)
。
(咳咳!迹象表明你是从别人那里改编的……)
#Frame rate is how often canvas will be updated
# each second. For Tic Tac Toe, 10 should be plenty.
FRAME_RATE = 10
【讨论】:
是的,改编自我教授的井字游戏。这是他为我们提供的图形编程示例。我已经使用递归作为这个类的练习,没有尝试在这个游戏中实现它,我专注于获取结果,然后压缩代码。 re:“显然,每次有移动(或升级)时,您都需要清除并重新绘制屏幕”。一般来说,这是不正确的。您无需在每次移动时都重新绘制屏幕。您可以轻松移动单个对象。 @Bryan,是的,但是清除和重绘肯定会修复他的错误。 (Clear-and-redraw 是对他的代码的一种更简单的修复,因为近一周后他无法调试它。你的方法需要他也改变他的 move fn 代码以记住旧的位置;他之前发布了移动代码,并且不是很OO,所以这可能会很痛苦:更多全局变量,更混乱) @smci:我建议您完全重写您的答案。我不必跳到答案的中间来获取原始部分,然后从中间向上阅读您的所有编辑。没有人关心编辑。只需创建一个写得很好的单一答案。以上是关于移动椭圆的板绘图代码的主要内容,如果未能解决你的问题,请参考以下文章