0

首先,我对使用 Tkinter 非常陌生,

我遇到的问题是,如果我只有一种对象类型,我的代码就可以工作。如果它是该标签类型中唯一的一种,它将正确交互。因此,如果每次执行时我都有一艘“船”和 100 个“炮弹”,它就会正确执行。

该代码检测两个对象之间是否存在碰撞,然后将当前选定项目的颜色更改为随机颜色。因此,只要当前只有一种标签类型,它就可以正常工作。因此,如果我单击并将“船”拖到“外壳”中,它将切换它的颜色。然后,如果我取 100 个外壳中的 1 个并做同样的事情,我会收到此错误。

我不明白为什么当只有一个给定类型的对象并且与无限数量的其他对象交互时它可以正常工作,但是当有多个标签类型时它会失败。

它正确选择了所选对象的 ID 号,所以我现在迷路了,感谢任何帮助。

以下是我收到的错误和我正在使用的代码。它只是执行所需任务所需的重要部分。碰撞代码与代码中的相同。


Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python34\lib\tkinter\__init__.py", line 1487, in __call__
    return self.func(*args)
  File "H:/Charles Engen/PycharmProjects/Battleship/BattleShipGUI.py", line 112, in on_token_motion
    self.collision_detection(event)
  File "H:/Charles Engen/PycharmProjects/Battleship/BattleShipGUI.py", line 85, in collision_detection
    self.canvas.itemconfig(current_token, outline=_random_color())
  File "C:\Python34\lib\tkinter\__init__.py", line 2385, in itemconfigure
    return self._configure(('itemconfigure', tagOrId), cnf, kw)
  File "C:\Python34\lib\tkinter\__init__.py", line 1259, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown option "22"

import tkinter as tk
from random import randint

HEIGHT = 400
WIDTH = 680

def _random_color():
    '''Creates a random color sequence when called'''
    random_color = ("#"+("%06x" % randint(0, 16777215)))
    return random_color

class Board(tk.Tk):
    '''Creates a Board Class'''

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.menu_item = tk.Menu(self)
        self.file_menu = tk.Menu(self.menu_item, tearoff=0)
        self.file_menu.add_command(label='New', command=self.new_game)
        self.file_menu.add_command(label='Exit', command=self.quit)

        self.config(menu=self.file_menu)

        self.canvas = tk.Canvas(width=WIDTH, height=HEIGHT)
        self.canvas.pack(fill='both', expand=True)

        # adds variable that keeps track of location
        self._token_location_data = {"x": 0, "y": 0, "item": None}


    def _create_object(self, coord, fcolor, color, token_name):
        '''Creates an object with a tag, each object is assigned the ability to be clicked and dragged'''
        (x, y) = coord
        if token_name == 'boat':
            points = [10+x, 10+y, 20+x, 20+y, 110+x, 20+y, 120+x, 10+y, 80+x,
                      10+y, 80+x, 0+y, 60+x, 0+y, 60+x, 10+y]
            self.canvas.create_polygon(points, outline=fcolor, fill=color, width=3, tag=token_name)
        elif token_name == 'shell':
            self.canvas.create_oval(0+x, 0+y, 10+x, 10+y, outline=fcolor, fill=color, width=3, tag=token_name)
        self.canvas.tag_bind(token_name, '<ButtonPress-1>', self.on_token_button_press)
        self.canvas.tag_bind(token_name, '<ButtonRelease-1>', self.on_token_button_press)
        self.canvas.tag_bind(token_name, '<B1-Motion>', self.on_token_motion)

    def collision_detection(self, event):
        '''This function tracks any collision between the boat and shell objects'''
        # I will upgrade this to take any object collision
        token = self.canvas.gettags('current')[0]
        current_token = self.canvas.find_withtag(token)

        x1, y1, x2, y2 = self.canvas.bbox(token)
        overlap = self.canvas.find_overlapping(x1, y1, x2, y2)

        for item in current_token:
            for over in overlap:
                if over != item:
                    # Changes the color of the object that is colliding.
                    self.canvas.itemconfig(current_token, outline=_random_color())

    # The following three functions are required to just move the tokens
    def on_token_button_press(self, event):
        '''Adds ability to pick up tokens'''
        # Stores token item's location data
        self._token_location_data['item'] = self.canvas.find_closest(event.x, event.y)[0]
        self._token_location_data['x'] = event.x
        self._token_location_data['y'] = event.y

    def on_token_button_release(self, event):
        '''Adds ability to drop token'''
        # Resets the drag
        self._token_location_data['item'] = self.canvas.find_closest(event.x, event.y)
        self._token_location_data['x'] = event.x
        self._token_location_data['y'] = event.y

    def on_token_motion(self, event):
        '''Adds ability to keep track of location of tokens as you drag them'''
        # Computes how much the object has moved
        delta_x = event.x - self._token_location_data['x']
        delta_y = event.y - self._token_location_data['y']
        # move the object the appropriate amount
        self.canvas.move(self._token_location_data['item'], delta_x, delta_y)
        # record the new position
        self._token_location_data['x'] = event.x
        self._token_location_data['y'] = event.y
        # Detects collision between objects
        self.collision_detection(event)

    def new_game(self):
        '''Creates new game by deleting the current canvas and creating new objects'''
        # Deletes current canvas
        self.canvas.delete('all')
        # runs the create board mechanism
        self._generate_board()
        # adds code to create a shell and boat on the screen from the appropriate function
        for i in range(1):
            self._create_object((410, 15*i), _random_color(), _random_color(), 'boat')
        for i in range(4):
            for j in range(10):
                self._create_object((590+(i*10), 15*j), _random_color(), _random_color(), 'shell')

def main():
    app = Board()
    app.mainloop()

if __name__ == '__main__':
    main()
4

1 回答 1

2

为了运行代码,我删除了对不存在的._generate_board. 在此之后,我得到了同样的错误,但未知选项'3'。

异常是由传递给self.canvas.itemconfig您误导性地调用的 id 元组current_token而不是标签或 id 引起的。由于_flatten调用,单例是可以容忍的,但任何更多都会成为错误。我相当肯定,'3' 是 shell 元组的第二个成员并不是巧合。通过token而不是停止异常。itemconfig此外,在第一次调用之后应该有一个休息时间。

然而,这样一来,壳被视为一个组,并且边界框包含所有壳并overlap包括所有壳。这就是为什么将单个外壳从其他外壳移开被视为碰撞的原因。此时,如果一个贝壳被移动,所有贝壳都将随机变为一种新颜色。要解决此问题,应将 token 设置为 中设置的单个项目on_token_button_press,而不是标记组。这实现了您的待办事项。这是结果。

def collision_detection(self, event):
    '''Detect collision between selected object and others.'''
    token = self._token_location_data['item']
    x1, y1, x2, y2 = self.canvas.bbox(token)
    overlap = self.canvas.find_overlapping(x1, y1, x2, y2)
    for over in overlap:
        if over != token:
            # Changes the color of the object that is colliding.
            self.canvas.itemconfig(token, outline=_random_color())
            break

一个小问题是,您为每个 shell 执行了“shell”的标签绑定(_create_objectnew.

for tag in 'boat', 'shell':
    self.canvas.tag_bind(tag, '<ButtonPress-1>', self.on_token_button_press)
    self.canvas.tag_bind(tag, '<ButtonRelease-1>', self.on_token_button_press)
    self.canvas.tag_bind(tag, '<B1-Motion>', self.on_token_motion)
于 2015-04-09T20:10:47.377 回答