tkinter,将交互式网格集成到页面上

时间:2018-07-25 16:46:54

标签: python tkinter

我的最终目标是创建战舰游戏(还有很长的路要走)。首先,我试图掌握多个带有各种小部件的窗口以及在窗口之间切换的能力。

当前,我正在尝试将游戏板放置在可以单击以更改颜色的页面之一上(当玩家单击图块来猜测船的位置时,将发生这种情况的开始)。但是,该板从我的主窗口中分离出来打开,单击该板会引发以下错误:

  

文件“ C:/Users/thani/Desktop/Code/Py_Projects/Boiler_Plate.py”,第109行,在       L.bind('',lambda e,i = i,j = j:on_click(i,j,e))   NameError:名称“ on_click”未定义

我在开发板上使用的代码部分将独立工作:

#import tkinter as tk is at the beginning

board =[ [None]*10 for _ in range(10) ]
counter = 0
root = tk.Tk()

def on_click(i,j,event):
    global counter
    color = "red" if counter % 2 else "black"
    event.widget.config(bg=color)
    board[i][j] = color
    counter += 1

for i, row in enumerate(board):
    for j, column in enumerate(row):
        L = tk.Label(root, text='    ', bg='grey')
        L.grid(row=i, column=j)
        L.bind('<Button-1>', lambda e, i=i, j=j: on_click(i,j,e))

#root.mainloop() is at the end

但不集成到页面中时:

import tkinter as tk

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

        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand=True)

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

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):

            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nesw")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

#Start page and page one omitted as they are not relevant to the issue of pagetwo's grid 

class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Page Two")
        label.grid(row=0,column=1)

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

        P2button1 = tk.Button(self, text="back to home",
                            command= lambda: controller.show_frame(StartPage))
        P2button1.grid(row=1, column=1)
        #P2button1.pack()

    board =[ [None]*10 for _ in range(10) ]
    counter = 0
    root = tk.Tk()

    def on_click(i,j,event):
        global counter
        color = "red" if counter % 2 else "black"
        event.widget.config(bg=color)
        board[i][j] = color
        counter += 1

    for i, row in enumerate(board):
        for j, column in enumerate(row):
            L = tk.Label(root, text='    ', bg='grey')
            L.grid(row=i, column=j)
            L.bind('<Button-1>', lambda e, i=i, j=j: on_click(i,j,e))


app = Grid_Test()
app.geometry("500x500")
app.mainloop()
root.mainloop()

因此,我认为问题出在以下事实,即董事会不在第二页中。考虑到这一点,我想我对tkinter中的小部件如何工作缺少一些重要的理解。

我的问题是,如何将网格集成到窗口中?

我的完整代码在这里:

