【问题标题】:Drag and drop a pawn拖放棋子
【发布时间】:2025-12-21 13:45:11
【问题描述】:
from tkinter import Tk, Canvas, Label, NSEW

class CanvasChecker(Canvas):

    def __init__(self, parent, n_pixels_per_case=60):

        self.checker = {(7, 0): 'o', (7, 2): 'o', (7, 4): 'o', (7, 6): 'o', (6, 1): 'o', 
                (6, 3): 'o', (6, 5): 'o', (6, 7): 'o', (5, 0): 'o', (5, 2): 'o', (5, 4): 'o',
                (5, 6): 'o', (2, 1): 'x', (2, 3): 'x', (2, 5): 'x', (2, 7): 'x', (1, 0): 'x',
                (1, 2): 'x', (1, 4): 'x', (1, 6): 'x', (0, 1): 'x', (0, 3): 'x', (0, 5): 'x',
                (0, 7): 'x'}

        self.n_pixels_per_case = n_pixels_per_case

        width = 8 * n_pixels_per_case
        height = 8 * n_pixels_per_case

        super().__init__(parent, width=width, height=height)

        self.bind('<Configure>', self.redimension)

    def draw_cases(self):
        for i in range(8):
            for j in range(8):
                beg_line = i * self.n_pixels_per_case
                end_line = beg_line + self.n_pixels_per_case
                beg_column = j * self.n_pixels_per_case
                end_column = beg_column + self.n_pixels_per_case

                if (i + j) % 2 == 0:
                    color = '#E88515'
                color = '#DDDDFF'

                self.create_rectangle(beg_column, beg_line, end_column, end_line, fill=color, tags='case')

    def draw_pieces(self):
        for position in self.checker:
            coordinate_y = position[0] * self.n_pixels_per_case + self.n_pixels_per_case // 2
            coordinate_x = position[1] * self.n_pixels_per_case + self.n_pixels_per_case // 2

            piece = self.checker[position]

            if piece == 'o':
                icon = "\u26C0"
            elif piece == 'O':
                icon = "\u26C1"
            elif piece == 'x':
                icon = "\u26C2"
            else:
                icon = "\u26C3"

            character_police = ('Already Seen', self.n_pixels_per_case//2)
            self.create_text(coordinate_x, coordinate_y, text=icon, font=character_police, tags='piece')

    def redimension(self, event):

        new_size = min(event.width, event.height)
        self.n_pixels_per_case = new_size // 8
        self.actualise()

    def actualise(self):
        self.delete('case')
        self.draw_cases()
        self.delete('piece')
        self.draw_pieces()


class Game(Tk):

    def __init__(self):
        super().__init__()

        self.canvas_checker = CanvasChecker(self, 60)
        self.canvas_checker.grid(sticky=NSEW)

        self.messages = Label(self)
        self.messages.grid()

        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)


if __name__ == '__main__':
    window = Game()
    window.mainloop()

我有一个棋盘。我会对在不同情况下拖放每个棋子感兴趣,但我很困惑如何做到这一点。我曾想过使用my_canvas.bind("&lt;B1-Motion&gt;", move),但我不确定如何在上面的代码中使用它。现在,我只能看到不同的白色和黑色块而不能移动它们。

如何在棋盘上拖放棋子?

