How can I raise a Frame from another class? - python

I want to raise a Frame from another class, but I'm getting an error(frame is not defined).
Does anyone can help me with this issue?
Thanks
import tkinter as tk
import ttkbootstrap as ttk
from gui_elements import ButtonFrame, EntryFrame, TreeviewFrame, FrameFrame, NotebookFrame, ThemeSelector, Page
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("1000x800")
self.frame_main = FrameFrame(self)
self.frame_main.pack(side=tk.LEFT,expand=True, fill=tk.BOTH)
self.theme = ThemeSelector(self.frame_main)
self.theme.pack()
self.entry_frame = EntryFrame(self.frame_main)
self.entry_frame2 = EntryFrame(self.frame_main)
self.entry_frame.pack()
self.entry_frame2.pack()
self.notebook_frame = NotebookFrame(self.frame_main)
self.notebook_frame.pack()
self.treeview_frame = TreeviewFrame(self.frame_main)
self.treeview_frame.pack()
self.btn_raise = ttk.Button(self.frame_main, text="Raise", command=lambda:Page.raise_frame(self.container_menu))
self.btn_raise.pack()
class PageOne(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
self.container_menu = FrameFrame(self)
self.container_menu.pack(expand=True, fill=tk.BOTH)
f1 = FrameFrame(self.container_menu)
f2 = FrameFrame(self.container_menu)
f3 = FrameFrame(self.container_menu)
f4 = FrameFrame(self.container_menu)
for frame in (f1, f2, f3, f4):
#frame.grid(row=0, column=0, sticky='news')
frame.place(relwidth=1, relheight=1)
#Page1
btn1 = ButtonFrame(f1, text="Next Page", command=lambda:self.raise_frame(f2))
btn1.pack()
#Page2
btn2 = ButtonFrame(f2, text="Previous Page", command=lambda:self.raise_frame(f1))
btn2.pack()
self.raise_frame(f1)
if __name__ == "__main__":
app = App()
app.mainloop()
The basic setup of my code.
I defined in another python file all my buttons and frames.
In my main file I only define the objects and pack all widgets on the screen.
I tried to fix this bug, but I´m struggle, because I have no clue how to get access to the frame container_menu from the class PageOne. My goal is to raise a frame which stores all widget from the class PageOne.

maybe you mean?
from ttkbootstrap import Frame, Entry, Treeview, Frame, Notebook
read about themes in tkinter
https://docs.python.org/3/library/tkinter.ttk.html
and about tkinter-page
https://pypi.org/project/tkinter-page/
maybe you mean to open a window instead of raise a frame?

Related

Display elements in different columns

I Want to place those two elements in different extremes of my window, so, the label will be placed on the left and the button in the right, as in the image below
I tried different styles of layout management, as pack and Grid, but cannot solve my problem.
main.py
from faces.schedules import Schedules
from faces.App import App
app = App()
schedules = Schedules(app)
app.mainloop()
Tkinter Window (app.py)
import tkinter as tk
from tkinter import ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.app_width = 800
self.app_height = 600
self.setup()
def setup(self):
self.title('Backup Manager')
self.iconbitmap('images/icon.ico')
#dimensoes
self.resizable(False, False)
self.geometry(newGeometry=f'{self.app_width}x{self.app_height}')
self.style = ttk.Style(self)
self.style.theme_use('xpnative')
Schedules.py
from tkinter.ttk import Button, Label, Frame
class Schedules(Frame):
def __init__(self, master):
super().__init__()
self.setupPage()
def setupPage(self):
self.header = Frame(self)
self.title = Label(self.header, text="Meus agendamentos")
self.setPageButton = Button(self.header, text='Mudar')
self.gridElements()
def gridElements(self):
self.header.grid(sticky='we')
self.title.grid(row=0, column=0, sticky='W')
self.setPageButton.grid(row=0, column=1, sticky= 'E')
self.grid()
I'd suggest using weighted columns with columnconfigure()
import tkinter as tk
root = tk.Tk()
root.geometry("200x200")
root.columnconfigure(1, weight = 2) # Configures column 1 to function as 2 columns
tk.Button(root, text = "Left") .grid(row = 0, column = 0)
tk.Button(root, text = "Right") .grid(row = 0, column = 1, sticky = tk.E)
root.mainloop()
For your case, I would suggest using .pack() instead of .grid():
main.py
from faces.schedules import Schedules
from faces.app import App
app = App()
schedules = Schedules(app)
schedules.pack(fill='x')
app.mainloop()
schedules.py
from tkinter.ttk import Button, Label, Frame
class Schedules(Frame):
def __init__(self, master):
super().__init__(master)
self.setupPage()
def setupPage(self):
self.header = Frame(self)
self.title = Label(self.header, text="Meus agendamentos")
self.setPageButton = Button(self.header, text='Mudar')
self.packElements()
def packElements(self):
# use pack() instead of grid()
self.header.pack(fill='x')
self.title.pack(side='left')
self.setPageButton.pack(side='right')
Have you tried manually placing it with .place? I use something like this:
self.setPageButton = Button(self.header, text="Meus agendamentos", command='What this button does if pressed').place(x = x-coord, y = y-coord) here
Since your app window is 800x600, you might want to try placing the button at (x = 780, y = 0 and moving it by experimentation. It's been a while since I've used tkinter, so not sure how well it might work.

import user input from TKinter button to a different .py module

On ticket.py module i have a Tkinter frame; value = Entry(frame), and on the same module I have a button where command=exoutput;
def exoutput():
print value.get()
I would like to import the value to othermodule.py on the button command/ when I hit the button.
Currently when I import, the print is generated from the exoutput() function, rather than from the othermodule.py file.
Suggestions on how to print the value on othermodule.py?
# ticket.py
from Tkinter import*
window = Tk()
window.title("Entry")
frame = Frame(window)
value = Entry(frame)
def exoutput():
print value.get()
btnStage = Button(frame, text='ACTION', command=exoutput)
btnStage.pack(side=RIGHT, padx=2)
value.pack(side=LEFT)
frame.pack(padx=10, pady=10)
window.resizable(0, 0)
window.mainloop()
The other file, I've tried something like this;
# othermodule.py
import ticket
usersinput = ticket.value.get()
print usersinput
I think you either need multi-threading, or swap your file contents. Nothing runs after mainloop until the Tk instance is destroyed.
Alternatively, you could structure your ticket.py in OOP, and fetch GUI object from it by your othermodule to use it as you please. Below is an example:
ticket.py:
#import tkinter as tk
import Tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Entry")
self.resizable(False, False)
# create widgets
self.frame = tk.Frame(self)
self.value = tk.Entry(self.frame)
self.btn_stage = tk.Button(self.frame, text="ACTION")
# widgets layout
self.frame.pack(padx=10, pady=10)
self.btn_stage.pack(side='right', padx=2)
self.value.pack(side='left')
if __name__ == "__main__":
root = Window()
root.mainloop()
and othermodule.py:
import ticket
def put():
global my_var_in_othermodule
my_var_in_othermodule = ticket_GUI.value.get()
ticket_GUI.destroy()
my_var_in_othermodule = ""
ticket_GUI = ticket.Window()
ticket_GUI.btn_stage['command'] = put
ticket_GUI.mainloop()
print(my_var_in_othermodule)
input()

Adding notebook tabs in tkinter - how do I do it with a class-based structure? (Python 3)

I wrote a tkinter application that had widgets displayed on two frames, similar to this example, which successfully runs.
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Example")
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()
#(The labels are examples, but the rest of the code is identical in structure).
labelA = ttk.Label(frame1, text = "This is on Frame One")
labelA.grid(column=1, row=1)
labelB = ttk.Label(frame2, text = "This is on Frame Two")
labelB.grid(column=1, row=1)
root.mainloop()
I decided that I should try to restructure the program to use a class (which I'm admittedly not very familiar with). However, I'm unsure what I should do to allow the widgets to appear on different frames (everything else works okay). For instance, the following produces a "TypeError: init() takes from 1 to 2 positional arguments but 3 were given." So presumably I'd need to initialise with an extra argument, but I'm not sure how the notebook would be worked into that, or if that's even the approach I should be taking. (The program will run if the "frame1" and "frame2" arguments are removed from the labels, it will, however, display the same thing on both frames).
from tkinter import *
from tkinter import ttk
class MainApplication(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.labelA = ttk.Label(self, frame1, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
self.labelB = ttk.Label(self, frame2, text = "This is on Frame Two")
self.labelB.grid(column=1, row=1)
root = Tk()
root.title("Example")
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()
MainApplication(root).pack()
root.mainloop()
I'm interested in a solution, but I'm also interested in learning what the class is doing differently compared to the standalone widgets.
This would be one way to generalize the application as a class. You want to eliminate the repeated code.
from tkinter import *
from tkinter import ttk
class Notebook:
def __init__(self,title):
self.root = Tk()
self.root.title(title)
self.notebook = ttk.Notebook(self.root)
def add_tab(self,title,text):
frame = ttk.Frame(self.notebook)
self.notebook.add(frame,text=title)
label = ttk.Label(frame,text=text)
label.grid(column=1,row=1)
self.notebook.pack()
def run(self):
self.root.mainloop()
nb = Notebook('Example')
nb.add_tab('Frame One','This is on Frame One')
nb.add_tab('Frame Two','This is on Frame Two')
nb.run()
I suggest you split the different Frames within notebook into separate files.
I used from tab2 import * because I wanted this to remain in the namespace without adding the file/class prefix ie. I didn't want to write: tab1.Tab1(notebook)
main.py
import tkinter as tk
from tkinter import ttk
from tab1 import *
from tab2 import *
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
notebook = ttk.Notebook(parent)
Tab1frame = Tab1(notebook)
Tab2frame = Tab2(notebook)
notebook.add(Typ1frame, text='TAB1')
notebook.add(Typ2frame, text='TAB2')
notebook.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
tab1.py
import tkinter as tk
from tkinter import ttk
class Typ14(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
shell_frame=tk.LabelFrame(self, text="Sample Label Frame", padx=5,pady=5)
shell_frame.grid(row=0,column=0,padx=5,pady=5)
To ensure that your code is clear and purposeful, you should create a class for your main application, and a class for each tab. You don't need to separate tab classes into separate files, but if you're working with other developers it would be in your best interest.
The code below shows your code reformatted to have 3 classes: 1 for the main app (MainApplication), and 2 for each tab (Frame1 and Frame2). In addition, I've imported tkinter as tk for referential clarity.
import tkinter as tk
from tkinter import ttk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.title("Example")
self.geometry('300x300')
self.notebook = ttk.Notebook(self)
self.Frame1 = Frame1(self.notebook)
self.Frame2 = Frame2(self.notebook)
self.notebook.add(self.Frame1, text='Frame1')
self.notebook.add(self.Frame2, text='Frame2')
self.notebook.pack()
class Frame1(ttk.Frame):
def __init__(self, container):
super().__init__()
self.labelA = ttk.Label(self, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
class Frame2(ttk.Frame):
def __init__(self, container):
super().__init__()
self.labelB = ttk.Label(self, text = "This is on Frame Two")
self.labelB.grid(column=1, row=1)
if __name__ == '__main__':
app = MainApplication()
app.mainloop()
As you can imagine, this will allow you to create additional classes to add frames to your tab classes. The code below shows an alteration to class Frame1 above, and the addition of class Frame1FrameA which does just this.
class Frame1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.labelA = ttk.Label(self, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
self.frame = Frame1FrameA(self)
self.frame.grid(row=1, columnspan=2)
class Frame1FrameA(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.LabelA = ttk.Label(self, text="LabelA in FrameA in tab Frame1")
self.LabelA.grid(column=0, row=0)
self.LabelB = ttk.Label(self, text="LabelB in FrameA in tab Frame1")
self.LabelB.grid(column=1, row=0)

Embedd Buttons with images and scrollbar on a frame using Tkinter (Python)

I am trying to make a on-button click event in a Tkinter window. I have a Tkinter Window on which there are buttons. Pressing one of those buttons,opens up a new Tkinter Window using Toplevel. This window would have a Scrollbar and some other buttons with images on it which can be vertically scrolled down. I could create the two functionalities separately,i.e, I could embedd a button with an image on a Tkinter window and use the Scrollbar but was unable to call the same function with the previous Tkinter window.
The code I am using is -
from Tkinter import *
from ttk import *
class VerticalScrolledFrame(Frame):
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior,
anchor=NW)
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
#if __name__ == "__main__":
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
#from Tkinter import *
print "in constructor"
import Tkinter
import ImageTk
import Image
import cv2
import numpy as np
import cv2.cv as cv
import math
import tkFileDialog
import tkMessageBox
import Tkinter as tk
root = Tk.__init__(self, *args, **kwargs)
def Print():
print "print function"
self.frame = VerticalScrolledFrame(root)
self.frame.pack()
self.label = Label(text="Shrink the window to activate the scrollbar.")
self.label.pack()
compare_button_path = "compare-button.jpg"
image_compare = Image.open(compare_button_path)
image_compare.thumbnail((70,45))
photo_compare = ImageTk.PhotoImage(image_compare)
button = tk.Button(self.frame, width=120, height=40, image=photo_compare,fg='black',bg='medium turquoise', activebackground='indian red',cursor="hand2",bd=6,relief=RAISED, compound=tk.LEFT, text="Compare",command = Print)
button.image = photo_compare
button.pack(side=LEFT)
buttons = []
for i in range(10):
buttons.append(Button(self.frame.interior, text="Button " + str(i)))
buttons[-1].pack()
app = SampleApp()
app.mainloop()
The above written function gives a pretty good result.
But how do I call this function on a button click from another Tkinter window? Changing the initial declaration of root to root = Tk() instead of root = Tk.init(self, *args, **kwargs) throws a
RuntimeError: maximum recursion depth exceeded while calling a Python object.
If I try to keep the function in some other file and import it into my original Tk file and create the object of that class on a button click, the second file gets automatically called during complilation of the original Tk file.
Can somebody please suggest a way out.
I really don't understand your question, even after asking for clarification. You finally wrote in the comments of the question "I simply want to open a tkinter window with buttons and images, on a button click from another Tkinter window".
I don't see what's preventing you from doing that. The only thing I see wrong with your code is that you're simply not creating an instance of Toplevel (well, except for a confusing set of imports). Other than that, your code should work.
For example, if you modify your sample app to look something like this, you can open as many windows as you want:
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
b = Button(self, text="Open a new window", command=self.open_new)
b.pack()
def open_new(self):
top = Toplevel()
self.frame = VerticalScrolledFrame(top)
self.frame.pack()
def Print():
print "print function"
button = Button(self.frame, text="Compare",command = Print)
button.pack(side=LEFT)
buttons = []
for i in range(10):
buttons.append(Button(self.frame.interior, text="Button " + str(i)))
buttons[-1].pack()

Cant get listbox to show for tkinter

So, what I am trying to do is open a file when pressing a button and displaying the contents in a listbox. This is what I have so far, but I am not getting the listbox to display, let alone get the info to be in the listbox:
#!/usr/bin/perl -w
import time
from Tkinter import *
import tkFileDialog
def listbox(listbox):
def open_file():
file = tkFileDialog.askopenfilename()
openFile = open(file)
for line in openFile:
listbox.insert(END, line)
open_file()
class App:
def __init__(self, parent):
frame = Frame(parent.title("Buttons"))
frame.pack()
root.pack_propagate(0)
self.exit = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.exit.pack(side=LEFT)
self.open = Button(frame, text="Open...", command=self.call_listbox)
self.open.pack(side=LEFT)
frame.listbox = Frame()
scrollme = Scrollbar(frame.listbox)
self.listbox = Listbox(frame.listbox, yscrollcommand = scrollme.set)
scrollme.config(command = self.listbox.yview)
scrollme.pack(side = RIGHT, fill = Y)
self.listbox.pack()
self.listbox.insert(END, "Code:")
def call_listbox(self):
listbox(self.listbox)
root = Tk()
app = App(root)
root.mainloop()
any suggestions? thanks
You are forgetting to pack the frame that contains the listbox.
FWIW, your overloading of the name "listbox" makes your code very confusing - you have def listbox(listbox), self.listbox and frame.listbox. And you also have call_listbox and the Listbox class to add to the confusion.

Categories