Tkinter resize frame inside canvas does not work - python

I want to resize a frame inside my canvas, so that the frame covers the whole window.
I did some googleing and saw some people are using a resize function, but this doesn't work for me.
I get the following error message:
_tkinter.TclError: invalid boolean operator in tag search expression
This is my code:
import tkinter as tk
from tkinter import ttk
class MainGUI:
def __init__(self):
self.root = tk.Tk()
self.root.geometry('400x400')
self.root.title("A simple GUI")
self.canvas = tk.Canvas(self.root, background='GREEN',width=900,height=300,scrollregion=(0,0,500,2000))
self.scrollbar=tk.Scrollbar(self.canvas,orient="vertical",command=self.canvas.yview)
self.scrollbar.pack(side="right",fill="y")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion = self.canvas.bbox("all")))
self.main = tk.Frame(self.canvas,bg='BLUE')
self.canvas.create_window((0,0), window=self.main, anchor="nw")
self.canvas.pack(side=tk.TOP,fill=tk.BOTH,expand=True)
self.canvas.bind("<Configure>", self.resize_frame)
for _ in range(30):
MyCoolFrame(self.main)
def resize_frame(self, e):
canvas_width = e.width
self.canvas.itemconfig(self.main, width = canvas_width)
class MyCoolFrame:
def __init__(self,master):
self.master = master
self.frame = tk.Frame(self.master,bg='RED')
for i in range(5):
tk.Label(self.frame,text=f'Cool Label {i}').pack(side=tk.LEFT,padx=2,pady=10)
self.frame.pack(side=tk.TOP,pady=10)
a = MainGUI()
a.root.mainloop()

Related

Python tkinker - scrolling problem with scrollbars

