I have a Canvas inside a Frame in tkinter. The frame has a background color and the canvas too. But seemingly the frame background overrides the canvas color.
How can I increase the transparency of the frame background such that the canvas is visible?
import Tkinter
import tkMessageBox
from Tkinter import *
top = Tkinter.Tk()
frame = Frame(top, width=1000, height=1000, background="bisque")
frame.pack()
bottomframe = Frame(top, width=1000, height=1000, background="red")
bottomframe.pack( side = BOTTOM )
def creatLayers(no_of_layers, max_nodes_in_each_layer, frame1=bottomframe):
print 'here2'
listLayerRect=[]
listDelimiterRect=[]
#The canvas is created here.
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
frame1.pack(side=LEFT)
for i in range (0,no_of_layers):
print 'here3'
x=15*i
#rectangles that are being drawn on the canvas.
mainCanvas.create_polygon(x,0,x+10,0,x+10,1000,x,1000, outline='gray', fill='gray', width=2)
# listLayerRect.append(Tkinter.Canvas(frame1, bg="blue", height=1000, width=30))
# listDelimiterRect.append(Tkinter.Canvas(frame1, bg="yellow", height=1000, width=30))
L1 = Label(frame, text="Layers")
E1 = Entry(frame, bd =8)
L2 = Label(frame, text="Layers2")
def helloCallBack(E=E1,):
# tkMessageBox.showinfo( "Hello Python", "Hello World")
k=int(E.get())
print 'here'
print k
creatLayers(k,k)
B = Tkinter.Button(frame, text ="Enter", command = helloCallBack)
B.pack(side=LEFT)
#L1.pack(side=LEFT)
E1.pack(side=LEFT)
#L2.pack(side=LEFT)
top.mainloop()
So, basically, when you enter a number in the box and press Enter, a canvas gets created in the red part (frame) and a grid pattern should be drawn on that canvas. Essentially, there are 2 frames, the top frame contains the button and the entry box, the lower frame should be able to draw stuff inside on the canvas created within.
The reason why the canvas is not displayed is because you're not telling it to be displayed inside frame1, i.e. you forgot to pack (or grid, or place) it, so just do in the meantime:
...
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
mainCanvas.pack()
...
Now depending on what you really want to achieve from the layout point of view, you may need to think better how to use pack, grid and pack.
Here's the result after the correction above (on Mac OS X, Sierra)
Before clicking Enter
After clicking Enter
In general, just remember that a frame will have a empty body if it doesn't contain any widget with a certain specified size.
Related
I am a newbie trying to use tkinter to build a GUI for an application. So far, I have a frame that I'd like to put several buttons into. However, every time I attempt to position this button, it isn't placed properly, being put outside of the frame itself. I wouldn't like to use the place function because of the several buttons I have to dynamically generate coming from an excel sheet so I was hoping to use the grid function instead.
Here is what I have so far
from tkinter import *
from customtkinter import *
window = Tk()
window.geometry("1920x1080")
window.state("zoomed")
window.title("My Company's Description Printer")
main_frame = CTkFrame(window, width=1920, height=1080, fg_color="grey21")
main_frame.place(x=0, y=0)
title = Label(main_frame,
text="My Company",
bg="grey21",
fg="white",
font=("Trajan Pro", 20)).place(x=626, y=30)
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19").place(x=60, y=110)
test_button = CTkButton(button_frame, text="test").grid(row=0, column=0)
window.mainloop()
Example of code being ran
As you can see, the button is being placed in the top left corner of the entire window rather than the top left corner of the black bordered button frame. Any help would be appreciated. Thank you so much.
Note that button_frame is None because it is the result of .place(...), so the button (test_button is None as well due to same reason) is a child of the root window instead of the instance of CTkCanvas. .place(...) should be called in separate line.
Also .create_window() is used instead of tkinter layout manager to put widget into a canvas:
...
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19")
# call .place(...) in separate line
button_frame.place(x=60, y=110)
test_button = CTkButton(button_frame, text="test") # don't use .grid(row=0, column=0)
# use .create_window() to put widget into canvas
button_frame.create_window(0, 0, window=test_button, anchor="nw")
This is how I want it.
I tried using the border option in the label itself but the problem is I am not using a frame so it's causing some undesired problems like if the temp or location changes so does the border. So it becomes small some times or sometimes goes out of the window
You need to connect it into frame.
Here's an example:
from tkinter import *
window = Tk()
#Creating frame
frame = Frame(window, #Connect it to window
bg="pink", #BaackGround color
bd=5, #Just border you can change that
relief=SUNKEN)
frame.pack()
label = Label(frame, #CONNECT IT TO THE FRAME
text='This is label',
)
label.pack()
window.mainloop()
I am newbie in programming, don't hate me pls :)
Why scroll is not working on my canvas widget?
I added loop with 30 rows and I cannot scroll down.
Its look like it because of create_text() method or maybe not.
I've written code for example below.
from tkinter import *
root = Tk()
root.geometry('200x150')
frame = Frame(root)
yscrollbar = Scrollbar(frame, orient=VERTICAL)
yscrollbar.pack(fill=Y, side=RIGHT)
canvas = Canvas(frame,
yscrollcommand=yscrollbar.set,
bg='white')
canvas.pack(fill=BOTH)
yscrollbar.config(command=canvas.yview)
n=12
for i in range(1,31):
canvas.create_text(10,n,text=i)
n+=12
frame.pack()
root.mainloop()
Scrolling is not responsive because you need to tell the canvas to limit the scrolling to a given area.
You can use the bbox method to get a bounding box for a given object, or a group of objects.
canvas.bbox(ALL) returns the bounding box for all objects on the canvas.
Link: http://effbot.org/zone/tkinter-scrollbar-patterns.htm you can check other methods to do this in this link
Here is the working code:
from tkinter import *
root = Tk()
root.geometry('200x150')
frame = Frame(root)
yscrollbar = Scrollbar(frame, orient=VERTICAL)
yscrollbar.pack(fill=Y, side=RIGHT)
canvas = Canvas(frame,
yscrollcommand=yscrollbar.set,
bg='white')
canvas.pack(fill=BOTH)
yscrollbar.config(command=canvas.yview)
n=12
for i in range(1,31):
canvas.create_text(10,n,text=i)
n+=12
frame.pack()
# Add this line to tell the canvas the area over to scroll
canvas.config(scrollregion=canvas.bbox(ALL))
root.mainloop()
I am trying to make an application that displays a grid in the middle of the screen surrounded by two bars, a top bar and a bottom bar, which contain buttons for the user to press. These buttons should be able to display no matter where the user scrolls to on the grid and should not be cut off if the window is resized. I am struggling to configure the scrollbar to track the right area and to have the grid fall off the screen when the window is resized. Here is my code so far:
from tkinter import *
def add_row(event):
input_row = Entry(grid_frame, bd=1, text="", bg="white", relief="solid")
input_row.grid(row=grid_frame.rows, sticky=N+S+E+W)
Grid.rowconfigure(grid_frame, grid_frame.rows, weight=1)
grid_frame.rows = grid_frame.rows + 1
class GridFrame(Frame):
rows = 0
def __init__(self, root):
Frame.__init__(self, root, bd=1)
root = Tk(className="Main screen")
root.minsize(408, 80)
# size to quarter of screen
w, h = root.winfo_screenwidth() / 2, root.winfo_screenheight() / 2
root.geometry("%dx%d+0+0" % (w, h))
# grid_frame will resize and bars will not
Grid.rowconfigure(root, 1, weight=1)
Grid.columnconfigure(root, 0, weight=1)
myframe = Frame(root, bd=4, relief="groove")
myframe.grid(row=1, sticky=N + W + S + E)
canvas = Canvas(myframe)
grid_frame = GridFrame(canvas)
grid_frame.pack(fill=BOTH, expand=True)
grid_frame.bind("<Button-1>", add_row)
scrollbar = Scrollbar(myframe, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.pack(side=LEFT, fill=BOTH, expand=True)
topBar = Frame(root, grid_frame)
label = Label(topBar, text="Top Text")
label.pack()
topBar.grid(row=0, sticky=W+N+E+S)
bottomFrame = Frame(root, grid_frame)
label = Label(bottomFrame, text="Bottom Text")
label.pack()
bottomFrame.grid(row=2, sticky=E+S+W)
mainloop()
The scrollregion I want to track is the myframe/canvas/grid_frame combination I read to use from this post. The current functionality is that the scrollbar is never in an "active" state and rows added to the grid merely shrink the grid for it to fit within the display. To add a new row, click within the grid_frame region. Any help would be greatly appreciated! Here are some images of the current UI:
UI display with only a few rows
UI display with many more rows
There are two major problems with your code.
First, for the canvas to be able to scroll the inner frame, the inner frame must be a canvas object created with create_window. You're adding it to the canvas with pack, which means the canvas cannot scroll it.
To fix that, use create_window instead of pack:
canvas.create_window(0, 0, anchor="nw", window=grid_frame)
Second, you must reset the scrollregion attribute whenever the contents inside the canvas change. Normally this is done in a <Configure> event handler on the frame, but you can just as easily call it in your add_row function.
For example, add the following line to the end of add_row:
canvas.configure(scrollregion=canvas.bbox("all"))
With those two changes, the scrollbars will start to work as soon as the inner frame is taller than the canvas.
The above solves the problem of the inner window being able to scroll when you add items. In the specific example of this test program, you also have the problem that your binding is on the frame. At startup the frame has a size of 1x1 so it's a bit hard to click on. Moving the binding to the canvas will make this specific demo program work better.
I have written some code for some buttons. However, I am not sure how to add a specific number of pixels of spacing for each button. So far is the code I have written. However, I have not yet figured out a reliable way to add spacing between the buttons in pixel sizes.
import tkinter as tk
#from tkinter import PhotoImage
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
root = tk.Tk()
root.geometry("960x600")
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky="we")
button_qwer = tk.Button(f1, text="Banana", command=banana)
button_asdf = tk.Button(f1, text="Tomato", command=tomato)
button_zxcv = tk.Button(f1, text="Potato", command=potato)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=1)
button_zxcv.grid(row=0, column=2)
root.mainloop()
Adding space between widgets depends on how you are putting the widgets in the window. Since you are using grid, one simple solution is to leave empty columns between the buttons, and then give these columns a minsize equal to the space you want.
Example:
f1.grid_columnconfigure((1, 3), minsize=10, weight=0)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=2)
button_zxcv.grid(row=0, column=4)
Using a specific number of pixels of spacing between each Buttondoesn't sound to me like such as good idea because it isn't very flexible nor easily portable to devices with different resolutions.
Nevertheless I've figured-out a way of doing it—namely by putting a do-nothing invisible button between of the each real ones. This got somewhat involved, mostly because it requires putting an image on each Button used this way so its width option argument will be interpreted as number of pixels instead of number of characters (here's some documentation describing the various Button widget configuration options).
import tkinter as tk
# Inline XBM format data for a 1x1 pixel image.
BITMAP = """
#define im_width 1
#define im_height 1
static char im_bits[] = {
0x00
};
"""
root = tk.Tk()
root.geometry("960x600")
bitmap = tk.BitmapImage(data=BITMAP, maskdata=BITMAP)
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky=tk.EW)
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
def layout_buttons(parent, buttons, spacing):
if buttons:
first, *rest = buttons
first.grid(row=0, column=0) # Position first Button.
for index, button in enumerate(rest, start=1):
col = 2*index
# Dummy widget to separate each button from the one before it.
separator = tk.Button(parent, relief=tk.FLAT, state=tk.ACTIVE,
image=bitmap, borderwidth=0, highlightthickness=0,
width=spacing)
separator.grid(row=0, column=col-1)
button.grid(row=0, column=col)
buttons = (
tk.Button(f1, text="Banana", command=banana),
tk.Button(f1, text="Tomato", command=tomato),
tk.Button(f1, text="Potato", command=potato),
)
layout_buttons(f1, buttons, 30)
root.mainloop()
Result:
Here's a blow-up showing that the spacing is exactly 30 pixels (as counted in my image editor and indicated by the thin horizontal black line between the adjacent edges of the two Buttons).