簡體   English   中英

為什么 tkinter 網格、列配置和行配置值不會動態變化?

[英]Why tkinter grid, columnconfigure and rowconfigure value not changing dynamically?

請幫助任何人您可以閱讀所有詳細信息作為內聯評論。

我創建了三個類LeftFrameRightFrameDynamicWindow

在 DynamicWindow 我繼承 RightFrame

步驟1

LeftFrame,在第 0 列,最小尺寸為 350

第2步

RightFrame,在第 1 列,權重為 1,捕獲所有可用空間

第 3 步

DynamicWindow, ingering RightFrame, 這是主要問題,請閱讀代碼。

import tkinter as tk
from win32api import GetMonitorInfo, MonitorFromPoint

root = tk.Tk()
monitor_info = GetMonitorInfo(MonitorFromPoint((0, 0)))['Work']
root.geometry(f'{monitor_info[2]}x{monitor_info[3]}')
root.state('zoomed')
root.columnconfigure(0, minsize=350)  # Width of left frame
root.columnconfigure(1, weight=1)  # All available space for right frame
root.rowconfigure(0, weight=1)  # Full screen height for both frame

class LeftFrame(tk.Frame):
    """
    Left Frame
    """
    def __init__(self, container):
        super().__init__(container)
        self.config(bg='red')
        self.grid(row=0, column=0, sticky='nsew')

class RightFrame(tk.Frame):
    """
    Right Frame:
        Divided into three section head frame, middle frame and bottom frame
        head frame contains button
        middle frame contains dynamically changeable frame. Here I am facing problem,
        frame not able to take actual size according to weight and minsize that I given.

    """
    def __init__(self, container):
        super().__init__(container)
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, minsize=30)
        self.rowconfigure(1, weight=1)
        self.rowconfigure(2, minsize=30)
        self.config(bg='green')
        self.grid(row=0, column=1, sticky='nsew')

        self.head_frame = tk.Frame(self, bg='orange')
        self.head_frame.grid(row=0, column=0, sticky='nsew')
        self.head_frame.rowconfigure(0, weight=1)

        self.first_screen_button = tk.Button(self.head_frame, text='First Screen')
        self.first_screen_button.grid(row=0, column=0, sticky='nsew', ipadx=20)

        self.second_screen_button = tk.Button(self.head_frame, text='Second Screen')
        self.second_screen_button.grid(row=0, column=1, sticky='nsew', ipadx=20)

        self.middle_frame = tk.Frame(self, bg='green')
        self.middle_frame.grid(row=1, column=0, sticky='nsew')

        self.bottom_frame = tk.Frame(self, bg='orange')
        self.bottom_frame.grid(row=2, column=0, sticky='nsew')

class DynamicWindow(RightFrame):
    def __init__(self, container):
        super().__init__(container)
        self.first_screen_button.config(command=self.first_screen)
        self.second_screen_button.config(command=self.second_screen)
        self.first_screen()  # I am calling this here becuase on first click on first screen button
        # window don't appear. You can check by comment this code.
        # Can anyone please tell me why first screen not appear on first click.

    def first_screen(self):
        """
        First screen that I want to appear when I click on button
        It has two frame
        """
        for widget in self.middle_frame.winfo_children():  
        # Want to destroy all available widget in middle frame
            widget.destroy()

        self.middle_frame.columnconfigure(0, weight=1)  
        # Configuring size and weight but this is not working properly
        self.middle_frame.columnconfigure(1, minsize=30)
        self.middle_frame.rowconfigure(0, weight=1)
        self.middle_frame.rowconfigure(1, weight=0)

        main_chart_window = tk.Frame(self.middle_frame, bg='#4d4d4d')
        main_chart_window.grid(row=0, column=0, sticky='nsew')

        toolbar = tk.Frame(self.middle_frame, bg='red')
        toolbar.grid(row=0, column=1, sticky='nsew')

    def second_screen(self):
        """
        This is not working in proper way
        I am not able to reconfigure weight of middle frame
        I want this window in full screen in middle frame
        Here you will notice column 1 taking minsize 30, can anyone solve this
        """
        for widget in self.middle_frame.winfo_children():
            widget.destroy()

        self.middle_frame.columnconfigure(0, weight=1)
        self.middle_frame.rowconfigure(0, weight=1)

        second_screen_window = tk.Frame(self.middle_frame, bg='purple')
        second_screen_window.grid(row=0, column=0, sticky='nsew')

left_frame = LeftFrame(root)
dynamic_window = DynamicWindow(root)
root.mainloop()

我讓這一切都為你工作。 我對使用您的代碼/方法失去了興趣,因此,我從頭開始完全重寫了代碼並設計了一種不同的方法。 您強調的所有問題都已解決。 我的代碼結構應該更容易使用。 主要問題是你正在摧毀孩子,但你沒有摧毀孩子所在的column和/或row 。你基本上不能。 即使與destroy()grid_remove() grid_forget()也不會刪除網格單元。

