Tkinter tk.Scrollbar Grid and alignment issues - python

I created a Text field in Tkinter, new items added next to the text field and I can't get the bottom line with grid. window does not open when grid is added. What is the solution?
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
pencere = tk.Tk()
frame = tk.Frame(pencere)
frame.pack()
scrol = tk.Scrollbar(frame)
scrol.pack(side=tk.RIGHT, fill=tk.Y)
text = "Good bye"
text = tk.Text(frame,width=70, height=0)
text.insert(tk.INSERT, text)
text.config(yscrollcommand=scrol.set)
scrol.config(command=text.yview)
text.pack(side=tk.LEFT, fill=tk.Y)
buton = tk.Button(frame, text='close', command=pencere.destroy)
buton.pack()
text.grid(row=1, column=1)
pencere.mainloop()

You cannot use pack and grid methods for the widgets which have the same master. In this example, your frame, text and scrol are already adjusted by pack therefore tkinter does not allow to adjust text or an another element with grid as long as you don't create another frame for the element that you want to adjust with grid.
So, in my opinion there are two options:
If you still want to use grid, you should adjust whole widgets(frame, text etc.) with grid.
If you want to use grid but you don't want to change widgets which are alredy adjusted by pack, create a new frame for your text.

Dear Zolo Baba the bar cannot appear because you are still using "pack" and "grid" together. Because of the reasons that I mentioned above, we can use only one layout management method for the master. I edited your code with using only "grid" method.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
pencere = tk.Tk()
frame = tk.Frame(pencere)
frame.grid()
scrol = tk.Scrollbar(frame, orient = tk.VERTICAL)
text = tk.Text(frame,width=70, height=5)
text.insert(tk.INSERT, "###\n\n\nNote\n\n\n###")
text.config(yscrollcommand=scrol.set)
text.grid(sticky="W")
scrol.config(command=text.yview)
scrol.grid(row = 0, column=2, sticky="NS")
buton = tk.Button(frame, text='close', command=pencere.destroy)
buton.grid(row = 0, column = 1)
pencere.mainloop()

Related

How to display widgets on a frame that is mounted on canvas in Tkinter?

I created a main root with two frames.
-One frame is for program toolbar.
-Other frame is for canvas where data will be displayed and a scrollbar widget.
-Inside of the canvas is a third smaller frame which will be used for scrolling trough data.
However, when I try to define new widgets and place them on that third smaller frame, nothing happens. I'm defining new widgets inside of a function call of a button command. I have also tried declaring everything as global variables but without luck.
Hint: I tried placing the code from the function to the top level of the code and it works. Also, if I try to mount these widgets on the toolbar frame it also works. It seems that the only thing I can't do is to mount these new widgets on the small frame that is inside the canvas.
I used a simple for loop to create labels just for testing.
Could anyone tell what I am doing wrong?
from tkinter import *
from tkinter import ttk
#Creating main window
root = Tk()
root.resizable(width=False, height=False)
#Defining Background
toolbar = Frame(root, width=613, height=114)
toolbar.grid(row=0, column=0)
background_frame = Frame(root, width=615, height=560)
background_frame.grid(row=1, column=0)
background = Canvas(background_frame, width=615, height=560)
background.pack(side=LEFT, fill=BOTH, expand=1)
scroll_bar = ttk.Scrollbar(background_frame, orient=VERTICAL, command=background.yview)
scroll_bar.pack(side=RIGHT, fill=Y)
background.configure(yscrollcommand=scroll_bar.set)
background.bind('<Configure>', lambda e:background.configure(scrollregion = background.bbox('all')))
second_frame = Frame(background)
background.create_window(150,100, window=second_frame, anchor='nw')
def confirm1():
for x in range(100):
Label(second_frame, text = x ).grid(row=x, column=1)
show_labels = Button(toolbar, text= "Show labels", fg="black", command=confirm1)
show_labels.grid(row=0, column=2)
root.mainloop()
Picture of the app so far
I surely can't reproduce the issue with your current code, but looking at the previous edit it is pretty clear what your problem is.
(taken from your previous edit)
def confirm1():
global background_image1
background.delete('all') # <--- this line of code
for x in range(100):
Label(second_frame, text = x ).grid(row=x, column=1)
Here you delete all your items from your canvas:
background.delete('all')
hence no item appears.
You should instead delete only those items that you want to remove by passing the id or tags to delete method. You can delete multiple items together at once by giving the same tags.
Another option would be to recreate the frame item again on canvas using create_window (Do note: your frame is not deleted/destroyed, it's only removed from canvas)

Tkinter: Skip through PDFs in GUI

I am very new to python and want to build a gui that lets you skip through different pdfs. I managed to display the first pdf. My problem now is that I could not manage to display the second pdf and all following. If I only repeat the command to display the pdf, the new pdf gets displayed next to the old one, instead of replacing it. I have been through several hours of extensive googling and could not find out how to solve this. Can someone maybe help?
Here's my code:
from tkinter import *
import tkinter as tk
import glob
from tkPDFViewer import tkPDFViewer as pdf
from tkdocviewer import *
parent_path = 'somepath\\'
doc_list = glob.glob((parent_path + "*//*.pdf"))
doc_counter = 0
root = tk.Tk()
root.title('Training Data Creator')
root.geometry("1000x1000")
frame_r = Frame(root, relief=RAISED, borderwidth=1)
frame_r.pack(fill=BOTH, expand=True, side=tk.LEFT)
# creating object of ShowPdf from tkPDFViewer.
pdf_show = pdf.ShowPdf()
# Adding pdf location and width and height.
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
#button skip
def skip_callback():
global doc_counter
doc_counter = doc_counter +1
# Here I want to display the next PDF!!
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack()
print(doc_counter)
button_skip = Button(root, text='skip', command= skip_callback)
button_skip.pack(fill=tk.X, pady=0)
root.mainloop()
When I hit the 'skip' button, the next pdf from the 'parent_path' is supposed to appear where the initial one was displayed.
Thanks for your help!
Flo
I have looked into tkPDFViewer's code and the ShowPdf class has not been designed to load a new document after the initial one.
The reason why the second file is displayed next to the first one is that pdf_show.pdf_view() creates a new Frame which is then packed on the right of the previous one. So you need to destroy the old one first with
pdf_show.frame.destroy()
Then, I have noticed that instead of displaying the second document when clicking on the skip button, what is displayed is a concatenation of both documents. This can be solved by clearing the image list of pdf_show
pdf_show.img_object_li.clear()
Here is the full code:
import tkinter as tk
import glob
from tkPDFViewer import tkPDFViewer as pdf
parent_path = '/home/juliette/Documents'
doc_list = glob.glob((parent_path + "*//*.pdf"))
doc_counter = 0
root = tk.Tk()
root.title('Training Data Creator')
root.geometry("1000x1000")
frame_r = tk.Frame(root, relief=tk.RAISED, borderwidth=1)
frame_r.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)
# creating object of ShowPdf from tkPDFViewer.
pdf_show = pdf.ShowPdf()
# Adding pdf location and width and height.
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
#button skip
def skip_callback():
global doc_counter
doc_counter = doc_counter +1
# Reset view:
pdf_show.frame.destroy()
pdf_show.img_object_li.clear()
# Display new pdf
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
print(doc_counter)
button_skip = tk.Button(root, text='skip', command= skip_callback)
button_skip.pack(fill=tk.X, pady=0)
root.mainloop()
By the way, you are importing tkinter twice:
from tkinter import *
import tkinter as tk
so I advise you to choose one of the ways to import it and stick with it, rather than mixing both. Furthermore, I suggest you to use import tkinter as tk, this way you will not crowd the namespace with a large amount of unknown names that can lead to naming conflicts (e.g. if you import Image from PIL and then do from tkinter import *, Image will be the tkinter class, not the PIL one).

