Tkinter: Pack new widgets below other widgets - python

I'm trying to pack the button below the Text and Scrollbar widget.
#!/usr/bin/python
try:
from Tkinter import *
except ImportError:
from tkinter import *
class Chat(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack(anchor=N, fill=BOTH)
self.create_widgets()
self.count = 0
def create_widgets(self):
self.scrolly = Scrollbar(self)
self.scrolly.pack(side=RIGHT, fill=Y)
self.chattext = Text(self, borderwidth=5, yscrollcommand=self.scrolly.set)
self.chattext.pack(side=LEFT)
self.scrolly.config(command=Text.yview(self.chattext))
self.button1 = Button(self, text="Add text", command=self.add_text)
self.button1.pack()
def add_text(self):
self.count += 1
self.chattext.insert("end", "%i\n" % self.count)
self.chattext.update_idletasks()
def main():
root = Tk()
root.title("Test Chat Client")
root.geometry("600x500")
#root.resizable(0,0)
app = Chat(root)
root.mainloop()
if __name__ == "__main__":
main()
This is what it looks like
I want the button to be below and not in between the other widgets.
I have tried the following:
self.button1.pack(after=self.scrolly)
self.button1.pack(after=self.chattext)
How may i pack the button at the bottom?
Another issue is that the scrollbar does not work, when i try to scroll nothing happens.
(Yes, i have tried to fill the Text widget with alot of lines, more than it can view.)
Also, why is the scrollbar viewed/packed outside/"far" away from the Text widget?

Try using the grid geometry manager instead.
http://www.tkdocs.com/tutorial/grid.html

I think you should consider replacing the text field with a ScrolledText field.
It's a lot easier to use and doesn't require manual scrollbar placement.
(Don't use pack to place it though. Use grid)
import tkinter as tk
import tkinter.scrolledtext as tkst
self.chattext = tkst.ScrolledText(
master = self,
wrap = tk.WORD,
width = 20,
height = 10
)

Related

tkinter - dock and undock frame with wm_manage and wm_forget

I found the following in the tk docs:
The wm manage and wm forget commands may be used to perform undocking
and docking of windows.
So I tried wm_manage and wm_forget in this code:
import tkinter as tk
root = tk.Tk()
class MyFigure(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.master = master
self.bc = tk.Button(self, text='confi',
command=lambda:self.configure(bg='red')
)
self.bmanage = tk.Button(self, text='manage',
command = lambda:self.master.wm_manage(self)
)
self.bforget = tk.Button(self, text='forget',
command = lambda:self.master.wm_forget(self)
)
self.bmanage.pack(side='left')
self.bc.pack(side='left')
self.bforget.pack(side='left')
mf = MyFigure(root)
mf.pack()
root.mainloop()
But it dosen't worked out. So I readed more and there is no way I can missunderstand this:
A toplevel widget may be used as a frame and managed with any of the
other geometry managers after using the wm forget command.
So I tried to do something like that:
def _manage(self):
top = self.master.wm_manage(self)
print(top)
def _forget(self):
frame = self.master.wm_forget(self)
print(frame)
But both return None. Am I something missing here? What am I doing wrong?
In order to make wm_forget correctly work, you should pass a toplevel window as argument. For instance, if you add the following lines in the constructor of the class:
self.top = tk.Toplevel()
self.top.title("Top level")
You can then call the method as follows:
self.master.wm_forget(self.top)
Regarding the wm_manage, you should pass as argument the widget you want to convert to a stand alone top-level window. Please keep in mind that you can only use this command with frame, labelframe and toplevel widgets. If you apply this command to your main window Tk, nothing will happen.
A full example converting a frame to a toplevel (pressing button manage) and converting it back to frame (pressing button forget):
import tkinter as tk
root = tk.Tk()
class MyFigure(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.master = master
self.bc = tk.Button(self, text='confi',
command=lambda:self.configure(bg='red')
)
self.bmanage = tk.Button(self, text='manage',
command = lambda:self._manage()
)
self.bforget = tk.Button(self, text='forget',
command = lambda:self._forget()
)
self.bmanage.pack(side='left')
self.bc.pack(side='left')
self.bforget.pack(side='left')
self.frame = tk.Frame(self.master, bg="red", height=100)
self.label=tk.Label(self.frame, text="hi")
self.frame.pack()
self.label.pack(expand=True, fill=tk.BOTH)
def _manage(self):
test=self.master.wm_manage(self.frame)
def _forget(self):
self.master.wm_forget(self.frame)
self.frame.pack()
mf = MyFigure(root)
mf.pack()
root.mainloop()

TopLevel window disappears when using askopenfilename, from tkinter.filedialog

For my program I want the user to select a file, and I am using the tkinter.filedialog library to do this. However, when the askopenfilename dialog is opened, the TopLevelwindow disappears behind the main Tk() window.
How would I stop this from happening?
Here is the code that I have written so far:
from tkinter import *
from tkinter.filedialog import askopenfilename
class MainWin(Tk):
def __init__(self):
super(MainWin, self).__init__()
self.update()
pu = PopUp(self)
self.configure(width=500, height=300)
class PopUp(Toplevel):
def __init__(self, master):
super(PopUp, self).__init__(master)
def entry_set(entry, text):
entry.delete(0, 'end')
entry.insert(END, text)
item_file = StringVar()
item_entry = Entry(self, textvariable=item_file)
item_entry.place(x=80, y=60, height=20, width=300)
item_label = Label(self, text="item file: ", bg="gray74", relief="groove")
item_label.place(x=20, y=60, height=20, width=60)
item_button = Button(self, text="\uD83D\uDCC2", relief="groove",
command=lambda: entry_set(item_entry, askopenfilename()))
item_button.place(x=380, y=60, height=20, width=20)
self.configure(width=460, height=180)
if __name__ == '__main__':
win = MainWin()
win.mainloop()
Edit:
I have realised that using the .grab_set() method works, and will make the
TopLevel() window appear back on top of the Tk() after the file is chosen.
However, this still means the window disappears behind the Tk() window whilst picking a file, I would still love to find a solution to this, even though this is now just a visual problem, not a functional one.
You can just make the Toplevel window a transient window, it will then be kept on top of its parent window:
class PopUp(Toplevel):
def __init__(self, master):
super(PopUp, self).__init__(master)
self.transient(master)

Python-Tkinter Place button on left of frame

How do I place the QUIT button in below code to the extreme right of the Frame?
I tried several things like:
padx
and
self.pack(side="top", anchor="e")
but after trying some 15 times both buttons are coming close to each other. Maybe Some help from anyone would be really appreciated. I need one button on extreme right and other on extreme left
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(anchor='e')
self.pack(side="top", anchor="w")
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
You have several problems here.
First, you're using the wrong geometry manager. The pack geometry manager, as the name implies, packs the widgets as close together as possible. That's not what you want. The grid geometry manager lets you put the widgets into a table-like layout with rows and columns. If you put the Browse button into the first column and the Quit button into the last column, you'll be a step closer.
Second, your Application window contains three child widgets and you're only putting two of them into a geometry manager. How that is going to mess you up I don't even want to think about. So I put the label into column 1, the Quit button into column 2, and the Browse button into column 0. The Quit button I gave a "sticky" value of "e" so it will be attached to the east (right) side of its allocated space.
Third, all the geometry managers try to compact the widgets as much as possible unless you specifically tell it to do otherwise. I told the grid manager to expand column 2 so that the extra space gets assigned to the cell that holds the Quit button.
Fourth, you need to tell the pack manager to expand the top widget so that it spans the entire window. The directive for that is fill="x".
Fifth, you have a redundant call to the pack manager at the end of your createWidgets function.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack(fill="x")
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.Label.grid(row=0, column=1)
self.Run_Main.grid(row=0, column=0, sticky="w")
self.QUIT.grid(row=0, column=2, sticky="e")
self.columnconfigure(2, weight=1)
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
These link, link helped. The other option would be to use tkinter's grid manager, it will be more intuitive and keep you more organized in the future.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.Label.pack(side='left')
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right')
self.pack(side="top", fill=tk.BOTH) # changes here
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
There are two simple fixes you can make in order to get the behavior you want.
First, you need to pack Application so that it fills the window:
class Application(...):
def __init__(...):
...
self.pack(fill="x")
Next, simply pack the quick button on the right side of the window:
self.QUIT.pack(side="right", anchor='e')
Even though the above is all you need to do in this specific example, there are additional things you can do to make your job much easier.
I would recommend creating a frame specifically for the buttons. You can pack it at the top. Then, put the buttons inside this frame, and pack them either on the left or right. You'll get the same results, but you'll find it easier to add additional buttons later.
I also find that it makes the code much easier to read, write, maintain, and visualize when you separate widget creation from widget layout.
class Application(...):
...
def createWidgets(self):
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
self.Run_Main = tk.Button(toolbar)
self.Label = tk.Label(toolbar)
self.QUIT = tk.Button(toolbar)
...
self.Run_Main.pack(side="left")
self.Label.pack(side="left", fill="x")
self.QUIT.pack(side="right")
...

Python how to show text in a tkinter application on a none-button

I'm extremely new to python and has started a small project to learn stuff. anyways, as it says in the title, how do I show text in a tkinter application without creating buttons? here's the code if you need it
import tkinter as tk
ulo = 1
hoho = 0
def lul():
global ulo
#ulo = ulo + 1
global hoho
hoho = hoho + ulo
print(hoho)
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.hi_there = tk.Button(self, fg="green")
self.hi_there["text"] = "Pressing buttons is fun,\n isn't it?"
self.hi_there["command"] = self.lel
self.hi_there.pack(side="top")
def lel(self):
lul()
root = tk.Tk()
app = Application(master=root)
app.mainloop()
There are couple options but using Labels are the most fitting one since Label's job is showing text/image.
The Label widget is a standard Tkinter widget used to display a text
or image on the screen. The label can only display text in a single
font, but the text may span more than one line.
def createWidgets(self):
self.lbl = tk.Label(self, text="Pressing buttons is fun, isn't it?")
self.hi_there = tk.Button(self, fg="green")
self.hi_there["text"] = "Let's press"
self.hi_there["command"] = self.lel
self.lbl.pack()
self.hi_there.pack(side="top")
You can use tkinter built-in Label widget to display text :
Here's the code:
from tkinter import *
root=Tk()
def showLabel():
myLabel=Label(root,text="Hello World")
myLabel.pack()
myButton=Button(root,text="Click here",command=showLabel)
myButton.pack()
root.mainloop()

Embedd Buttons with images and scrollbar on a frame using Tkinter (Python)

I am trying to make a on-button click event in a Tkinter window. I have a Tkinter Window on which there are buttons. Pressing one of those buttons,opens up a new Tkinter Window using Toplevel. This window would have a Scrollbar and some other buttons with images on it which can be vertically scrolled down. I could create the two functionalities separately,i.e, I could embedd a button with an image on a Tkinter window and use the Scrollbar but was unable to call the same function with the previous Tkinter window.
The code I am using is -
from Tkinter import *
from ttk import *
class VerticalScrolledFrame(Frame):
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior,
anchor=NW)
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
#if __name__ == "__main__":
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
#from Tkinter import *
print "in constructor"
import Tkinter
import ImageTk
import Image
import cv2
import numpy as np
import cv2.cv as cv
import math
import tkFileDialog
import tkMessageBox
import Tkinter as tk
root = Tk.__init__(self, *args, **kwargs)
def Print():
print "print function"
self.frame = VerticalScrolledFrame(root)
self.frame.pack()
self.label = Label(text="Shrink the window to activate the scrollbar.")
self.label.pack()
compare_button_path = "compare-button.jpg"
image_compare = Image.open(compare_button_path)
image_compare.thumbnail((70,45))
photo_compare = ImageTk.PhotoImage(image_compare)
button = tk.Button(self.frame, width=120, height=40, image=photo_compare,fg='black',bg='medium turquoise', activebackground='indian red',cursor="hand2",bd=6,relief=RAISED, compound=tk.LEFT, text="Compare",command = Print)
button.image = photo_compare
button.pack(side=LEFT)
buttons = []
for i in range(10):
buttons.append(Button(self.frame.interior, text="Button " + str(i)))
buttons[-1].pack()
app = SampleApp()
app.mainloop()
The above written function gives a pretty good result.
But how do I call this function on a button click from another Tkinter window? Changing the initial declaration of root to root = Tk() instead of root = Tk.init(self, *args, **kwargs) throws a
RuntimeError: maximum recursion depth exceeded while calling a Python object.
If I try to keep the function in some other file and import it into my original Tk file and create the object of that class on a button click, the second file gets automatically called during complilation of the original Tk file.
Can somebody please suggest a way out.
I really don't understand your question, even after asking for clarification. You finally wrote in the comments of the question "I simply want to open a tkinter window with buttons and images, on a button click from another Tkinter window".
I don't see what's preventing you from doing that. The only thing I see wrong with your code is that you're simply not creating an instance of Toplevel (well, except for a confusing set of imports). Other than that, your code should work.
For example, if you modify your sample app to look something like this, you can open as many windows as you want:
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
b = Button(self, text="Open a new window", command=self.open_new)
b.pack()
def open_new(self):
top = Toplevel()
self.frame = VerticalScrolledFrame(top)
self.frame.pack()
def Print():
print "print function"
button = Button(self.frame, text="Compare",command = Print)
button.pack(side=LEFT)
buttons = []
for i in range(10):
buttons.append(Button(self.frame.interior, text="Button " + str(i)))
buttons[-1].pack()

Categories