變化:

  • 每個主要小部件都已分離成它自己的 class
  • 名稱已更改以反映每個小部件的實際用途(根據您的示例,盡我所能)
  • 我們通過刪除/重新安裝整個小部件來交換“主顯示”小部件〜而不是銷毀/重新創建它的所有孩子
  • 在按鈕command中使用lambda將所需的“主顯示”傳遞給進行交換的方法
  • 我們從不使用super()來實例化 class。 我們專門通過類名來引用super
  • 所有的argskwargs都被維護了,所以我們可以把我們的自定義小部件當作他們的超級
  • 我們只import我們需要的東西(我的偏好)

評論應該告訴你rest,但如果有混淆,請在評論部分指出,我會做出更詳細的解釋。


小部件.py

from tkinter import Frame, Button
from typing import List, Dict, Callable
from dataclasses import dataclass


#a simple "typedef" for storing menu button data
@dataclass
class MenuData_t:
    func:Callable        #method the command lambda will call
    buttons:List[Dict]   #Button(**kwargs)
    targets:List[Frame]  #'main display' to switch to
    griddata:List[Dict]  #.grid(**kwargs)


'''
this replaces your 'head_frame'
it also provides an interface to concoct all of the buttons that will swap 'main displays'
if you need other types of buttons you will need to manually create them in __init__
considerations have been made in init_displayswap_menu for existing buttons
'''
class MenuFrame(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)

    def init_displayswap_menu(self, md:MenuData_t):
        c = len(self.winfo_children())
        for i, (b, t, g) in enumerate(zip(md.buttons, md.targets, md.griddata)):
            self.__dict__[f'swap_btn{i+1}'] = Button(self, command=lambda m=t: md.func(m), **b)
            self.__dict__[f'swap_btn{i+1}'].grid(row=0, column=i+c, **g)


#this replaces your "bottom_frame"
class Footer(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)


#this replaces your "first_screen"
class PrimaryFrame(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)

        self.chart   = Frame(self, bg='#4d4d4d')
        self.chart.grid(row=0, column=0, sticky='nswe')

        self.toolbar = Frame(self, bg='red')
        self.toolbar.grid(row=0, column=1, sticky='nswe')

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


#this replaces your "second_screen"
class SecondaryFrame(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)


#this replaces your "LeftFrame"
class Sidebar(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)


#this replaces your "RightFrame" AND "DynamicWindow"
class MainFrame(Frame):
    def __init__(self, master, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)

        ##INSTANTIATE
        #menu
        self.menu = MenuFrame(self, bg='orange')
        self.menu.grid(row=0, column=0, sticky='nsew')

        #main display
        self.current   = None #for storing currently used 'main display'

        self.primary   = PrimaryFrame(self, bg='green')
        self.secondary = SecondaryFrame(self, bg='purple')

        #footer
        self.footer = Footer(self, bg='orange')
        self.footer.grid(row=2, column=0, sticky='nsew')

        ##UTILIZE
        #concoct main display swap menu
        '''    
        append accordingly to the 3 lists to create more buttons that will switch frames
        done this way so button creation can remain in MenuFrame but use remote data
        row, column and command are managed in MenuFrame
        '''
        self.menu.init_displayswap_menu(MenuData_t(
            self.main_display,              #method the command lambda will call
            [{'text':'Primary'},            #Button(**kwargs)
             {'text':'Secondary'},
            ],
            [self.primary,                  #'main display' to switch to
             self.secondary,
            ],
            [{'sticky':'nswe','ipadx':20},  #.grid(**kwargs)
             {'sticky':'nswe','ipadx':20},
            ]
        ))

        #init main display
        '''
        I could have called main_display directly but this illustrates 2 things
        1: how to virtually click a button
        2: how to access the buttons that MenuFrame created in it's __dict__
        '''
        self.menu.swap_btn1.invoke()

        #configure grid
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, minsize=30)
        self.grid_rowconfigure(1, weight=1)
        self.grid_rowconfigure(2, minsize=30)

    #replaces your 'first_screen' AND 'second_screen' methods
    def main_display(self, frame):
        if self.current is not frame:       #only swap if we aren't requesting the current 'main display'
            if self.current: 
                self.current.grid_remove()  #remove current from the grid, instead of destroy
    
            self.current = frame            #set new current and add it to the grid
            self.current.grid(row=1, column=0, sticky='nsew')
 

主文件

from win32api import GetMonitorInfo, MonitorFromPoint
from widgets import Sidebar, MainFrame
from tkinter import Tk


#This is your "root"
class Application(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        Sidebar(self, bg='red').grid(row=0, column=0, sticky='nswe')
        MainFrame(self, bg='black').grid(row=0, column=1, sticky='nswe')

        #configure grid
        self.grid_columnconfigure(0, minsize=350)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)


#kick off the entire app with proper PEP8
if __name__ == '__main__':
    monitor_info = GetMonitorInfo(MonitorFromPoint((0, 0)))['Work']

    app = Application()
    app.title("Manish Pushpam's Bad-Ass Application")
    app.geometry(f'{monitor_info[2]}x{monitor_info[3]}')
    app.minsize(800, 600)
    app.mainloop()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM