Having difficulties placing a frame within a frame with Tkinter - python

I am having issues trying to place a frame within a frame using classes/objects with python/tkinter. My goal is to simply place a frame in the north west corner of the outer frame instead of the whole window itself. I think I'm incorrectly referencing the outer frame in the inner frame class but I'm not 100% sure. I am fairly new to OOP and tkinter so forgive my ignorance and I appreciate the help.
Sample code:
from tkinter import *
class window():
def __init__(self, master):
self.master = master.minsize(500, 500)
master.maxsize(500,500)
self.outer_frame = Frame(master, width = 250, height = 250, bg = "red").place(anchor = CENTER, relx = 0.5, rely = 0.5)
def create_inner_window(self):
self.inner_frame = inner_frame(self.outer_frame)
class inner_frame():
def __init__(self, outer_frame):
self.inner_frame = Frame(master = outer_frame, width = 125, height = 125, bg = "blue").place(anchor = NW)
root = Tk()
my_window = window(root)
my_window.create_inner_window()
root.mainloop()
What I'm trying to accomplish:
What I get instead:

Related

Image not seen in Tkinter (python) [duplicate]

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 2 years ago.
I am starting to program, in which I have to make a python graphical interface, in which by means of a button the jpg file is chosen and it is displayed in the interface, but I have had a problem, since the image is not displayed and in the terminal it does not detect any error and practically I go crazy here I leave the code
from PIL import Image, ImageTk
from tkinter import Tk, Frame, Button, Label, Text, filedialog, PhotoImage
class Application_BotonPath(Frame):
def __init__(self, master = None):
super().__init__(master, width = "1300", height = "950", bg = "old lace")
self.master = master
self.pack()
self.Panel()
self.widget()
def Panel(self):
self.frame_side = Frame(self, width = '300', height = '850', bg = 'sky blue').place(x = 20, y = 50)
self.frame_show = Frame(self, width = '900', height = '850').place(x = 360, y = 50)
def widget(self):
boton = Button(self.frame_side, text = "Abrir Imagen", command = self.cargar_imagen).place(x = 85, y = 60, width = 150, height = 30)
salida = Text(self.frame_side, state = "disable").place(x = 43, y = 110, width = 250, height = 700)
def cargar_imagen(self):
self.ruta_imagen = filedialog.askopenfilename(title = "Abrir", filetypes = [('Archivo png', '*.png'), ('Archivo jpeg', '*.jpg')])
load = Image.open(self.ruta_imagen)
imagen = ImageTk.PhotoImage(load)
label = Label(self.frame_show, image = imagen)
label.place(x=0, y=0)
root = Tk()
root.wm_title("Detector de Caracteres")
app = Application_BotonPath(root)
app.mainloop()
image
This is what I get, the gray box that is in the upper right I suppose it is the image, but it does not show it. please help
Welcome to SO.
The image is created in a function, and when the function ends the reference to the image is garbage collected. Therefore the Label can not find any image.
You can save a reference to the image in the label object:
label = Label(self.frame_show, image = imagen)
label.image = imagen # Save reference to image
or you can make the reference an attribute of the instance:
self.imagen = ImageTk.PhotoImage(load)
label = Label(self.frame_show, image = self.imagen)

multiplying moving rectangles in python (tkinter)

I have two questions:
I want to make several rectangles, moving randomly. I am at a point where i
can do it with one rectangle but i don't get it how to multiply them.
I am a beginner so i have copied this example and modified it in my favor but i don't know exactly why i have to write everytime the "self" and the "init". It seems to be common to name those parameters in this manner.
I looked both questions up several times but didn't find a satisfying answer.
here the code:
from tkinter import *
from tkinter.ttk import *
from random import *
class simulation:
def __init__(self, anzahl, master = None):
self.master = master
self.canvas = Canvas(master, width= 2736, height= 1824)
self.rectangle = self.canvas.create_rectangle(500, 380, 515, 395, fill = "black")
self.canvas.pack()
self.movement()
def movement(self):
self.canvas.move(self.rectangle, randint(-10,10), randint(-10,10))
self.canvas.after(100, self.movement)
if __name__ == "__main__":
master = Tk()
master.title("Simulation")
simulation = simulation(master)
mainloop()
maybe this will help you, make an object for each player and the canvas packed ones in order not to hide other players ...
from tkinter import *
from random import *
class simulation:
def __init__(self, master , canvas , color):
self.master = master
self.canvas = canvas
self.rectangle = canvas.create_rectangle(500, 380, 515, 395, fill=color)
def movement(self):
canvas.move(self.rectangle, randint(-10,10), randint(-10,10))
self.canvas.after(100, self.movement)
if __name__ == "__main__":
master = Tk()
canvas = Canvas(master, width=2736, height=1824)
canvas.pack()
master.title("Simulation")
player1 = simulation(master, canvas,"red")
player2 = simulation(master,canvas, "black")
player1.movement()
player2.movement()
mainloop()

