I'm creating an instrument panel in Tkinter (Python 3.7) and have been attempting to place an image on top of other widgets to augment their appearance. The problem is, every time I place an image it ends up in the background. Ideally I would like to put an image with transparency over all the widgets in my panel, but I would settle for simply being able to put non-transparent images over parts of my display.
I've been using place() to position my widgets since I never want the widgets to move and only need it to work for a specific screen resolution.
So far I've tried using the PIL package and tried placing the image inside a label and a canvas, but both seem to have the same result. Even if I place my widgets inside the canvas with the image, the widgets will show up in front.
Here's a simple example:
import tkinter as tk
import PIL.Image
import PIL.ImageTk
root = tk.Tk()
image = PIL.Image.open('esis/decals_green.gif')
photo = PIL.ImageTk.PhotoImage(image)
label = tk.Label(root, image=photo)
label.image = photo #keep reference
sampleWidget = tk.Button(root, text='Test')
sampleWidget.place(x=0, y=0, height=100, width=100)
label.place(x=0, y=0, height=200, width=200)
root.mainloop()
Even though I'm placing the image label last, it shows up underneath the button.
When tkinter widgets overlap, tkinter will use the stacking order (sometimes referred to as a z-index) to determine which widget overlays the other.
The stacking order defaults to the order in which the widgets are created (widgets created earlier are lower in the order than widgets created later). You can change this ordering with the lower and lift methods. Because you created the button widget last, it will have a higher place in the stacking order and thus it will appear on top of the image.
If you wait to create the label with the image until after all of the other widgets have been created, it will be highest in the stacking order and thus appear on top of all other widgets. You could also leave the code as-is and add label.lift() near the end of the code to raise it to the top of the stacking order.
Related
I am writing a GUI to display images (essentially a slideshow). I would like to add a scroll bar so the user can cycle through multiple images at a faster rate then clicking a button. An example of what I would like is below.
Currently, I have implemented the scale widget which when moved with cycle through every 25 images. However, this only works in one direction. Is there a way to bind the scale widget with two commands, one to go forward and backwards? I already have left and right arrow buttons that will cycle through images one by one, but it does not "sync" with the scale widget.
scale = tk.Scale(master=self.root
, orient=tk.HORIZONTAL, from_=self.imgIndex + 1, to=int(len(self.img_lst)), resolution=25,
length=250, width =25,
showvalue=False, command=self.next_25)
# scale.pack(side=BOTTOM, fill=X)
scale.place(x=550,y=650, anchor=tk.CENTER)
Any help/ideas would be greatly appreciated.
How can I create buttons and labels in a canvas widget and make the whole set of widgets in the canvas scrollable?
I came across a _create() option in my IDE but I'm not sure how to use it. I tried entering Button for itemType but it didn't work.
I tried create_window() but I'm not sure how to use it either.
My question is not how to use a loop and add the buttons, but how can I create widgets and place them in a canvas, but scrollable? Also is there a _create()? If yes, is there any way to create widgets using it?
How to create buttons and labels in a canvas widget and make the whole set of widgets in the canvas scrollable?
You have two choices:
use the canvas method create_window to add the buttons and labels to the canvas at specific coordinates
use the canvas method create_window to add a frame to the canvas, and then put your buttons and labels inside of the frame using pack or grid or place.
You then have to make sure you configure the scrollregion of the canvas to be large enough to include the widgets.
I came across a _create() option in my IDE but I'm not sure how to use it.
The following example shows how to use create_window to add a button to a canvas. The procedure is the same for any type of widget.
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
the_button = tk.Button(canvas, text="Click me", ...)
canvas.create_window(10, 10, window=the_button, anchor="nw")
canvas.configure(scrollregion=canvas.bbox("all"))
My code is the following:
import tkinter as tk
#setting up window.
root = tk.Tk()
root.title("CSV Maker")
root.geometry("600x300")
#setting up frames.
leftFrame = tk.Frame(root, bg="red", width=300, height=300)
rightFrame = tk.Frame(root, bg="blue", width=300, height=300)
#placing frames on window.
leftFrame.grid(row=0, column=0)
rightFrame.grid(row=0, column=1)
#setting up labels.
inputPathLabel = tk.Label(leftFrame, text="Input File Path:")
#placing labels on frames.
inputPathLabel.grid(row=0, column=0)
root.mainloop()
When I remove the label I get the following:
Without label
However when I leave the code as it is below (with a label), I get a completely different result. It seems as if the frame was resized to another size than the one that I selected and the color is gone. Why is this?
With label
That is simply how tkinter was designed to work. When you use pack or grid, frames (or any other widget) will shrink or expand to try to fit all of its contents.
99.9% of the time, this is the behavior you want. Tkinter is really good at making GUIs the appropriate size.
From the official documentation for grid:
The grid geometry manager normally computes how large a master must be to just exactly meet the needs of its slaves, and it sets the requested width and height of the master to these dimensions. This causes geometry information to propagate up through a window hierarchy to a top-level window so that the entire sub-tree sizes itself to fit the needs of the leaf windows. However, the grid propagate command may be used to turn off propagation for one or more masters. If propagation is disabled then grid will not set the requested width and height of the master window. This may be useful if, for example, you wish for a master window to have a fixed size that you specify.
From the documentation for pack:
The packer normally computes how large a master must be to just exactly meet the needs of its slaves, and it sets the requested width and height of the master to these dimensions. This causes geometry information to propagate up through a window hierarchy to a top-level window so that the entire sub-tree sizes itself to fit the needs of the leaf windows. However, the pack propagate command may be used to turn off propagation for one or more masters. If propagation is disabled then the packer will not set the requested width and height of the packer. This may be useful if, for example, you wish for a master window to have a fixed size that you specify.
Notice that place doesn't have the same behavior. From the place documentation:
Unlike many other geometry managers (such as the packer) the placer does not make any attempt to manipulate the geometry of the master windows or the parents of slave windows (i.e. it does not set their requested sizes). To control the sizes of these windows, make them windows like frames and canvases that provide configuration options for this purpose.
for my program I need to place an transparent image on top of multiple frames (as a kind of overlay), and apparently, placing the image in a canvas is a good way to do it as they support transparency. However, the canvas has a non-transparent border around it, which means it just sits on top of the frames with a a gray rectangular background around it, and I have no clue how to fix it:
from tkinter import *
root = Tk()
root.title("Transparency")
root.geometry("500x500")
frame = Frame(root, width=500, height=500, bg="yellow")
frame.place(x=0, y=0)
photoimage = PhotoImage(file="example1.png")
canvas = Canvas(frame, width=300, height=200, bg="red")
canvas.create_image(128, 64, image=photoimage)
canvas.place(x=100, y=100)
root.mainloop()
This is not my actual program, but it does replicates what happens. I also need to use the Place manager instead of the others (my program uses a lot of coordinates to place several widgets).
This is what it looks like when running the above code
This is what I want it to look like (assume the yellow underneath the image are a bunch of frames).
All I am trying to do is place a transparent Canvas on top of opaque Frames, and it should be impossible to tell that the Canvas is even present.
How do I accomplish this?
In Tkinter, resizing a canvas and/or frame can be done using
canvas.pack(fill="both", expand=True)
This way I can drag the tkinter window with the mouse and the canvas and frames within will adapt to the new size.
However I have not found a solution for applying this to images within the canvas. Only solutions so far are to independently change the size of the images through event actions.
Is there any way to make images within a canvas to resize dynamically, just like the canvas does with the one-liner above?
Is there any way to make images within a canvas to resize dynamically, just like the canvas does with the one-liner above?
No, there is no way to do what you want. Images aren't like widgets which can automatically grow and shrink. You will need to set up a binding on the <Configure> event of the containing widget, and in the bound function you will have to convert the image to the desired size.