My frame is resizing instead of the listbox - tkinter

I'm making a GUI and I'm stuck trying to resize a listbox. The listbox is supposed to expand to fill the frame but instead, the frame shrinks to fit the listbox.
Thanks, in advance, for any help.
I have tried many variations of the code, but none seem to work, so I simplified the code (it still does't work) to put it on here.
import tkinter as tk
w = tk.Tk() # New window
f = tk.Frame(w, width=300, height=500, bg='red') # New frame with specific size
f.grid_propagate(0)
f.grid(row=0, column=0)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True)
I ran these sequentially in IDLE and the frame appears (in red) at the correct size, however, when I pack the listbox, the whole window shrinks to the size of the listbox (and turns completely blue, which is expected).
In my experience, turning off geometry propagation is almost never the right solution. With a couple of decades of using tk and tkinter I've done that only two or three times for very specific edge cases.
Tkinter is very good at making the widget the best size based on the set of widgets you're using. Turning off propagation means you are responsible for doing all calculations to get the window to look right, and your calculations may not be correct when your program runs on a machine with different fonts or a different resolution. Tkinter can handle all of that for you.
Unfortunately, with such a small code example and without knowing your end goal it's hard to solve your layout problems. If your goal is to have a window that is 300x500, the best solution is to make the window that size and then have the frame fill the window, which is easier to do with pack than grid:
import tkinter as tk
w = tk.Tk() # New window
w.geometry("300x500")
f = tk.Frame(w, bg='red') # New frame with specific size
f.pack(fill="both", expand=True)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True)
w.mainloop()
Thank you so much to Tls Chris for your answer and explanation. What I didn't realize is that grid_propagate() and pack_propagate() also affect a widget's children.
Therefore, with the help of Tls Chris, I have fixed my code and it now expands the listbox and doesn't shrink the frame.
Fixed code:
import tkinter as tk
w = tk.Tk() # New window
f = tk.Frame(w, width=300, height=500, bg='red') # New frame
f.grid_propagate(False) # Stopping things (that use grid) resizing the frame
f.pack_propagate(False) # Stopping things (that use pack) resizing the frame
f.grid(row=0, column=0)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True) # Packing the listbox and making it expand and fill the frame
Edit: I think you actually want the listbox to expand to fit the frame. Amended the code to do that as an option
I haven't used pack much but I suspect that grid_propagate changes the behaviour of the grid geometry manager but not of the pack manager.
The below lets app() run with or without propagate set. It uses the grid geometry manager throughout.
import tkinter as tk
def app(propagate = False, expand = False ):
w = tk.Tk() # New window
tk.Label( w, text = 'Propagate: {} \nExpand: {}'.format(propagate, expand) ).grid()
f = tk.Frame(w, width=300, height=500, bg='red') # New frame with specific size
f.grid_propagate(propagate)
f.grid( row=1, column=0 )
lb = tk.Listbox(f, bg='blue') # New listbox
if expand:
f.columnconfigure(0, weight = 1 )
f.rowconfigure(0, weight = 1 )
# lb.pack(fill=tk.BOTH, expand=True)
lb.grid( row=0, column=0, sticky = 'nsew' )
# My guess is that grid_propagate has changed the behaviour of grid, not of pack.
lb.insert(tk.END, 'Test 1', 'Test 2', 'Test 3')
w.mainloop()
This changes the listbox geometry manager to grid. Run app() as below.
app(True, True) # propagate and Expand
app(False, True) # no propagate but expand
app(True, False) # propagate without expand
app() # no propagate or expand

