绘制椭圆形的板块移动代码

7
我正在为大学制作一个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

3
这不是一个问题。您不能只是倾倒整个程序并寻求帮助。能否告诉我们你遇到的问题?如果可以,请将代码片段缩小到与移动棋子相关的部分,因为我没有时间浏览所有内容。 - machine yearning
1
@Tim Post,至少给那个人一两天的时间来编辑问题以修复它? - smci
5
关闭后仍可编辑问题。如果被修复,请标记以重新开放。 - Bill the Lizard
@Tim Post,我已经编辑了这个问题,我该如何标记它或让它重新开放?谢谢。 - William Armstrong
1
@smci:我不明白你的观点。那条评论已经五年半了,而在我发表那条评论之后,唯一的编辑就是你刚刚做的编辑。其中一个编辑后,我也取消了我的踩,但我不知道是哪一个编辑。 - Bryan Oakley
显示剩余7条评论
2个回答

35

你可以使用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()

1
我知道这已经是过去式了,但万一你仍在使用这种风格,我想提一下我认为"On..."函数名不好。首先,它们是大写的,所以大多数人会立即想到类,你打破了传统,如果你问我,这些传统有助于更快地理解。我可以理解你为什么要这样做,但最大的问题是你在重复自己。tag_bind函数告诉你所有的信息,最好的方法是解释函数正在做什么,它封装了什么,而不是调用它的内容,因为那可能会改变。 - GRAYgoose124
@GRAYgoose124:你对函数名使用大写字母的观点是完全正确的。感谢你指出来。我不知道为什么会选择那种命名方案。我已经更新了答案,使用全部小写字母。我不太明白你试图表达的另一个观点。我不知道你在哪里看到我在重复自己。所有的代码都是必要的,以便得到一个完整的示例,我认为在这种情况下,一个完整的示例是最好的。 - Bryan Oakley
我差点就达到了注释限制,所以如果我没有表达清楚,我很抱歉。让我详细说明一下。...tag_bind("token", "<ButtonPress-1>"...)传达了有关它如何被激活的所有信息,这与名称on_token_press所传达的信息相同。相反,我认为根据函数的功能而不是调用方式来命名函数更加明智。在这种情况下,我认为drag_object更能说明正在发生的事情。阅读您当前的tag_bind声明。然后将其与此进行比较:...tag_bind("token", "<ButtonPress-1>", self.drag_object)... - GRAYgoose124
在我的情况下,这个绑定链接到哪些按钮上的操作是非常明显的。而在你的情况下,它只是说明了正在被链接的绑定,而函数并没有传达它正在做什么的信息。如果你改变了绑定,那么你需要改变函数名称以匹配“onX”格式。这就是我所说的重复自己的意思,我不认为这种方式的代码能够自我记录得像现在这样,而且现在函数名称并不取决于函数内部,而是取决于它被调用的方式,这会混淆分离。 - GRAYgoose124
我在一个新的问题中引用了你的回答:https://stackoverflow.com/questions/59463144/python-tkinter-drag-object-embed-a-class-within-a-function - WinEunuuchs2Unix

2

第六次编辑:这里有两个解决方案:

  1. (如Bryan所建议的)要么记住移动棋子的旧位置,在那里取消绘制(=> 用背景色绘制),然后在新位置重新绘制它
  2. 更简单的方法:清除并重新绘制整个棋盘

第五次编辑:好的,感谢您精简代码。

准确解释一下您的棋盘绘制代码存在什么问题?“移动的棋子没有从旧位置删除”?“所有棋子都以错误的坐标或颜色绘制”?…?只是不断地倾倒代码并说“此代码不起作用”是不可接受的。

“我不知道如何重绘棋子,而不删除其他棋子。” 我认为你的问题就在这里。如果声明并调用 redrawBoard(),它应该重新绘制所有的棋子(!),而不仅仅是移动的棋子。同意吗?即,您必须迭代 board[][] 上的所有内容,并对每个棋子调用 drawPiece()。但是您的代码似乎已经实现了这一点?

让我建议您如何清理现有的棋盘绘制代码,在这个过程中,您几乎肯定会找到错误。显然,每次移动(或晋升)时需要清除并重新绘制屏幕,您真的这样做了吗?为此声明一个 redrawBoard() 函数。如果您不进行清除操作,则棋子将显示在旧位置和新位置,这显然是错误的。 (有关“Frame rate 是画布每秒更新的频率”的评论使我想知道何时重新绘制,除非您也有时钟或其他更改的数据,否则您不需要每秒重绘 10 次。但是,嘿,这也起作用。)

首先,强烈建议您使用枚举来自行记录 board[][] 中使用的值

class Checkers():
    EMPTY=0
    RED_PIECE=1
    RED_KING=2
    BLACK_PIECE=3
    BLACK_KING=4

接下来,您可以大大简化绘制棋盘的代码。 由于所有4种棋子绘制情况都调用了一个公共情况,因此将其制作为函数,并使该函数不那么凌乱:

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')

这种分解难道不是更易于阅读和调试吗?它是自我记录的。现在你的错误应该几乎跳到了你面前。

(顺便说一下,你目前将国王的绘制方式与棋子完全相同,但我猜你以后会修复这个问题。)


第四次编辑:你让我们看错了函数,真气人...你说你的错误实际上在棋盘绘制代码中。你能否更正标题仍然说"实现一个移动函数"


原始回复: 机器学习所说的,这不是一个问题——至少还不是:告诉我们你目前尝试了什么,为什么不起作用。另外,删除所有无关的代码。

看起来你在函数moveTo(i,j)方面遇到了困难——但具体是什么呢? (全局变量secondPass、secondPosition表明你可能正在遇到麻烦...你知道递归吗?如果不知道,也没关系。)

此外,作为一件风格上的事情,并且为了使你的生活更轻松,这个实现并不是面向对象的,全局变量表明分解得不好。尝试将其重写为一个类Checkers,将棋盘等作为成员变量,编写一个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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接