使用Tkinter矩形

时间:2015-06-05 03:41:32

标签: python tkinter tkinter-canvas

我正在尝试模拟一个网格,就像使用tkinter矩形被绘制到画布上的游戏板一样,但是我无法正确地创建一个循环。

我有一个变量,它包含一定数量的网格行和一个变量作为列数,我需要根据那些配置创建一个矩形网格,这些配置可以在应用程序运行时随时更改,这对我来说有点棘手。

无论如何,我目前有一个功能可以像这样在屏幕上绘制一个矩形:

def _place_empty_tiles(self):
        self._canvas.update()
        width = self._canvas.winfo_width()
        height = self._canvas.winfo_height()
        self.x = width / self._game.columns
        self.y = height / self._game.rows
        for i in range(self._game.columns):
            click_point = point.from_pixel(self.x, self.y, width, height)
            self._state.handle_click(click_point)
            self.x += 60

    def _redraw_game_pieces(self)->None:
        '''Delete and redraw all the  of game pieces'''
        self._canvas.delete(tkinter.ALL)
        canvas_width = self._canvas.winfo_width()
        canvas_height = self._canvas.winfo_height()

        for tile in self._state.all_tiles():
            center_x, center_y = tile.center().pixel(canvas_width, canvas_height)

            radius_x = tile.radius_frac() * canvas_width
            radius_y = tile.radius_frac() * canvas_height

            self._canvas.create_rectangle(
                center_x - radius_x, center_y - radius_y,
                center_x + radius_x, center_y + radius_y,
                fill = '#006000', outline = '#000000')

你可能会注意到我有一些自定义坐标转换方法,以补偿屏幕重新调整大小。但是,我的问题是在for循环中的函数_place_empty_tiles(self)下。我们假设列数为4,它将在画布上打印出以下内容:enter image description here

然而,

  1. 它实际上正在排一行,
  2. 这只是一行。
  3. 如何在self._game.columnsself._game.rows存储行数和列数的情况下创建类似样式的网格?

    更新: 尝试将它关闭The tkinter Knight's Tour Demo我重写了这样的函数:

    def _place_empty_tiles(self):
        self._canvas.update()
        width = self._canvas.winfo_width()
        height = self._canvas.winfo_height()
        for r in range(self._game.rows -1,-1,-1):
            for c in range(self._game.columns):
                click_point = point.from_pixel(c*30+4, r*30+4, c*30+30, r*30+30)
                self._state.handle_click(click_point)
        self._redraw_game_pieces()
    

    虽然很接近,但仍有一些意想不到的结果: enter image description here

2 个答案:

答案 0 :(得分:3)

Tk演示包括绘制棋盘的Knight's Tour演示。有人converted it for tkinter你可以检查_draw_board函数,看看如何使用画布矩形创建这样的布局的例子:

def draw_board(canvas):  
    # draw checkerboard
    for r in range(7, -1, -1):
        for c in range(8):
            if c&1 ^ r&1:
                fill = 'tan3'
                dfill = 'tan4'
            else:
                fill = 'bisque'
                dfill= 'bisque3'
            coords = (c*30+4, r*30+4, c*30+30, r*30+30)
            canvas.create_rectangle(coords, fill=fill, disabledfill=dfill,
                                    width=2, state='disabled')

答案 1 :(得分:1)

这是一个绘制尽可能贴近窗口的网格的示例。它被设计为在窗口调整大小时重绘自身。如果您不想要这种行为,可以对单元格的宽度和高度进行硬编码。

单元格存储在由行和列索引的字典中,以便于引用每个图块。单击图块将在蓝色和红色之间切换。请注意,当窗口调整大小时,它将不记得以前单击过哪些单元格。如果你愿意,这很容易解决。

import Tkinter as tk

class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.canvas = tk.Canvas(self, width=500, height=500, borderwidth=0, highlightthickness=0)
        self.canvas.pack(side="top", fill="both", expand="true")
        self.rows = 20
        self.columns = 20
        self.tiles = {}
        self.canvas.bind("<Configure>", self.redraw)
        self.status = tk.Label(self, anchor="w")
        self.status.pack(side="bottom", fill="x")

    def redraw(self, event=None):
        self.canvas.delete("rect")
        cellwidth = int(self.canvas.winfo_width()/self.columns)
        cellheight = int(self.canvas.winfo_height()/self.columns)
        for column in range(self.columns):
            for row in range(self.rows):
                x1 = column*cellwidth
                y1 = row * cellheight
                x2 = x1 + cellwidth
                y2 = y1 + cellheight
                tile = self.canvas.create_rectangle(x1,y1,x2,y2, fill="blue", tags="rect")
                self.tiles[row,column] = tile
                self.canvas.tag_bind(tile, "<1>", lambda event, row=row, column=column: self.clicked(row, column))

    def clicked(self, row, column):
        tile = self.tiles[row,column]
        tile_color = self.canvas.itemcget(tile, "fill")
        new_color = "blue" if  tile_color == "red" else "red"
        self.canvas.itemconfigure(tile, fill=new_color)
        self.status.configure(text="you clicked on %s/%s" % (row, column))

if __name__ == "__main__":
    app = App()
    app.mainloop()