I want to create a GUI program base on tkinter. One of the widgets is Text. I want to add a horizontal scrollbar in it, but it didn't work.
Where did I make a mistake?
from Tkinter import *
import tkFont
class DpWin(object):
def run(self):
root=Tk()
root.geometry('768x612')
title='dp'
root.title(title)
xscrollbar = Scrollbar(root, orient=HORIZONTAL)
xscrollbar.pack(side=BOTTOM, fill=X)
yscrollbar = Scrollbar(root)
yscrollbar.pack(side=RIGHT, fill=Y)
text = Text(root,xscrollcommand=xscrollbar.set,yscrollcommand=yscrollbar.set)
text.pack()
xscrollbar.config(command=text.xview)
yscrollbar.config(command=text.yview)
text.insert(END,'a'*999)
mainloop()
def start(self):
self.b_start.config(state=DISABLED)
self.b_stop.config(state=ACTIVE)
def stop(self):
self.b_stop.config(state=DISABLED)
self.b_start.config(state=ACTIVE)
if __name__=='__main__':
win=DpWin()
win.run()
I've modified your code according to here. There are 2 main differences.
I made it so the textbox doesn't wrap. If you wrap text, there is nothing for the horizontal scrollbar to scroll to.
I used the grid geometry manager on a frame to keep the scrollbars and text widgets together. The advantage to using .grid is that you actually get scrollbars which are the correct width/height (something you can't achieve with pack).
...
from Tkinter import *
import tkFont
class DpWin(object):
def run(self):
root=Tk()
root.geometry('768x612')
title='dp'
root.title(title)
f = Frame(root)
f.pack()
xscrollbar = Scrollbar(f, orient=HORIZONTAL)
xscrollbar.grid(row=1, column=0, sticky=N+S+E+W)
yscrollbar = Scrollbar(f)
yscrollbar.grid(row=0, column=1, sticky=N+S+E+W)
text = Text(f, wrap=NONE,
xscrollcommand=xscrollbar.set,
yscrollcommand=yscrollbar.set)
text.grid(row=0, column=0)
xscrollbar.config(command=text.xview)
yscrollbar.config(command=text.yview)
text.insert(END, 'a'*999)
mainloop()
def start(self):
self.b_start.config(state=DISABLED)
self.b_stop.config(state=ACTIVE)
def stop(self):
self.b_stop.config(state=DISABLED)
self.b_start.config(state=ACTIVE)
if __name__=='__main__':
win=DpWin()
win.run()
There is one comment regarding making both x and y scrollbars work within the pack framework. Here is a minimal example:
import tkinter as tk
from tkinter import X, Y, BOTTOM, RIGHT, LEFT, Y, HORIZONTAL
class TextExample(tk.Frame):
def __init__(self, master=None):
super().__init__()
sy = tk.Scrollbar(self)
sx = tk.Scrollbar(self, orient=HORIZONTAL)
editor = tk.Text(self, height=500, width=300, wrap='none')
sx.pack(side=BOTTOM, fill=X)
sy.pack(side=RIGHT, fill=Y)
editor.pack(side=LEFT, fill=Y)
sy.config(command=editor.yview)
sx.config(command=editor.xview)
self.pack()
def main():
root = tk.Tk()
root.geometry("800x500+0+0")
app = TextExample(master=root)
root.mainloop()
if __name__ == '__main__':
main()
Related
I'm trying to make a sub window for configuration settings. But the text in the sub window is invisible. I am reading correctly but I cannot see the text. Below is my sample code with the problem:
import tkinter as tk
from tkinter import ttk
from tkinter import Menu
class Frames:
def __init__(self):
self.port_com = None
def main_frame(self, win):
# Main Frame
main = ttk.LabelFrame(win, text="")
main.grid(column=0, row=0, sticky="WENS", padx=10, pady=10)
return main
def dut_configuration_frame(self, win):
# Configuration Frame
dut_config_frame = ttk.LabelFrame(win, text="Config")
dut_config_frame.grid(column=0, row=0, sticky='NWS')
# Port COM
ttk.Label(dut_config_frame, text="Port COM").grid(column=0, row=0)
self.port_com = tk.StringVar()
ttk.Entry(dut_config_frame, width=12, textvariable=self.port_com).grid(column=0, row=1, sticky=tk.EW)
self.port_com.set(value="COM7")
print(self.port_com.get())
class ConfigFrames:
def __init__(self):
self.port_com = None
def main_frame(self, win):
# Main Frame
main = ttk.LabelFrame(win, text="")
main.grid(column=0, row=0, sticky="WENS", padx=10, pady=10)
return main
def configuration_frame(self, win):
# Configuration Frame
dut_config_frame = ttk.LabelFrame(win, text="Config")
dut_config_frame.grid(column=0, row=0, sticky='NWS')
# Port COM
ttk.Label(dut_config_frame, text="Port COM").grid(column=0, row=0)
self.port_com = tk.StringVar()
ttk.Entry(dut_config_frame, width=12, textvariable=self.port_com).grid(column=0, row=1, sticky=tk.EW)
self.port_com.set(value="COM5")
print(self.port_com.get())
def menu_bar(win):
def _config():
config_frame = ConfigFrames()
config_window = tk.Tk()
config_window.title("Sub window")
config_window.geometry("200x200")
config_window.resizable(0, 0)
main = config_frame.main_frame(config_window)
config_frame.configuration_frame(main)
config_window.mainloop()
# Menu
menuBar = Menu(win)
win.config(menu=menuBar)
settingsMenu = Menu(menuBar, tearoff=0)
settingsMenu.add_command(label="Config", command=_config)
menuBar.add_cascade(label="Settings", menu=settingsMenu)
frames = Frames()
win = tk.Tk()
win.title("Main window")
win.geometry("200x200")
win.resizable(0, 0)
menu_bar(win)
main = frames.main_frame(win)
frames.dut_configuration_frame(win)
win.mainloop()
As you can see in main window it is visible, but in sub window it is invisible.
And printing in console is correct:
I'm creating a console that has a single Label packing into a frame. I'm using pack and not grid or place.
Currently the label is placed into the frame, and is configured to expand. However the expansion doesn't stretch to expand to every edge. Also, when the window is resized, the Label widget doesn't resize together with the frame.
#!/usr/bin/python3
import tkinter as tk
import time as t
class app(tk.Tk):
def __init__(self):
super().__init__()
self.canvas=tk.Canvas(self,bg="black")
self.frame=tk.Canvas(self.canvas,bg="black")
self.scrollbar=tk.Scrollbar(self,orient="vertical",command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.title("label insert and scrollbar test")
self.geometry("900x400")
self.scrollbar.pack(side=tk.RIGHT,fill=tk.Y)
self.canvas.pack(fill=tk.X,expand=1)
self.frame.pack(fill=tk.X,expand=1)
self.canvas.create_window((0,0),anchor='n',window=self.frame)
self.frame.bind("<Configure>",self.onFrameConfigure)
def onFrameConfigure(self,event):
pass
def run(self):
ntime=t.asctime(t.gmtime())
n=tk.Label(self.frame,text=ntime,anchor="nw",justify=tk.LEFT)
n.config(bg="black",fg="white",font=("Courier",12))
n.pack(fill=tk.BOTH,expand=1,padx=0)
self.after(800,self.run)
if __name__=="__main__":
m=app()
m.run()
m.mainloop()
How do i get this to work such that the Label fills the entire frame?
There are a couple of reason why your frame is not expanding to fit the canvas. You have a few issues that we need to correct first.
self.frame=tk.Canvas(self.canvas,bg="black") is not actually a frame.
onFrameConfigure is not doing anything and this is an important part to getting the frame to resize.
self.frame.bind() should be self.canvas.bind() as we are using the even that the canvas has changed in some way to trigger the function to resize the frame.
self.canvas.create_window((0,0),anchor='n',window=self.frame) needs a tag set for the frame so we can configure it in the function later.
See below code example and let me know if you have any questions.
import tkinter as tk
import time as t
class App(tk.Tk):
def __init__(self):
super().__init__()
self.canvas = tk.Canvas(self, bg="black")
self.frame = tk.Frame(self.canvas, bg="black")
self.scrollbar = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.title("label insert and scrollbar test")
self.geometry("900x400")
self.scrollbar.pack(side='right', fill='y')
self.canvas.pack(fill='x', expand=True)
self.frame.pack(fill='x', expand=True)
self.canvas.create_window((0, 0), anchor='n', window=self.frame, tags='my_frame')
self.canvas.bind("<Configure>", self.onFrameConfigure)
self.run()
def onFrameConfigure(self, event):
width = event.width
self.canvas.itemconfigure(my_frame, width=width)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
def run(self):
ntime = t.asctime(t.gmtime())
n = tk.Label(self.frame, text=ntime, anchor="nw", justify='left')
n.config(bg="black", fg="white", font=("Courier", 12))
n.pack(fill='both', expand=True, padx=0)
self.after(800, self.run)
if __name__ == "__main__":
App().mainloop()
Update:
The below example changes some background colors and padding so you can see where exactly things are expanding.
import tkinter as tk
import time as t
class App(tk.Tk):
def __init__(self):
super().__init__()
self.canvas = tk.Canvas(self, bg="black")
self.frame = tk.Frame(self.canvas, bg='darkblue',)
self.scrollbar = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.title("label insert and scrollbar test")
self.geometry("900x400")
self.scrollbar.pack(side='right', fill='y')
self.canvas.pack(fill='x', expand=True)
self.frame.pack(fill='both', expand=True)
self.canvas.create_window((0, 0), anchor='n', window=self.frame, tags='my_frame')
self.canvas.bind("<Configure>", self.on_canvas_configure)
self.run()
def on_canvas_configure(self, event):
self.canvas.itemconfigure("my_frame", width=event.width, height=event.height)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
def run(self):
ntime = t.asctime(t.gmtime())
tk.Label(self.frame, bg='black', fg='white', text=ntime,
font=("Courier", 12)).pack(fill='both', expand=True , padx=5, pady=5)
self.canvas.config(scrollregion=self.canvas.bbox('all'))
self.after(800, self.run)
if __name__ == "__main__":
App().mainloop()
Example results:
Just to answer my question:
'''
self.canvas.pack(fill=tk.BOTH,expand=1)
self.frame.pack(fill=tk.BOTH,expand=1,padx=1)
'''
Would expand both canvas and frame to fit the entire window. The original code only had fill=tk.X, which expands only in the X direction. For some reason i could have sworn the documentation said tk.X meant in both directions.
I want to make a lot of notebook tabs, and I want to put them in canvas and to add a horizontal scrollbar so that I can scroll trough them.
I set the canvas size, but canvas size keep changing when I add new tab. Also, scrollbar does not work, can you tell me what am I doing wrong?
The program does not show me any error. This is the code:
from tkinter import *
from tkinter import ttk
myApp = Tk()
myApp.title(" Program ")
myApp.geometry("900x500")
CanvasTabs = Canvas(myApp, width=50, height=50)
CanvasTabs.grid(row=0,column=0)
tabs = ttk.Notebook(CanvasTabs, width=100, height=100)
tab1 = ttk.Frame(tabs)
tabs.add(tab1,text=" Tab 1 ")
tab2 = ttk.Frame(tabs)
tabs.add(tab2,text=" Tab 2 ")
tab3 = ttk.Frame(tabs)
tabs.add(tab3,text=" Tab 3 ")
tab4 = ttk.Frame(tabs)
tabs.add(tab4,text=" Tab 4 ")
hbar=Scrollbar(CanvasTabs,orient=HORIZONTAL)
hbar.pack(side=TOP,fill=X)
hbar.config(command=CanvasTabs.xview)
CanvasTabs.config(xscrollcommand=hbar.set)
tabs.pack(expand=1, fill="both")
myApp.mainloop()
I code a widget to fix the problem. Here is a real solution:
https://github.com/muhammeteminturgut/ttkScrollableNotebook
# -*- coding: utf-8 -*-
# Copyright (c) Muhammet Emin TURGUT 2020
# For license see LICENSE
from tkinter import *
from tkinter import ttk
class ScrollableNotebook(ttk.Frame):
def __init__(self,parent,*args,**kwargs):
ttk.Frame.__init__(self, parent, *args)
self.xLocation = 0
self.notebookContent = ttk.Notebook(self,**kwargs)
self.notebookContent.pack(fill="both", expand=True)
self.notebookTab = ttk.Notebook(self,**kwargs)
self.notebookTab.bind("<<NotebookTabChanged>>",self._tabChanger)
slideFrame = ttk.Frame(self)
slideFrame.place(relx=1.0, x=0, y=1, anchor=NE)
leftArrow = ttk.Label(slideFrame, text="\u25c0")
leftArrow.bind("<1>",self._leftSlide)
leftArrow.pack(side=LEFT)
rightArrow = ttk.Label(slideFrame, text=" \u25b6")
rightArrow.bind("<1>",self._rightSlide)
rightArrow.pack(side=RIGHT)
self.notebookContent.bind( "<Configure>", self._resetSlide)
def _tabChanger(self,event):
self.notebookContent.select(self.notebookTab.index("current"))
def _rightSlide(self,event):
if self.notebookTab.winfo_width()>self.notebookContent.winfo_width()-30:
if (self.notebookContent.winfo_width()-(self.notebookTab.winfo_width()+self.notebookTab.winfo_x()))<=35:
self.xLocation-=20
self.notebookTab.place(x=self.xLocation,y=0)
def _leftSlide(self,event):
if not self.notebookTab.winfo_x()== 0:
self.xLocation+=20
self.notebookTab.place(x=self.xLocation,y=0)
def _resetSlide(self,event):
self.notebookTab.place(x=0,y=0)
self.xLocation = 0
def add(self,frame,**kwargs):
if len(self.notebookTab.winfo_children())!=0:
self.notebookContent.add(frame, text="",state="hidden")
else:
self.notebookContent.add(frame, text="")
self.notebookTab.add(ttk.Frame(self.notebookTab),**kwargs)
def forget(self,tab_id):
self.notebookContent.forget(tab_id)
self.notebookTab.forget(tab_id)
def hide(self,tab_id):
self.notebookContent.hide(tab_id)
self.notebookTab.hide(tab_id)
def identify(self,x, y):
return self.notebookTab.identify(x,y)
def index(self,tab_id):
return self.notebookTab.index(tab_id)
def insert(self,pos,frame, **kwargs):
self.notebookContent.insert(pos,frame, **kwargs)
self.notebookTab.insert(pos,frame,**kwargs)
def select(self,tab_id):
self.notebookContent.select(tab_id)
self.notebookTab.select(tab_id)
def tab(self,tab_id, option=None, **kwargs):
return self.notebookTab.tab(tab_id, option=None, **kwargs)
def tabs(self):
return self.notebookContent.tabs()
def enable_traversal(self):
self.notebookContent.enable_traversal()
self.notebookTab.enable_traversal()
Taking Bryan's example on this post and modifying it to include your Notebook code we get a functioning scrollbar that will allow you to scroll over your Notebook widget if it exceeds the limit of the window.
Bryan's example uses the pack() geometry manager however I personally find grid() easier to visualize so I replace pack with grid() in my example.
UPDATE:
import tkinter as tk
import tkinter.ttk as ttk
class Example(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(self, borderwidth=0)
self.frame = tk.Frame(self.canvas)
self.vsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview)
self.vsb.grid(row=1, column=0, sticky="nsew")
self.canvas.configure(xscrollcommand=self.vsb.set)
self.canvas.grid(row=0, column=0, sticky="nsew")
self.canvas.create_window((3,2), window=self.frame, anchor="nw", tags="self.frame")
self.frame.bind("<Configure>", self.frame_configure)
self.populate()
def populate(self):
tabs = ttk.Notebook(self.frame, width=100, height=100)
for tab in range(50):
tabs.add(ttk.Frame(tabs), text=" Tab {} ".format(tab))
tabs.grid(row=0, column=0, sticky="ew")
def frame_configure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
app = Example()
app.mainloop()
Updated results:
Per your request in the comments here is a Non-OOP example:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
canvas = tk.Canvas(root, borderwidth=0)
frame = tk.Frame(canvas)
vsb = tk.Scrollbar(root, orient="horizontal", command=canvas.xview)
vsb.grid(row=1, column=0, sticky="nsew")
canvas.configure(xscrollcommand=vsb.set)
canvas.grid(row=0, column=0, sticky="nsew")
canvas.create_window((3,2), window=frame, anchor="nw", tags="frame")
tabs = ttk.Notebook(frame, width=100, height=100)
for tab in range(50):
tabs.add(ttk.Frame(tabs), text=" Tab {} ".format(tab))
tabs.grid(row=0, column=0, sticky="ew")
def frame_configure(event):
global canvas
canvas.configure(scrollregion=canvas.bbox("all"))
frame.bind("<Configure>", frame_configure)
root.mainloop()
I am using the following code, I am having 2 issues,
1. When I try to maximize the window, the Entry widget is not aligned/maximized along with main window and see a gap between scrolled text and Entry widget.
2. Second I am trying to set my cursor in Entry widget when I try to open or whenever my app is active but it's not working for some reason. Any Idea what am I making mistakes?
import tkinter as tk
from tkinter import scrolledtext
class Main:
def __init__(self, master):
self.master = master
master.title("Main")
width = master.winfo_screenwidth()
height = master.winfo_screenheight()
master.minsize(width=1066, height=766)
master.maxsize(width=width, height=height)
self.frame = tk.Frame(self.master)
text_area = scrolledtext.ScrolledText(self.master,width=75,height=35)
text_area.pack(side="top",fill='both',expand=True)
text_entry = tk.Entry(self.master,width=65)
text_entry.pack(side="top",fill=X, expand=True,ipady=3, ipadx=3)
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
self.frame.pack()
def initial(self):
print ("initializing")
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == '__main__':
main()
I can address the issue of your entry field not expanding properly.
That is because you have fill=X and this is not a valid input for fill. Instead use fill="x". I believe your 2nd issue with the entry field having a large gap is because you have set expand = True instead change that to expand = False.
That said I prefer to use the grid() geometry manager instead. Take a look at my below example of how to do this with grid and weights.
When using the grid() manager you can tell each widget exactly where you want it along a grid. The use of weights is for telling a row or column how much if any it should expand with the window. This combined with sticky="nsew" will help us control stuff expands within the window.
import tkinter as tk
from tkinter import scrolledtext
class Main(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Main")
width = self.winfo_screenwidth()
height = self.winfo_screenheight()
self.minsize(width=1066, height=766)
self.maxsize(width=width, height=height)
self.rowconfigure(0, weight=1)
self.rowconfigure(1, weight=0)
self.columnconfigure(0, weight=1)
text_area = scrolledtext.ScrolledText(self,width=75,height=35)
text_area.grid(row=0, column=0, ipady=3, ipadx=3, sticky="nsew")
text_entry = tk.Entry(self,width=65)
text_entry.grid(row=1, column=0, ipady=3, ipadx=3, sticky="ew")
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
def initial(self):
print ("initializing")
if __name__ == '__main__':
root = Main()
root.mainloop()
Update:
To clarify on your issue with fill and expand I have updated your code with the correction so you can see it working.
import tkinter as tk
from tkinter import scrolledtext
class Main:
def __init__(self, master):
self.master = master
master.title("Main")
width = master.winfo_screenwidth()
height = master.winfo_screenheight()
master.minsize(width=1066, height=766)
master.maxsize(width=width, height=height)
self.frame = tk.Frame(self.master)
text_area = scrolledtext.ScrolledText(self.master,width=75,height=35)
text_area.pack(side="top",fill='both',expand=True)
text_entry = tk.Entry(self.master,width=65)
text_entry.pack(side="top",fill="x", expand=False, ipady=3, ipadx=3)
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
self.frame.pack()
def initial(self):
print ("initializing")
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == '__main__':
main()
I created two overlapping buttons on a canvas, using tkinter and python 3.4:
Now I would like to bring button1 to the front (the button you cannot see right now, because it is under button2)
self.canvas.lift(self.button1)
But for some reason this does not work. Just nothing happens. Also lowering button2 has no effect. Can you tell me why?
import tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(self, width=400, height=400, background="bisque")
self.canvas.create_text(50,10, anchor="nw", text="Click to lift button1")
self.canvas.grid(row=0, column=0, sticky="nsew")
self.canvas.bind("<ButtonPress-1>", self.click_on_canvas)
self.button1 = tk.Button(self.canvas, text="button1")
self.button2 = tk.Button(self.canvas, text="button2")
x = 40
self.canvas.create_window(x, x, window=self.button1)
self.canvas.create_window(x+5, x+5, window=self.button2)
def click_on_canvas(self, event):
print("lifting", self.button1)
self.canvas.lift(self.button1)
self.canvas.lower(self.button2)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Instead of calling lift() on the canvas, you need to call it on the widget instance directly:
def click_on_canvas(self, event):
print("lifting", self.button1)
self.button1.lift()
self.button2.lower() # Not necessary to both lift and lower
This is only true for widgets displayed via a window on your canvas.
If you were to draw objects such as lines or rectangles, you would use lift() or tag_raise() on the canvas instance as you were doing before.