移动椭圆的板绘图代码

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", "&lt;ButtonPress-1&gt;"... 传达了名称 on_token_press 传达的所有关于它是如何被激活的信息。相反,我认为根据函数的作用而不是调用方式来命名函数更为谨慎。在这种情况下,我认为drag_object 对正在发生的事情更有启发性。阅读您当前的tag_bind 声明。然后将其与此进行比较:...tag_bind("token", "&lt;ButtonPress-1&gt;", 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:我建议您完全重写您的答案。我不必跳到答案的中间来获取原始部分,然后从中间向上阅读您的所有编辑。没有人关心编辑。只需创建一个写得很好的单一答案。

以上是关于移动椭圆的板绘图代码的主要内容,如果未能解决你的问题,请参考以下文章

如何正确绘制椭圆[KineticJs]

椭圆形移动程序。椭圆不动,有啥我可以在这里炼制的吗?

猎豹MFC--画椭圆圆弧饼图多边形及其他形状

CAD绘图技巧,怎么绘制椭圆弧

绘图(CGcontext)

实现 Keynote 等绘图功能的最佳方式