Question
I'm trying to make a 'ttk Label' which a) is 20 pixels high by 300 pixels wide, b) is scrollable (in this case horizontally), and c) uses the simplest code possible within reason (except for the fact that the text and scrollbar are both within a frame*). I've found stackoverflow to be helpful in describing the processes I need to go through (put the label in a frame, put the frame in a canvas, put the scroll bar next to or underneath the canvas and 'bind' them together somehow), but despite looking at a fair few docs and stackoverflow questions, I can't figure out why my code isn't working properly. Please could someone a) update the code so that it satisfies the conditions above, and b) let me know if I've done anything unnecessary? Thanks
*the frame will be going in a project of mine, with text that is relevant
Current code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
myframe_outer = ttk.Frame(root)
mycanvas = tk.Canvas(myframe_outer, height=20, width=300)
myframe_inner = ttk.Frame(mycanvas)
myscroll = ttk.Scrollbar(myframe_outer, orient='horizontal', command=mycanvas.xview)
mycanvas.configure(xscrollcommand=myscroll.set)
myframe_outer.grid()
mycanvas.grid(row=1, sticky='nesw')
myscroll.grid(row=2, sticky='ew')
mycanvas.create_window(0, 0, window=myframe_inner, anchor='nw')
ttk.Label(myframe_inner, text='test ' * 30).grid(sticky='w')
root.mainloop()
Edit:
Current result
Answer
Use a readonly 'entry' widget - it looks the same as a label, and doesn't need to be put in a canvas.
Code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
mytext = tk.StringVar(value='test ' * 30)
myframe = ttk.Frame(root)
myentry = ttk.Entry(myframe, textvariable=mytext, state='readonly')
myscroll = ttk.Scrollbar(myframe, orient='horizontal', command=myentry.xview)
myentry.config(xscrollcommand=myscroll.set)
myframe.grid()
myentry.grid(row=1, sticky='ew')
myscroll.grid(row=2, sticky='ew')
root.mainloop()
Result
Related
This is my first time using tkinter and I already did some research on pack and grid. How do I fix this code so that the pack and grid components don't intertwine?
I want to use grid for my checkbox so that 16 checkboxes show up in a column next to the words corresponding to them. Can I do this with pack?
# tkinter will help us with the GUI
import tkinter as tk
from tkinter import filedialog, Text
import os
def data():
categoriesArray = ["16 words here"]
for i in range(16):
checkbox = tk.Checkbutton(buttonFrame, bg="white")
checkbox.grid(row=i, column=0, sticky="w")
tk.Label(canvasFrame, text=categoriesArray[i]).grid(row=i, column=1, sticky="ew")
# Define the scrolling function for the scrollbar
def scrollFunction(event):
canvas.configure(scrollregion=canvas.bbox("all"), width=200, height=500)
# The root holds the whole app structure. Always attach to root.
root = tk.Tk()
# These two lines literally make the rectangular structure of the app.
canvas = tk.Canvas(root, height = 500, width= 1300, bg="#00008B")
canvas.pack()
# These two lines make the white screen you see on the left of the buttons.
frame = tk.Frame(root, bg="white")
frame.place(relwidth=0.8, relheight=0.8, relx=0.03, rely=0.1)
# This is the frame for the buttons on the right
buttonFrame = tk.Frame(root, bg="white")
buttonFrame.place(relwidth=0.13, relheight=0.8, relx=0.85, rely=0.1)
# You need a canvas to define a scrollbar within the app.
# Resource: https://stackoverflow.com/questions/16188420/tkinter-scrollbar-for-frame
canvas=tk.Canvas(buttonFrame)
canvasFrame=tk.Frame(canvas)
scrollbar=tk.Scrollbar(buttonFrame, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
canvas.pack(side="left")
canvas.create_window((36,0), window=canvasFrame, anchor='nw')
canvasFrame.bind("<Configure>", scrollFunction)
# Call the data for the categories to show on the right
data()
# This runs the mainframe to work
root.mainloop()
Please let me know anything I can do to make my question better.
Places I've looked but gotten confused: fix this code 'cannot use geometry manager grid inside . which already has slaves managed by pack'
I fixed it. checkbox = tk.Checkbutton(buttonFrame, bg="white") should have canvasFrame instead of buttonFrame.
I've been creating an app where there are Clients that I can add to a table, the problem is, I need a scrollbar to scroll through all the clients since the app Height is limited and the clients aren't.
Using tkinter I found a way to create a "table" using Entry and grid, but what if I want to create 100 rows? they would be outside of the view, so that's why the need of a scrollbar.
For those of you who know Java, I wanted to create something similar to Jtable, it has a method to create row, delete row, and it generates automatically that scrollbar as soon as the JTable runs out of space.
I've tried to use TkTable from ttk and mess around with some properties, but I preferred how Entries look.
root = Tk()
root.geometry("1200x900")
for i in range(10):
e = Entry(relief=RIDGE)
e.grid(row=i, column=2, sticky=N)
root.mainloop()
I created a root = Tk() and used root to grid them.
You'll see 10 Entries on top of the other.
When a window contains many widgets, they might not all be visible. However, neither a window (Tk or Toplevel instance) nor a Entry are scrollable.
One solution to make the window content scrollable is to put all the widgets in a Frame, and then, embed this Frame in a Canvas using the create_window method.
from tkinter import *
root = Tk()
canvas = Canvas(root)
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
frame = Frame(canvas)
# group of widgets
for i in range(100):
e = Entry(frame, relief=RIDGE, width = 100)
e.grid(row=i, column=2, sticky=N)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# make sure everything is displayed before configuring the scrollregion
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set)
canvas.pack(fill='both', expand=True, side='left')
scroll_y.pack(fill='y', side='right')
root.mainloop()
output:
I'm trying to make a text box having h scrollbar on its side.
How can I expand it top to bottom ?
(other widgets are expanded using sticky = N + S + E + W, but got an error:
TypeError: must be str, not Scrollbar
this is example code:
from tkinter import *
root = Tk()
text = Text(root)
text.grid(row=0, column=0)
S = Scrollbar(root)
S.grid(row=0, column=1)
S.config(command=text.yview)
text.config(yscrollcommand=S.set)
root.mainloop()
Your error is due to a naming conflict: you have named your scrollbar the same way as the S tkinter constant.
I advise you to do import tkinter as tk instead of from tkinter import *, this way the tkinter constant S will be tk.S, so there won't be confusion with the variables you define.
I want to put a small image and other widgets over a canvas on which an image is displayed. I've tried options such ascompound and other things.
Background picture is fine and the small image that I want to put over the background image shows fine but it's always top or bottom of the window. I want it to be placed over any area of background image. I've tried many options of all the geometry manager (pack, grid, place) but none of them works. Please help, here's my code :
from Tkinter import *
root = Tk()
root.iconbitmap('E:/a.ico')
root.title('Unick Locker')
canvas = Canvas(root, width=730, height=600)
canvas.grid()
bgImg = PhotoImage(file="E:/a.gif")
canvas.create_image(370, 330, image=bgImg)
login = PhotoImage(file="E:/login.gif")
lo = Label(root, image=login)
lo.grid()
root.mainloop()
In order to add any widgets over or the foreground of any background image or canvas, the row and column values of all the widgets must be same as of the background image. so, my above mentioned program would be like this :
from Tkinter import *
root = Tk()
root.iconbitmap('E:/a.ico')
root.title('Unick Locker')
canvas = Canvas(root, width=730, height=600)
canvas.grid(row=0, column=0)
bgImg = PhotoImage(file="E:/a.gif")
canvas.create_image(370, 330, image=bgImg)
login = PhotoImage(file="E:/login.gif")
lo = Label(root, image=login)
lo.grid(row=0, column=0)
root.mainloop()
I tried putting the same row and column values to the widgets in grid() methods which I wanted to put over the image, and it worked fine as I wanted :-)
Have you considered using the paste method, which lets you define the position of the pasted image through a box argument?
See http://effbot.org/imagingbook/imagetk.htm.
Please also take a look at this thread: Tkinter, overlay foreground image on top of a background image with transparency, which seems very similar to your issue.
You are looking to draw the widgets over the canvas, this means you must specify the canvas as the parent widget, not the root as you did. For that, modify lo = Label(root, image=login) to lo = Label(canvas, image=login)
Also, do not forget to specify the rows and columns where you want to position the different widgets. This means you need to write, for example, lo.grid(row=0, column=0) instead of lo.grid(). For the moment you do not see big problems because you have only one label widget. But if you try to add an other widget without mentioning the exact positions (rows and columns) you will get unexpected results.
This question isn't about images at all, it's just a basic layout problem. You'll have the same issues with or without images. The problem is simply that you aren't giving any options to grid, so it naturally puts things at the top. Tkinter also has the behavior that a containing widget (eg: your canvas) will shrink or expand to exactly fit its contents.
Here's a version that creates several widgets over a background image. Notice the use of options to pack and grid, and the use of grid_rowconfigure and grid_columnconfigure to specify how extra space is allocated.
from Tkinter import *
root = Tk()
canvas = Canvas(root, width=730, height=600)
canvas.pack(fill="both", expand=True)
bgImg = PhotoImage(file="E:/a.gif")
canvas.create_image(370, 330, image=bgImg)
l1 = Label(canvas, text="Hello, world")
e1 = Entry(canvas)
t1 = Text(canvas)
l1.grid(row=0, column=0, sticky="ew", padx=10)
e1.grid(row=1, column=1, sticky="ew")
t1.grid(row=2, column=2, sticky="nsew")
canvas.grid_rowconfigure(2, weight=1)
canvas.grid_columnconfigure(2, weight=1)
root.mainloop()
I'm trying to change the layering of Tkinter Canvas widgets. With most widgets you can force the widget above other widgets by using the lift method. However, if I try the same on a Canvas widget I get an error.
Error :
TypeError: tag_raise() got an unexpected keyword argument 'aboveThis'
An Example of my Problem :
import Tkinter as Tk
root = Tk.Tk()
w, h = 200, 200
a = Tk.Canvas(root, bg='red', width=w, height=h)
a.grid(column=0, row=0)
b = Tk.Canvas(root, bg='blue', width=w, height=h)
b.grid(column=0, row=0)
a.lift(aboveThis=None)
root.mainloop()
If I do the same thing with Frame widgets, it works.
Example:
import Tkinter as Tk
root = Tk.Tk()
w, h = 200, 200
a = Tk.Frame(root, bg='red', width=w, height=h)
a.grid(column=0, row=0)
b = Tk.Frame(root, bg='blue', width=w, height=h)
b.grid(column=0, row=0)
a.lift(aboveThis=None)
root.mainloop()
The canvas lift() method is an alias for tag_raise(), which is used to raise not the canvas itself but entities within the canvas.
I found this comment within the Tkinter.py source code:
# lower, tkraise/lift hide Misc.lower, Misc.tkraise/lift,
# so the preferred name for them is tag_lower, tag_raise
# (similar to tag_bind, and similar to the Text widget);
# unfortunately can't delete the old ones yet (maybe in 1.6)
If you replace a.lift(aboveThis=None) with Tk.Misc.lift(a, aboveThis=None) then the canvas widget is raised correctly.
I came to this question because I was actually wanting to implement the equivalent of a tk statement like
canvas-pathName raise tagOrId ?aboveThis?
in order to raise individual canvas items to a particular z-position. For those interested in the same thing, I'll just post my realization (after a little head-scratching) that this can be done in Python quite easily:
canvasObject.tag_raise(tagOrId, tagOrId2)
The second argument here just gets incorporated into the tk command line, and is then interpreted as an "aboveThis" value.