import tkinter as tk

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

        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand=True)

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

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):

            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nesw")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent,bg="red")
        label = tk.Label(self, text="start page")
        label.grid(row=0,column=1)
        self.grid_rowconfigure(1,weight=1)
        self.grid_columnconfigure(1,weight=1)

        button1 = tk.Button(self, text="Go to Page 1",
                            command=lambda: controller.show_frame(PageOne))
        button1.grid(row=0, column=1)

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent,bg="white")
        label = tk.Label(self, text="Page One",bg="blue")
        label.grid(row=0,column=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.create_text()

        P1_button1 = tk.Button(self, text="back to home",
                            command= lambda: controller.show_frame(StartPage))
        P1_button1.grid(row=1,column=1)

        P1_button2 = tk.Button(self, text="Continue to page 2",
                               command= lambda: controller.show_frame(PageTwo))
        P1_button2.grid(row=2, column=1)

    def create_text(self):
        self.textbox = tk.Text(self.master, height=10, width=79, wrap='word')
        vertscroll = tk.Scrollbar(self.master)
        vertscroll.config(command=self.textbox.yview)
        self.textbox.config(yscrollcommand=vertscroll.set)
        self.textbox.grid(column=0, row=0)
        vertscroll.grid(column=2, row=0, sticky='NS')


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Page Two")
        label.grid(row=0,column=1)

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

        P2button1 = tk.Button(self, text="back to home",
                            command= lambda: controller.show_frame(StartPage))
        P2button1.grid(row=1, column=1)

    board =[ [None]*10 for _ in range(10) ]
    counter = 0
    root = tk.Tk()

    def on_click(i,j,event):
        global counter
        color = "red" if counter % 2 else "black"
        event.widget.config(bg=color)
        board[i][j] = color
        counter += 1

    for i, row in enumerate(board):
        for j, column in enumerate(row):
            L = tk.Label(root, text='    ', bg='grey')
            L.grid(row=i, column=j)
            L.bind('<Button-1>', lambda e, i=i, j=j: on_click(i,j,e))


app = Grid_Test()
app.geometry("500x500")
app.mainloop()
root.mainloop()

2 个答案:

答案 0 :(得分:0)

根据定义,全局变量根本不会缩进...因此它们不能成为类的一部分。另外,您需要使用实例变量来解决类中的问题,例如self.onclick而不是onclick。试试这个:

counter = 0
board =[ [None]*10 for _ in range(10) ]
class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Page Two")
        label.grid(row=0,column=1)

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

        P2button1 = tk.Button(self, text="back to home",
                            command= lambda: controller.show_frame(StartPage))
        P2button1.grid(row=1, column=1)

        for i, row in enumerate(board):
            for j, column in enumerate(row):
                L = tk.Label(self, text='    ', bg='grey')
                L.grid(row=i, column=j)
                L.bind('<Button-1>', lambda e, i=i, j=j: self.on_click(i,j,e))

    def on_click(self, i,j,event):
        global counter
        color = "red" if counter % 2 else "black"
        event.widget.config(bg=color)
        board[i][j] = color
        counter += 1

除非在极少数情况下,否则您永远都不应将代码放在类中,而应将代码不属于方法的一部分。

我测试了它...可以正常工作,但是文本和Button搞砸了您的布局。您应该将网格移动到它自己的框架:

gridframe = tk.Frame(self)
gridframe.grid(columnspan=2)
for i, row in enumerate(board):
    for j, column in enumerate(row):
        L = tk.Label(gridframe, text='    ', bg='grey')
        L.grid(row=i, column=j)
        L.bind('<Button-1>', lambda e, i=i, j=j: self.on_click(i,j,e))

答案 1 :(得分:0)

好,所以我看到的问题通常很容易纠正。首先,您永远不想运行多个Tk()实例。在代码中,您有一个继承自Tk()且在PageTwo之后的类,您还尝试运行Tk()的第二个实例。实际上这是不需要的,应该删除。

我们可以将on_click函数移到您的类中,然后将变量转换为类属性,而不是使用某些函数并尝试使用global函数。这将使我们无需全局即可直接使用属性。

在您的GridTest类中,您使用的是pack(),这在混合几何图形管理器时可能会并且会引起问题。相反,让我们在PageTwo中添加2帧,然后对所有内容进行网格化。这样,我们可以为标签和导航按钮设置顶部框架,为游戏网格设置底部框架。

看看下面的代码,如果您有任何疑问,请告诉我。

import tkinter as tk

class GridTest(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.geometry("500x500")
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        container = tk.Frame(self)
        container.grid(row=0, column=0,columnspan=10, rowspan=10, sticky="nsew")
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):

            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nesw")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent,bg="red")
        label = tk.Label(self, text="start page")
        label.grid(row=0,column=1)
        self.grid_rowconfigure(1,weight=1)
        self.grid_columnconfigure(1,weight=1)

        button1 = tk.Button(self, text="Go to Page 1",
                            command=lambda: controller.show_frame(PageOne))
        button1.grid(row=0, column=1)

class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent,bg="white")
        tk.Label(self, text="Page One",bg="blue").grid(row=0,column=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.create_text()

        tk.Button(self, text="back to home",
                  command= lambda: controller.show_frame(StartPage)).grid(row=1,column=1)
        tk.Button(self, text="Continue to page 2",
                  command= lambda: controller.show_frame(PageTwo)).grid(row=2, column=1)

    def create_text(self):
        self.textbox = tk.Text(self.master, height=10, width=79, wrap='word')
        vertscroll = tk.Scrollbar(self.master)
        vertscroll.config(command=self.textbox.yview)
        self.textbox.config(yscrollcommand=vertscroll.set)
        self.textbox.grid(column=0, row=0)
        vertscroll.grid(column=2, row=0, sticky='NS')


class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        top_frame = tk.Frame(self)
        top_frame.grid(row=0, column=0, sticky="ew")
        bottom_frame = tk.Frame(self)
        bottom_frame.grid(row=1, column=0, sticky="nsew")

        tk.Label(top_frame, text="Page Two").grid(row=0,column=0)
        top_frame.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)

        tk.Button(top_frame, text="back to home",
                  command= lambda: controller.show_frame(StartPage)).grid(row=1, column=0)

        self.board =[ [None]*10 for _ in range(10) ]

        self.counter = 0

        for i, row in enumerate(self.board):
            for j, _ in enumerate(row):
                bottom_frame.rowconfigure(i, weight=1)
                bottom_frame.columnconfigure(i, weight=1)
                L = tk.Label(bottom_frame, text='    ', bg='grey')
                L.grid(row=i, column=j, sticky="nsew")
                L.bind('<Button-1>', lambda e, i=i, j=j: self.on_click(i,j,e))

    def on_click(self, i,j,event):
        color = "red" if self.counter % 2 else "black"
        event.widget.config(bg=color)
        self.board[i][j] = color
        self.counter += 1


app = GridTest()
app.mainloop()

结果是一个具有游戏网格的PageTwo,它可以随窗口调整大小。

enter image description here