I would like to do scrolling in my photo editing program. But something is not working for me. The code was in theory based on one of the stackoverflow entries and I can't identify the problem. I have tried to remake it in various ways. _inbound_to_mousewheel prints nicely and so does _un_to_mousewheel. The one responsible directly for scrolling doesn't even print.
Is it possible to do a zoom in such a project, and if not, does it need to be rebuilt?
Main window file:
import sys
import tkinter as tk
from tkinter import filedialog, Frame
from PIL import ImageTk, Image
from gui.menu_bar import MenuBar
from gui.workspace import Workspace
def close_all_windows():
sys.exit()
class MainForm(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("myPhotoEditor by Sebastian KÄ…kolewski")
self.geometry("1000x850")
self.protocol("WM_DELETE_WINDOW", close_all_windows)
self.menu_bar = MenuBar(self)
self.config(menu=self.menu_bar)
# region Workspace
self.workspace_frame = Frame(self)
self.workspace_frame.pack(expand=True, fill="both", side="left")
self.workspace = Workspace(self.workspace_frame)
self.workspace.pack(fill="both", expand=True)
file_url = filedialog.askopenfilename()
img = Image.open(file_url)
self.workspace.image = ImageTk.PhotoImage(img)
self.workspace.create_image(0, 0, image=self.workspace.image, anchor='nw')
self.workspace_frame.bind('<Enter>', self._bound_to_mousewheel)
self.workspace_frame.bind('<Leave>', self._unbound_to_mousewheel)
# endregion
def _bound_to_mousewheel(self, event):
print("_bound_to_mousewheel")
self.workspace_frame.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbound_to_mousewheel(self, event):
print("_unbound_to_mousewheel")
self.workspace_frame.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
print("_on_mousewheel")
self.workspace_frame.yview_scroll(int(-1 * (event.delta / 120)), "units")
Workspace file:
from tkinter import Canvas, Scrollbar
class Workspace(Canvas):
def __init__(self, master=None):
Canvas.__init__(self, master)
self.master = master
workspace_hbar = Scrollbar(self.master, orient="horizontal")
workspace_hbar.pack(side="bottom", fill="x")
workspace_hbar.config(command=self.xview)
workspace_vbar = Scrollbar(self.master, orient="vertical")
workspace_vbar.pack(side="right", fill="y")
workspace_vbar.config(command=self.yview)
self.config(xscrollcommand=workspace_hbar.set, yscrollcommand=workspace_vbar.set)

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.

How store event.width in a variable in python?

I have a script that displays the dimensions of the tkinter window and I was wondering how I can set those values into a variable?
Here is the code:
import tkinter as tk
from tkinter import PhotoImage
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
def canvas_changed(event):
global WW
WW = event.width
global HH
HH = event.height
img = PhotoImage(file="competeButton.png")
foo = canvas.create_image(image=img, width=WW, height=HH)
foo.pack(fill="both", expand=True)
root.mainloop()
Many thanks in advance for any help.
There's nothing special you have to do. Data is data, whether it comes from a widget or a database or anything else.
Here's an example that saves the value to a global variable:
def canvas_changed(event):
global canvas_width
canvas_width = event.width
Create a class with all your image and the resizing of it. Then issue an if statement to call those values every time it changes. Here is an example of what you might do:
import tkinter as tk
from PIL import Image, ImageTk
class MainFrame(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.canvas = tk.Canvas(self)
self.canvas.pack(fill=tk.BOTH, expand=1)
self.canvas.bind("<Configure>", self.resize)
self.img = ImageTk.PhotoImage(Image.open(r"competeButton.png"))
self.canvas_img = self.canvas.create_image(0, 0, anchor="nw", image=self.img)
def resize(self, event):
img = Image.open(r"competeButton.png").resize((event.width, event.height), Image.ANTIALIAS)
self.img = ImageTk.PhotoImage(img)
self.canvas.itemconfig(self.canvas_img, image=self.img)
if __name__ == "__main__":
main_frame = MainFrame()
main_frame.mainloop()

Image not shown python tkinter

I want to input an image in my program, when I do this code
from tkinter import *
root = Tk()
image1 = PhotoImage(file='dog.gif')
label1 = Label(root, image=image1)
label1.pack()
root.mainloop()
The image will appear, but when I do this code
from tkinter import *
class Image():
def __init__(self, master):
self.master = master
self.display_image()
def display_image(self):
self.image = PhotoImage(file='dog.gif')
self.label1 = Label(self.master, image=self.image)
self.label1.pack()
if __name__ == '__main__':
root = Tk()
Image(root)
root.mainloop()
The window will appear but the image was not shown
If you create an image without keeping a strong reference somewhere, it will create a conflict between the garbage collector of Python and Tkinter : the reference stored in the Label prevents the complete destruction and the image will be blank.
Also, be careful to not override base classes like Image, you'd better rename your own class My_Image
A simple workaround is to create a global variable containing the image:
from tkinter import *
class My_Image():
def __init__(self, master):
self.master = master
self.display_image()
def display_image(self):
self.label1 = Label(self.master, image=label1_img)
self.label1.pack()
root = Tk()
label1_img = PhotoImage(file='dog.gif')
My_Image(root)
root.mainloop()
or you can even create a global variable to store the My_Image instance :
if __name__ == '__main__':
root = Tk()
img = My_Image(root)
root.mainloop()

Is it possible to keep the same window for every class in python's tkinter?

I am trying to create a program in tkinter which allows me to open an initial window then to keep it throughout all classes used. For example, if I was to create a button in a window then when I click this button, it would exuecute a method that destroys the widget, and then executes a new class that builds a new screen within the same window, such as text opposed to a button.
from tkinter import *
class Window1:
def __init__(self, master):
self.master = master
self.label = Button(self.master, text = "Example", command = self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
## Code to execute next class
class Window2:
def __init__(self, master):
self.master = master
self.label = Label(self.master, text = "Example")
self.label.pack()
def main():
root = Tk()
run = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
I understand this is less practical, but I am curious. Cheers.
Tk() creates main window and variable root gives you access to this window. You can use root as argument for Window2 and you will have access to main window inside Window2
from tkinter import *
class Window1:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Button(self.master, text="Example", command=self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
# use `root` with another class
self.another = Window2(self.master)
class Window2:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Label(self.master, text="Example")
self.label.pack()
root = Tk()
run = Window1(root)
root.mainloop()
--
Probably nobody use another class to create Label in place of Button ;)
--
EDIT: In this example using names Window1 and Windows2 is misleading because there is only one window and two classes which use this window. I would rather use names FirstOwner, SecondOwner
Everything is implemented in one Tk class and in this case there always is only one window.
from tkinter import *
from tkinter import ttk
class MainWindow():
def __init__(self, mainWidget):
self.main_frame = ttk.Frame(mainWidget, width=300, height=150, padding=(0, 0, 0, 0))
self.main_frame.grid(row=0, column=0)
self.some_kind_of_controler = 0
self.main_gui()
def main_gui(self):
root.title('My Window')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=0, column=0)
self.main_label_2 = ttk.Label(self.main_frame, text='Object_2')
self.main_label_2.grid(row=1, column=0)
self.main_label_3 = ttk.Label(self.main_frame, text='Object_3')
self.main_label_3.grid(row=2, column=0)
self.setings_button = ttk.Button(self.main_frame, text='Setings')
self.setings_button.grid(row=0, column=1)
self.setings_button.bind('<Button-1>', self.setings_gui)
self.gui_elements = [self.main_label_1,
self.main_label_2,
self.main_label_3,
self.setings_button]
def setings_gui(self, event):
self.gui_elements_remove(self.gui_elements)
root.title('Setings')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=2, column=0)
self.main_menu_button = ttk.Button(self.main_frame, text='Main menu')
self.main_menu_button.grid(row=0, column=1)
self.main_menu_button.bind('<Button-1>', self.back_to_main)
self.some_kind_of_controler = 1
self.gui_elements = [self.main_label_1,
self.main_menu_button]
def back_to_main(self, event):
if self.some_kind_of_controler == 1:
self.gui_elements_remove(self.gui_elements)
else:
pass
self.main_gui()
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def main():
global root
root = Tk()
root.geometry('300x150+50+50')
window = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()

Categories