【问题讨论】:

    标签: python tkinter canvas bind


    【解决方案1】:

    您所要做的就是从您链接到的问题(在您的问题的早期版本中)中的最高投票答案中复制一些代码,然后调整绑定以处理标签“piece”而不是“令牌”。

    首先,将该代码中的函数drag_startdrag_stopdrag 复制到您的CanvasChecker 类中。在复制的代码中将 self.canvas 更改为 self

    接下来,在 __init__ 函数的某处添加这一行:

    self._drag_data = {"x": 0, "y": 0, "item": None}
    

    最后,添加以下绑定,同样在__init__

    self.tag_bind("piece", "<ButtonPress-1>", self.drag_start)
    self.tag_bind("piece", "<ButtonRelease-1>", self.drag_stop)
    self.tag_bind("piece", "<B1-Motion>", self.drag)
    

    您需要添加逻辑来确定移动是否合法,但这回答了如何用鼠标移动棋子的问题。


    from tkinter import Tk, Canvas, Label, NSEW
    
    class CanvasChecker(Canvas):
    
        def __init__(self, parent, n_pixels_per_case=60):
    
            self.checker = {(7, 0): 'o', (7, 2): 'o', (7, 4): 'o', (7, 6): 'o', (6, 1): 'o',
                    (6, 3): 'o', (6, 5): 'o', (6, 7): 'o', (5, 0): 'o', (5, 2): 'o', (5, 4): 'o',
                    (5, 6): 'o', (2, 1): 'x', (2, 3): 'x', (2, 5): 'x', (2, 7): 'x', (1, 0): 'x',
                    (1, 2): 'x', (1, 4): 'x', (1, 6): 'x', (0, 1): 'x', (0, 3): 'x', (0, 5): 'x',
                    (0, 7): 'x'}
    
            self.n_pixels_per_case = n_pixels_per_case
    
            width = 8 * n_pixels_per_case
            height = 8 * n_pixels_per_case
    
            super().__init__(parent, width=width, height=height)
    
            self.bind('<Configure>', self.redimension)
    
            self._drag_data = {"x": 0, "y": 0, "item": None}
            self.tag_bind("piece", "<ButtonPress-1>", self.drag_start)
            self.tag_bind("piece", "<ButtonRelease-1>", self.drag_stop)
            self.tag_bind("piece", "<B1-Motion>", self.drag)
    
    
        def draw_cases(self):
            for i in range(8):
                for j in range(8):
                    beg_line = i * self.n_pixels_per_case
                    end_line = beg_line + self.n_pixels_per_case
                    beg_column = j * self.n_pixels_per_case
                    end_column = beg_column + self.n_pixels_per_case
    
                    if (i + j) % 2 == 0:
                        color = '#E88515'
                    color = '#DDDDFF'
    
                    self.create_rectangle(beg_column, beg_line, end_column, end_line, fill=color, tags='case')
    
        def draw_pieces(self):
            for position in self.checker:
                coordinate_y = position[0] * self.n_pixels_per_case + self.n_pixels_per_case // 2
                coordinate_x = position[1] * self.n_pixels_per_case + self.n_pixels_per_case // 2
    
                piece = self.checker[position]
    
                if piece == 'o':
                    icon = "\u26C0"
                elif piece == 'O':
                    icon = "\u26C1"
                elif piece == 'x':
                    icon = "\u26C2"
                else:
                    icon = "\u26C3"
    
                character_police = ('Already Seen', self.n_pixels_per_case//2)
                self.create_text(coordinate_x, coordinate_y, text=icon, font=character_police, tags='piece')
    
        def redimension(self, event):
    
            new_size = min(event.width, event.height)
            self.n_pixels_per_case = new_size // 8
            self.actualise()
    
        def actualise(self):
            self.delete('case')
            self.draw_cases()
            self.delete('piece')
            self.draw_pieces()
    
        def drag_start(self, event):
            """Begining drag of an object"""
            # record the item and its location
            self._drag_data["item"] = self.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.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
    
    class Game(Tk):
    
        def __init__(self):
            super().__init__()
    
            self.canvas_checker = CanvasChecker(self, 60)
            self.canvas_checker.grid(sticky=NSEW)
    
            self.messages = Label(self)
            self.messages.grid()
    
            self.grid_columnconfigure(0, weight=1)
            self.grid_rowconfigure(0, weight=1)
    
    
    if __name__ == '__main__':
        window = Game()
        window.mainloop()
    

    【讨论】:

    • @Bryan-Oakley 这是一个糟糕的答案,因为您只是在指导 OP 做什么而没有详细说明。
    • @Space:为什么?如果你让我知道,也许我可以把答案做得更好。
    • @Space:详细说明在复制代码的内联 cmets 中。 OP 知道存在更好的答案,但有某种心理障碍将该代码应用于他们的代码。从他们对 cme​​ts 的回答中可以清楚地看出,他们永远不会弄明白。
    • 我知道 OP 已链接到答案(他们已将其删除),您不必解释游戏的机制,而是解释他们的代码失败的原因和位置。
    • @Space:他们的代码没有失败。即使在我询问之后,他们也拒绝显示试图移动对象的代码。