change Tkinter frame color with for loop from list of colors

I have the following code snippet. What i need to code that when i click the button i need the frame color to change one by one from the list of colors defined.
from tkinter import *
from tkinter import ttk
def ChangeColor():
colors = ['red','green', 'orange','blue']
for color in colors:
#color = entry.get()
frame.config(bg = color)
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()
You can use the cycle iterator from itertools for this.
from tkinter import *
from tkinter import ttk
from itertools import cycle
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
colors = ['red','green', 'orange','blue']
color_gen = cycle(colors)
def ChangeColor():
frame.config(bg = next(color_gen))
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()
One thing I need to mention: please avoid doing "star" imports. When you do
from tkinter import *
it puts 135 Tkinter names into your namespace; in Python 2 you get 175 names. This creates needless clutter in the namespace and it can cause name collisions: if you accidentally name one of your variables with one of the imported names that can lead to mysterious bugs. It's even worse when you do star imports with multiple modules since they can stomp over each others' names. Also, star imports make the code harder to read since you have to remember which names are defined locally and which are imported.
I would change your app to a class so you can store variables and access them easily, also I bound the enter key to the entry widget so that works too. This way when you create an instance of class app it is an instance of a Tk() root, but you don't have to call it root
import tkinter as tk
from tkinter import ttk
class app(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Title")
self.frame = tk.Frame(self, width = 260, height = 200)
self.frame.pack()
self.btn = ttk.Button(self.frame, text = 'Change color', command = self.ChangeColor)
self.btn.place (x = 80, y = 100)
self.entry = ttk.Entry (self.frame, width = 20)
self.entry.place(x = 80, y = 70)
self.entry.bind("<Return>",self.ChangeColorEntry)
self.colors = ['red','green','orange','blue']
self.current_color = -1
self.standard_bg = self.frame['background']
def ChangeColor(self,event=None):
if self.current_color == len(self.colors) - 1:
self.frame.config(bg = self.standard_bg)
self.current_color = -1
return
else:
self.current_color += 1
color = self.colors[self.current_color]
self.frame.config(bg = color)
def ChangeColorEntry(self,event=None):
entered = self.entry.get().lower().strip()
if entered == "none":
self.frame.config(bg = self.standard_bg)
else:
try:
self.current_color = self.colors.index(entered)
self.frame.config(bg = entered)
except:
pass
#PM 2Rings answer is cleaner but since I was working on this I thought I'd still post it incase you wanted to implement it manually
from tkinter import *
from tkinter import ttk
colors = ['red', 'green', 'orange', 'blue']
colors_it = iter(colors)
def get_next_color():
try:
global colors_it
return next(colors_it)
except StopIteration:
colors_it = iter(colors)
return next(colors_it)
def ChangeColor():
frame.config(bg=get_next_color())
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()

tkinter canvas: text object variable font size?

I'm making some pretty pictures using a tkinter canvas and overlaying text on top of circles like in the following picture:
http://static.guim.co.uk/sys-images/Guardian/Pix/pictures/2012/11/6/1352220546059/Causes-of-deaths-graphic-008.jpg
I want the font size to be dependent on the same number that the circle size is dependent on.
tempfont = tkFont.Font(family='Helvetica',size=int(round(ms*topnode[1])))
self.display.create_text(center[0],center[1],fill = "#FFFFFF",text = int(round(ms*topnode[1])),font = tempfont)
My problem is that when I use the above code, the overlayed text is a constant size for every text object. The text itself is right, as in it displays the number that I want the font size to be, just not in the correct font size. I've experimented with putting in constant integers in the size definition (works as it's supposed to), and adding a del(tempfont) immediately after the above 2 lines of code, but I haven't found what fixes this problem yet.
What am I doing wrong?
Here's a self-contained little program that reproduces the problem:
from Tkinter import *
import tkFont
class TestApp(Frame):
def __init__(self, master=None, height = 160, width = 400):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.display = Canvas(self, width = 800, height = 320, bg = "#FFFFFF")
self.display.grid(row=0,column=0)
def recurtext(tsize):
if tsize > 20:
recurtext(tsize-10)
tempfont = tkFont.Font(family='Helvetica',size=tsize)
self.display.create_text(800 - (tsize*12),160, text = str(tsize), font = tempfont)
recurtext(60)
app = TestApp()
app.master.title("Test")
app.mainloop()
The gist is that recurtext resizes the font recursively, and shows writes out the font size in that size... or I think it should. Maybe this is a bug with tkinter, but I'm still holding on to some hope that I'm the one who made a mistake in the logic here.
I've never run across this behavior before; it looks like a Tkinter bug. The good news is, there appears to be a workaround. If you give each font a unique name the problem seems to vanish.
The following example shows multiple lines, each with a different font size:
import Tkinter as tk
import tkFont
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.display = tk.Canvas(self, width=400, height=600, background="black")
self.display.pack(side="top", fill="both", expand=True)
y = 10
for size in range (2, 38, 2):
tempfont = tkFont.Font(family='Helvetica',size=size,
name="font%s" % size)
self.display.create_text(10, y, fill = "#FFFFFF",text = size,
font = tempfont, anchor="nw")
y = y + tempfont.metrics()["linespace"]
if __name__ == "__main__":
root = tk.Tk()
frame = Example(parent=root)
frame.pack(side="top", fill="both", expand=True)
root.mainloop()

