使用 Tkinter 中的菜单栏在页面之间切换
Posted
技术标签:
【中文标题】使用 Tkinter 中的菜单栏在页面之间切换【英文标题】:Switch from page to page with Menubar in Tkinter 【发布时间】:2020-06-25 15:38:56 【问题描述】:我的 tkinter IHM 有一些问题: 我希望能够通过始终保持原位的菜单栏从一帧切换到另一帧。 我尝试了 Bryan Oakley 的方法。可以使用简单的小部件页面,但不能使用涉及更复杂小部件的页面。
在我的示例中:ZoomConstructorPage 始终可见,不会被其他页面隐藏。 此外,我不知道如何将 Zoom_constructor_Frame 小部件包含在 PanedWindow 中。
我希望在打开应用程序时显示 StartPage,然后在单击“Charger un jeu de données”时切换到 PageTwo,或者在单击“Définir un zoom”时在 PanedWindow 中显示 ZoomConstructorPage
我是 tkinter 的新手,非常感谢任何帮助。
这是我的主要代码:`
import tools
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkinter import filedialog
###############################################################################
class MainWindow(tools.FullscreenWindow):
""" Fenêtre principale """
def __init__(self, title, *args, **kwargs):
super().__init__(*args, **kwargs)
self.wm_title(title)
# self.iconbitmap('drone.ico')
container = ttk.Frame(self)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
# Affiche la Barre de Menu
'''MenuBar(self).grid()
self.columnconfigure(0, weight=1)'''
###############################################################################
# Barre de Menu principal
menu_bar = tk.Menu(self)
menu_bar.add_command(label='Traiter nouveau fichier',
command=self.explorer_fichier)
menu_bar.add_command(label='Charger un jeu de données',
command=self.charger_data)
menu_bar.add_command(label='Définir un zoom',
command=self.definir_zoom)
# Sous menu liste des enregistrements
submenu1 = tk.Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label='Enregistrements', menu=submenu1)
submenu1.add_command(label='Liste complète')
# Quitter l'application
menu_bar.add_command(label='Quitter',
command=self.close_application)
# Affiche le Menu
self.config(menu=menu_bar)
###############################################################################
# Déroule les différentes pages
self.frames =
for F in (StartPage, ZoomConstructorPage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def inscrire(self):
messagebox.showinfo("Info", "Wait a minute")
def explorer_fichier(self):
file_path = filedialog.askopenfilename()
print(file_path)
def close_application(self):
reponse = messagebox.askyesnocancel("Quit",
"êtes vous sûr de vouloir quitter l'application ?")
if reponse:
self.destroy()
def charger_data(self):
self.show_frame(PageTwo)
def definir_zoom(self):
self.show_frame(ZoomConstructorPage)
class Zoom_constructor_Frame(ttk.Frame):
""" Frame et controller des 2 Scales qui permettent de définir une section
zoommée"""
def __init__(self, parent, xmin, xmax, *args, **kwargs):
super().__init__(*args, **kwargs)
self.parent = parent
label = ttk.Label(text='Choisir le début et la fin de la section à zoomer')
self.start_slider = tools.Slider(parent, self, 'start', xmin, xmin, xmax,
name='Début du zoom')
self.end_slider = tools.Slider(parent, self, 'end', xmax, xmin, xmax,
name='Fin du zoom')
label.grid(column=0, row=0, pady=5)
self.start_slider.grid(column=0, row=1, pady=8)
self.end_slider.grid(column=0, row=2, pady=8)
def slider_command(self, command_parameter):
""" commande les deux sliders """
# Récupère les valeurs des 2 sliders
self.get_sliders_value()
print('\nentrée', command_parameter, self.new_start_value, self.new_end_value)
if command_parameter == 'start':
self.border_start_value()
if command_parameter == 'end':
self.border_end_value()
# Récupére les valeurs éventuellement actualisées
self.get_sliders_value()
def get_sliders_value(self):
""" Récupère les valeurs actualisées des 2 sliders """
self.new_start_value = self.start_slider.value.get()
self.new_end_value = self.end_slider.value.get()
def border_start_value(self):
""" Limite la valeur de start_value à end_value - 1 """
if self.new_start_value > self.new_end_value:
self.start_slider.value.set(self.new_end_value - 1)
def border_end_value(self):
"""Limite la valeur de end_value à start_value + 1 """
if self.new_start_value > self.new_end_value:
self.end_slider.value.set(self.new_start_value + 1)
###############################################################################
# ***** PAGES *****
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
label = tk.Label(self, text="Start Page This is a Start Page")
label.pack(pady=10, padx=10)
class ZoomConstructorPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
# Valeurs à définir avec la data
self.start_value = 0
self.end_value = 5850
label = tk.Label(self, text="This is Zoom ConstructPage")
label.grid(column=0, row=0, pady=8)
# Affiche la fenêtre de gauche
my_paned_window_1 = tk.PanedWindow()
my_paned_window_1.grid(column=1, row=1)
my_paned_window_2 = tk.PanedWindow(my_paned_window_1, orient=tk.VERTICAL)
my_paned_window_1.add(my_paned_window_2)
self.left_frame = Zoom_constructor_Frame(my_paned_window_2,
self.start_value,
self.end_value,
height=400, width=500,
relief='groove', borderwidth=4)
my_paned_window_2.add(self.left_frame)
bottom_pane_text = tk.Text(my_paned_window_2, height=30, width=60)
my_paned_window_2.add(bottom_pane_text)
right_pane_text = tk.Text(my_paned_window_1, height=30, width=3)
my_paned_window_1.add(right_pane_text)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two")
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(ZoomConstructorPage))
button2.pack()
###############################################################################
if __name__ == '__main__':
app = MainWindow(title='Instrument analyser')
app.mainloop()
还有一些来自工具模块的类:
class FullscreenWindow(tk.Tk):
""" Une Window qui se met directement en plein écran (wm_state = 'zoomed')
et que l'on peut réduire par touche "escape", rezoomer par F11 """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#self.parent = parent
self.initUI()
def initUI(self):
#self.pack(fill="both", expand=True, side="top")
self.grid()
self.bind("<F11>", self.fullscreen_toggle)
self.bind("<Escape>", self.fullscreen_cancel)
self.fullscreen_toggle()
def fullscreen_toggle(self, event="none"):
self.wm_state('zoomed')
self.focus_set()
#self.parent.wm_attributes("-topmost", 1)
def fullscreen_cancel(self, event="none"):
self.reduceWindow()
self.wm_state('normal')
#self.parent.wm_attributes("-topmost", 0)
def reduceWindow(self):
""" Récupére largeur et hauteur d'écran. Réduit la taille de la
fenêtre à 70% """
sw = self.winfo_screenwidth()
sh = self.winfo_screenheight()
w = sw*0.7
h = sh*0.7
x = (sw-w)/2
y = (sh-h)/2
self.geometry("%dx%d+%d+%d" % (w, h, x, y))
class Slider(tk.Frame):
""" Associe un Entry et un Scale, dont les valeurs sont liées à un
IntVar unique : value. Les modifications de value sont pris en compte en
temps réel et retournées au widget controller par la fonction slider_command """
def __init__(self, parent, controller, command_parameter,
start, xmin, xmax,
orientation='horizontal', name='Slider', *args, **kwargs):
super().__init__(*args, **kwargs)
if start < xmin or start > xmax:
raise ValueError("Start is out of bounds")
self.parent = parent
self.controller = controller
self.command_parameter = command_parameter
self.start = start
self.min = xmin
self.max = xmax
self.orientation = orientation
self.name = name
self.value = tk.IntVar()
# Calcul de l'intervalle entre 2 ticks pour le Scale
nb_data = self.max - self.min
nb_interval = 5
tickinterval = tick_interval(nb_data, nb_interval)
# Création des deux widgets
self.entry = ttk.Entry(self, width= 10, textvariable=self.value)
self.slider = tk.Scale(self,
from_=self.min,
to=self.max,
orient=self.orientation,
resolution=1,
tickinterval=tickinterval,
length=300,
variable=self.value,
label=self.name,
command = self.slider_command)
# Initialisation de la valeur de départ
self.slider.set(self.start)
# Affichage des widgets
self.entry.grid(column=0, row=0, sticky='e', padx=10)
self.slider.grid(column=0, row=1, columnspan=1, padx=8)
#self.entry.pack()
#self.slider.pack()
def slider_command(self, value):
self.controller.slider_command(self.command_parameter)
def tick_interval(x, nb_ticks=10):
''' Calcul l'intervalle entre 2 ticks pour un ensemble de x data, arrondi
à la puissance de 10 la plus proche '''
# intervalle réel
interval = x/nb_ticks
# nombre de chiffres de l'intervalle
nb_digit = len(str(interval))
# puissance de dix la plus proche
power = 10**(nb_digit - 3)
# intervalle arrondi à la puissance de 10 la plus proche
interval = round(interval/power) * power
return interval
【问题讨论】:
Zoom_constructor_Frame
没有正确调用超类__init__()
(您省略了parent
参数),它正在父级(或未指定父级)而不是自身中创建小部件.
这个问题一次想问太多东西,代码太长了。请尝试一次只关注一个问题,并创建一个minimal reproducible example。
【参考方案1】:
在我的示例中:ZoomConstructorPage 始终可见,不会被其他页面隐藏。此外,我不知道如何将 Zoom_constructor_Frame 小部件包含在 PanedWindow 中。
那是因为您没有将父参数传递给超类的__init__
。因为您不这样做,所以框架默认位于根窗口中。
应该是这样的:
class Zoom_constructor_Frame(ttk.Frame):
def __init__(self, parent, xmin, xmax, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
...
此外,我不知道如何将 Zoom_constructor_Frame 小部件包含在 PanedWindow 中。
你似乎做得对。它不起作用的事实可能与没有将父级传递给super.__init__()
【讨论】:
以上是关于使用 Tkinter 中的菜单栏在页面之间切换的主要内容,如果未能解决你的问题,请参考以下文章
按下按钮后可以更改菜单栏文本吗? (Python,tkinter)