Trying to use "grid" and "place" in one frame after using "pack" in another frame but widgets are not visible

I'm currently working on an invoice generator in python tkinter for my coursework. I am quite new to programming and I have created a basic login page so when the login button is pressed (haven't setup restraints yet) the GUI moves to the next 'screen.' I am using frames to do this. However, on the next 'page' I can only pack() widgets, if I try and place them or use a grid they simply don't appear and I get an empty GUI window.
#!/usr/bin/python
# -- coding: utf-8 --
import tkinter as tk
root = tk.Tk()
def go_to_login():
f1.pack()
f2.pack_forget()
def go_to_first():
f1.pack_forget()
f2.pack()
root.geometry('1280x800')
root.title('Invoice')
f1 = tk.Frame(root)
label_US = tk.Label(f1, text='Username')
label_US.pack()
label_PW = tk.Label(f1, text='Password')
label_PW.pack()
entry_US = tk.Entry(f1)
entry_US.pack()
entry_PW = tk.Entry(f1, show='*')
entry_PW.pack()
checkbox_LI = tk.Checkbutton(f1, text='Keep me logged in')
checkbox_LI.pack()
but_LI = tk.Button(f1, text='login', command=go_to_first)
but_LI.pack()
but_QT = tk.Button(f1, text='Quit', command=quit)
but_QT.pack()
f2 = tk.Frame(root)
but_LO = tk.Button(f2, text='Logout', command=go_to_login)
but_LO.pack() # try to change pack here
but_HP = tk.Button(f2, text='Help')
but_HP.pack() # try to change pack here
but_NX1 = tk.Button(f2, text='Next', command=quit)
but_NX1.pack() # try to change pack here
f1.pack()
root.mainloop()
What I basically want is to be able to place or use grid to set the locations of my widgets also on the second frame, but unless I use pack I get an empty GUI screen. What have I done wrong or how can I place the widgets instead to packing them?
I'm not sure if this is the best way to do things and I have no experience using classes etc but I'm open to suggestions.
As long as you specify the row and column when using .grid(), the code works fine for both 'screens'. I can think of two possible reasons why you're code wasn't working
1) If you try and use .pack and .grid for two labels in the same frame, you will get an error
2) There may be something else in your code causing your error, if so, add it to your question
Anyway, here's the code that worked for me:
f2 = tk.Frame(root)
but_LO = tk.Button(f2, text='Logout', command=go_to_login)
but_LO.grid(row = 0, column = 0) # specify row and column
but_HP = tk.Button(f2, text='Help')
but_HP.grid(row = 1, column = 0)
but_NX1 = tk.Button(f2, text='Next', command=quit)
but_NX1.grid(row = 2, column = 0)

Change position of checkbox relative to text in Tkinter's Checkbutton

How can I change where the text is relative to the checkbox for a Tkinter Checkbutton?
By default, the text is to the right of the checkbox. I would like to change that, so the text is on the left or above of the checkbox.
I know this can be done by creating a Label with the required text and delicately positioning the two near each other, but I'd prefer to avoid that method.
Some sample code:
from Tkinter import *
root = Tk()
Checkbutton(root, text="checkButon Text").grid()
root.mainloop()
Well, I don't think you can do it directly, but you can do something that looks as it should. It is the Label-solution, but I altered it slightly, so the resulting compound of Checkbutton and Label is treated as a single Widget as wrapped in a Frame.
from Tkinter import *
class LabeledCheckbutton(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.checkbutton = Checkbutton(self)
self.label = Label(self)
self.label.grid(row=0, column=0)
self.checkbutton.grid(row=0, column=1)
root = Tk()
labeledcb = LabeledCheckbutton(root)
labeledcb.label.configure(text="checkButton Text")
labeledcb.grid(row=0, column=0)
root.mainloop()
When you create multiple frames (with their respective content - Checkbutton and Label) you can handle them easily. That way you would just have to position the Frames like you would do it with the Checkbuttons.

Categories

Resources