Tkinter custom window

I want to make a window in Tk that has a custom titlebar and frame. I have seen many questions on this website dealing with this, but what I'm looking for is to actually render the frame using a canvas, and then to add the contents to the canvas. I cannot use a frame to do this, as the border is gradiented.
According to this website: http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_window-method, I cannot put any other canvas items on top of a widget (using the create_window method), but I need to do so, as some of my widgets are rendered using a canvas.
Any suggestions on how to do this? I'm clueless here.
EDIT: Bryan Oakley confirmed that rendering with a canvas would be impossible. Would it then be possible to have a frame with a custom border color? And if so, could someone give a quick example? I'm sort of new with python.
You can use the canvas as if it were a frame in order to draw your own window borders. Like you said, however, you cannot draw canvas items on top of widgets embedded in a canvas; widgets always have the highest stacking order. There is no way around that, though it's not clear if you really need to do that or not.
Here's a quick and dirty example to show how to create a window with a gradient for a custom border. To keep the example short I didn't add any code to allow you to move or resize the window. Also, it uses a fixed color for the gradient.
import Tkinter as tk
class GradientFrame(tk.Canvas):
'''A gradient frame which uses a canvas to draw the background'''
def __init__(self, parent, borderwidth=1, relief="sunken"):
tk.Canvas.__init__(self, parent, borderwidth=borderwidth, relief=relief)
self._color1 = "red"
self._color2 = "black"
self.bind("<Configure>", self._draw_gradient)
def _draw_gradient(self, event=None):
'''Draw the gradient'''
self.delete("gradient")
width = self.winfo_width()
height = self.winfo_height()
limit = width
(r1,g1,b1) = self.winfo_rgb(self._color1)
(r2,g2,b2) = self.winfo_rgb(self._color2)
r_ratio = float(r2-r1) / limit
g_ratio = float(g2-g1) / limit
b_ratio = float(b2-b1) / limit
for i in range(limit):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr,ng,nb)
self.create_line(i,0,i,height, tags=("gradient",), fill=color)
self.lower("gradient")
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.wm_overrideredirect(True)
gradient_frame = GradientFrame(self)
gradient_frame.pack(side="top", fill="both", expand=True)
inner_frame = tk.Frame(gradient_frame)
inner_frame.pack(side="top", fill="both", expand=True, padx=8, pady=(16,8))
b1 = tk.Button(inner_frame, text="Close",command=self.destroy)
t1 = tk.Text(inner_frame, width=40, height=10)
b1.pack(side="top")
t1.pack(side="top", fill="both", expand=True)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Here is a rough example where the frame, titlebar and close button are made with canvas rectangles:
import Tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
# Get rid of the os' titlebar and frame
self.overrideredirect(True)
self.mCan = tk.Canvas(self, height=768, width=768)
self.mCan.pack()
# Frame and close button
self.lFrame = self.mCan.create_rectangle(0,0,9,769,
outline='lightgrey', fill='lightgrey')
self.rFrame = self.mCan.create_rectangle(760,0,769,769,
outline='lightgrey', fill='lightgrey')
self.bFrame = self.mCan.create_rectangle(0,760,769,769,
outline='lightgrey', fill='lightgrey')
self.titleBar = self.mCan.create_rectangle(0,0,769,20,
outline='lightgrey', fill='lightgrey')
self.closeButton = self.mCan.create_rectangle(750,4,760, 18,
activefill='red', fill='darkgrey')
# Binds
self.bind('<1>', self.left_mouse)
self.bind('<Escape>', self.close_win)
# Center the window
self.update_idletasks()
xp = (self.winfo_screenwidth() / 2) - (self.winfo_width() / 2)
yp = (self.winfo_screenheight() / 2) - (self.winfo_height() / 2)
self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_width(),
self.winfo_height(),
xp, yp))
def left_mouse(self, event=None):
obj = self.mCan.find_closest(event.x,event.y)
if obj[0] == self.closeButton:
self.destroy()
def close_win(self, event=None):
self.destroy()
app = Application()
app.mainloop()
If I were going to make a custom GUI frame I would consider creating it with images,
made with a program like Photoshop, instead of rendering canvas objects.
Images can be placed on a canvas like this:
self.ti = tk.PhotoImage(file='test.gif')
self.aImage = mCanvas.create_image(0,0, image=self.ti,anchor='nw')
More info →here←

Categories

Resources