绘制椭圆的代码移动

7 投票
2 回答
14410 浏览
提问于 2025-04-16 21:48

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

2 个回答

2

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

  1. (正如Bryan所建议的)记住被移动棋子的旧位置,在那里把它“擦掉”(也就是用背景颜色画),然后在新位置重新画上它。
  2. 更简单的方法:清空并重新绘制整个棋盘。

第五次编辑:谢谢你简化了代码。

准确说明你的棋盘绘制代码有什么问题?“移动的棋子没有从旧位置删除”?“所有棋子都画在错误的坐标或颜色上”?……只是一味地扔代码并说“这段代码不工作”是不可接受的。

“我不知道如何重新绘制棋子,而不删除其他棋子。”我觉得这就是你的问题所在。如果你声明并调用redrawBoard(),它应该重新绘制所有棋子,而不仅仅是移动的那个。你同意吗?也就是说,你必须遍历整个board[][],并对每个棋子调用drawPiece()。但你的代码似乎已经这样做了?

让我建议你如何清理现有的棋盘绘制代码,在这个过程中你几乎肯定会找到你的错误。显然,每次移动(或升变)时,你都需要清空并重新绘制屏幕,你真的这样做了吗?为此声明一个函数redrawBoard()。如果你不清空,那么在移动后,棋子会同时显示在旧位置和新位置,这显然是错误的吧?(关于帧率是画布每秒更新的频率。的评论让我想知道,当你重新绘制时,除非你还有时钟或其他变化的数据,否则不需要每秒重绘10次。不过,这样也可以。)

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

class Checkers():
    EMPTY=0
    RED_PIECE=1
    RED_KING=2
    BLACK_PIECE=3
    BLACK_KING=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')

这样的分解是不是容易得多阅读和调试?它是自我记录的。现在你的错误应该几乎会跳出来让你看到。

(顺便说一下,你现在绘制国王的方式和其他棋子是一样的,但我想你会在之后修复这个问题。)


第四次编辑:你让我们看到了错误的函数,真让人恼火……你说你的错误实际上在棋盘绘制代码中。请你改正标题,现在仍然写着“实现一个移动函数”吗?


原始回复:正如machine yearning所说,这还不是一个问题:告诉我们你目前尝试了什么,以及为什么它不工作。同时,去掉所有无关的代码。

看起来你在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
36

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

撰写回答