如何创建一个显示网格并允许用户选择和填充单个单元格的 python GUI?

问题描述 投票:0回答:1

一些背景知识:

我正在学习基于代理的建模,并且刚刚使用 Mesa 包实现了康威的生命游戏。我读过该模型的初始配置,这些配置产生了有趣的现象,并且想要重新创建它们(即 Gosper 滑翔机枪、R-pentomino 等)

我认为一个有趣的学习项目是创建一个显示网格的 GUI 应用程序,并允许用户选择他们想要用来实例化模型的单元格。

问题:

我希望显示一个包含网格的 GUI,如下所示: Grid

然后用户可以单击他们想要填充的单元格,它将显示如下:Grid w/ Selections

然后,当用户单击“保存状态”按钮(或按指定的结束键)时,他们的选择将保存到 numpy 数组中以实例化模型。

我不知道从哪里开始。如果有人能指出我正确的方向,我将不胜感激。

python user-interface mesa-abm
1个回答
0
投票

差不多 5 年后,我有同样的想法并使用 tkinter 解决了。只是在这里留下一个(未优化的)演示,以防其他人经过:

import tkinter
from tkinter import *


class Universe:

    def __init__(self, w: int, h: int, seed: [(int, int)]):
        self.max_row = h
        self.max_col = w

        self.board = []
        for _ in range(h):
            self.board.append([0] * w)

        self.seed(seed)

    def seed(self, positions: [(int, int)]):
        for x, y in positions:
            self.board[y][x] = 1

    def update(self):
        temp = [[0]*self.max_col for _ in range(self.max_row)]

        for r in range(self.max_row):
            for c in range(self.max_col):
                neighbors = self.count_neighbors(r, c)

                # underpopulation
                if self.board[r][c] == 1 and neighbors < 2:
                    temp[r][c] = 0

                # next generation
                elif self.board[r][c] == 1 and neighbors == 2 or neighbors == 3:
                    temp[r][c] = 1

                # overpopulation
                elif self.board[r][c] == 1 and neighbors > 3:
                    temp[r][c] = 0

                # reproduction
                elif self.board[r][c] == 0 and neighbors == 3:
                    temp[r][c] = 1

                else:
                    temp[r][c] = self.board[r][c]

        self.board = temp

    def count_neighbors(self, y: int, x: int) -> int:
        n = 0
        n += self.board[y - 1][x - 1] if (y - 1 >= 0) and (x - 1 >= 0) else 0
        n += self.board[y - 1][x] if (y - 1 >= 0) else 0
        n += self.board[y - 1][x + 1] if (y - 1 >= 0) and (x + 1 < self.max_col) else 0
        n += self.board[y][x - 1] if (x - 1 >= 0) else 0
        n += self.board[y][x + 1] if (x + 1 < self.max_col) else 0
        n += self.board[y + 1][x - 1] if (y + 1 < self.max_row) and (x - 1 >= 0) else 0
        n += self.board[y + 1][x] if (y + 1 < self.max_row) and (x + 1 < self.max_col) else 0
        n += self.board[y + 1][x + 1] if (y + 1 < self.max_row) and (x + 1 < self.max_col) else 0
        return n


class UI:

    def __init__(self, universe:Universe):
        self.cells = [[]]
        self.universe = universe

        window = Tk()
        window.minsize(width=500, height=500)

        canvas = Canvas(width=500, height=500)
        self.fill_canvas(canvas)
        canvas.pack()

        button = Button(text='go', command=self.button_clicked, width=5, height=2)
        button.pack()

        window.mainloop()

    def button_clicked(self):
        self.universe.update()
        for i in range(self.universe.max_row):
            for j in range(self.universe.max_col):
                self.cells[i][j].configure(background='white' if self.universe.board[i][j] == 0 else 'black')

    def fill_canvas(self, canvas: tkinter.Canvas):
        for i in range(self.universe.max_row):
            self.cells.append([])
            for j in range(self.universe.max_col):
                e = Entry(canvas, width=2, background='white' if self.universe.board[i][j] == 0 else 'black')
                e.grid(row=i, column=j)
                self.cells[i].append(e)


# test with penta-decathlon oscillator
ui = UI(
    Universe(20, 20,
             [(4, 6), (4, 11), (5, 4), (5, 5), (5, 7), (5, 8), (5, 9), (5, 10), (5, 12), (5, 13), (6, 6), (6, 11)]))
© www.soinside.com 2019 - 2024. All rights reserved.