I have a python tkinter program and i have two text widgets that need to be side by side. How can i get the two widgets to take up exactly half of the window which is 400 x 400 px ?
My Code is here
import tkinter
class MyApp:
def __init__(self):
self.root = tkinter.Tk()
self.root.title("App")
self.root.geometry("400x400")
self.root.update()
self.box1Text = tkinter.Text(self.root)
self.box1Text.pack(fill=tkinter.BOTH,side=tkinter.LEFT)
self.box2Text = tkinter.Text(self.root)
self.box2Text.pack(fill=tkinter.BOTH,side=tkinter.RIGHT)
App = MyApp()
By default the text widget wants to be 80 characters wide by 24 characters tall. If you don't specify a size, tkinter will do its best to make sure the widget is that size. If it can't fit all the widgets at their requested size it will start shrinking widgets, starting with the last widget created. That is why the first widget takes up all (or most) of the window -- tkinter tries to display it, and then will give any left-over room to the next widget. In this case there is zero left over room.
Since you are giving the window an explicit size, you can give the text widget a width and height of 1 (one), and then let the geometry manager expand the widget to fill the extra space. You also need to tell pack to let the widget expand. So, add width=1, height=1 to the definition of the widgets, and then add expand=True when packing the widgets in the frame.
Here is a working example:
import tkinter
class MyApp:
def __init__(self):
self.root = tkinter.Tk()
self.root.title("App")
self.root.geometry("400x400")
self.root.update()
self.box1Text = tkinter.Text(self.root, width=1, height=1)
self.box1Text.pack(fill=tkinter.BOTH,side=tkinter.LEFT, expand=True)
self.box2Text = tkinter.Text(self.root, width=1, height=1)
self.box2Text.pack(fill=tkinter.BOTH,side=tkinter.RIGHT, expand=True)
App = MyApp()
App.root.mainloop()
Related
I am aware that you cannot use different types of geometry managers within the same Tkinter window, such as .grid() and .pack(). I have a window that has been laid out using .grid() and I am now trying to add a status bar that would be snapped to the bottom of the window. The only method I have found online for this is to use .pack(side = BOTTOM), which will not work since the rest of the window uses .grid().
Is there a way that I can select the bottom of the window to place widgets from when using .grid()?
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
class sample(Frame):
def __init__(self,master=None):
Frame.__init__(self, master)
self.status = StringVar()
self.status.set("Initializing")
statusbar = Label(root,textvariable = self.status,relief = SUNKEN, anchor = W)
statusbar.pack(side = BOTTOM, fill = X)
self.parent1 = Frame()
self.parent1.pack(side = TOP)
self.createwidgets()
def createwidgets(self):
Label(self.parent1,text = "Grid 1,1").grid(row = 1, column = 1)
Label(self.parent1,text = "Grid 1,2").grid(row = 1, column = 2)
Label(self.parent1,text = "Grid 2,1").grid(row = 2, column = 1)
Label(self.parent1,text = "Grid 2,2").grid(row = 2, column = 2)
if __name__ == '__main__':
root = Tk()
app = sample(master=root)
app.mainloop()
So using labels since I was kinda lazy to do other stuff, you can do frames to ensure that each section of your window can be packed/grid as required. Frames will be a useful tool for you to use when trying to arrange your widgets. Note that using a class can make things a little easier when deciding your parents. So imagine each frame is a parent and their children can be packed as required. So I would recommend drawing out your desired GUI and see how you will arrange them. Also if you want to add another frame within a frame simply do:
self.level2 = Frame(self.parent1)
You can check out additional settings in the docs
http://effbot.org/tkinterbook/frame.htm
PS: I am using a class hence the self, if you don't want to use classes then its okay to just change it to be without a class. Classes make it nicer to read though
Just give it a row argument that is larger than any other row. Then, give a weight to at least one of the rows before it.
Even better is to use frames to organize your code. Pack the scrollbar on the bottom and a frame above it. Then, use grid for everything inside the frame.
Example:
# layout of the root window
main = tk.Frame(root)
statusbar = tk.Label(root, text="this is the statusbar", anchor="w")
statusbar.pack(side="bottom", fill="x")
main.pack(side="top", fill="both", expand=True)
# layout of the main window
for row in range(1, 10):
label = tk.Label(main, text=f"R{row}")
label.grid(row=row, sticky="nsew")
main.grid_rowconfigure(row, weight=1)
...
I'm kind of new to tkinter. I tried to create a Text widget on the left side at 0,0, but it appears in the middle, like a default pack().
Here is my code:
from Tkinter import *
# the ui of the main window
class Ui(object):
# the init of the client object
def __init__(self):
self.root = Tk()
self.mid_height = self.root.winfo_screenheight() / 2
self.mid_width = self.root.winfo_screenwidth() / 2
self.root.title("Journey-opening")
self.root.geometry("600x600+{}+{}".format(self.mid_width - 300, self.mid_height - 300))
self.root.resizable(width=0, height=0)
self.cyan = "#0990CB"
self.root["background"] = self.cyan
self.frame = Frame(self.root)
self.frame.pack()
self.chat_box = Text(self.frame, height=30, width=50)
self.chat_box.pack(side=LEFT)
def open(self):
self.root.mainloop()
wins = Ui()
wins.open()
I also tried with grid method but it did not change anything, and also created another widget because maybe it needs 2 widgets at least.
I guess its something with my frame but I follow a tutorial and everything seems fine.
"Pack a text widget on the side doesn't work"
That is incorrect the line self.chat_box.pack(side=LEFT) does pack the Text widget to side. It's just that it is done inside self.frame which allocates exactly as much space needed for the widgets it encapsulates(in this case that is only the text widget) by default. So in a way, the Text widget is packed, not just to left, but to all sides.
In order to have self.chat_box on the upper left corner, you should let frame to occupy more space than needed, in this case, it can simply occupy all space in the x-axis inside its parent(self.root). In order to do that, replace:
self.frame.pack()
with:
self.frame.pack(fill='x') # which is the same as self.frame.pack(fill=X)
I am having a problem with my first tkinter (Python 3) notebook app.
The canvas on which the data is displayed only needs to be 775px wide, by 480px high. This is all very well until the number of tabs makes the window wider than that. All the data is placed on one side and the other is a sea of emptyness. I have tried to make the notebook widget scrollable but I cannot get it to work.
Any advice would be greatly received.
#!/usr/bin/python
# Try to work with older version of Python
from __future__ import print_function
import sys
if sys.version_info.major < 3:
import Tkinter as tk
import Tkinter.ttk as ttk
else:
import tkinter as tk
import tkinter.ttk as ttk
#============================================================================
# MAIN CLASS
class Main(tk.Frame):
""" Main processing
"""
def __init__(self, root, *args, **kwargs):
tk.Frame.__init__(self, root, *args, **kwargs)
self.root = root
self.root_f = tk.Frame(self.root)
self.width = 700
self.height = 300
# Create a canvas and scroll bar so the notebook can be scrolled
self.nb_canvas = tk.Canvas(self.root_f, width=self.width, height=self.height)
self.nb_scrollbar = tk.Scrollbar(self.root_f, orient='horizontal')
# Configure the canvas and scrollbar to each other
self.nb_canvas.config(yscrollcommand=self.nb_scrollbar.set,
scrollregion=self.nb_canvas.bbox('all'))
self.nb_scrollbar.config(command=self.nb_canvas.xview)
# Create the frame for the canvas window, and place
self.nb_canvas_window = tk.Frame(self.nb_canvas, width=self.width, height=self.height)
self.nb_canvas.create_window(0, 0, window=self.nb_canvas_window)
# Put the whole notebook in the canvas window
self.nb = ttk.Notebook(self.nb_canvas_window)
self.root_f.grid()
self.nb_canvas.grid()
self.nb_canvas_window.grid()
self.nb.grid(row=0, column=0)
self.nb_scrollbar.grid(row=1, column=0, sticky='we')
self.nb.enable_traversal()
for count in range(20):
self.text = 'Lots of text for a wide Tab ' + str(count)
self.tab = tk.Frame(self.nb)
self.nb.add(self.tab, text=self.text)
# Create the canvas and scroll bar for the tab contents
self.tab_canvas = tk.Canvas(self.tab, width=self.width, height=self.height)
self.tab_scrollbar = tk.Scrollbar(self.tab, orient='vertical')
# Convigure the two together
self.tab_canvas.config(xscrollcommand=self.tab_scrollbar.set,
scrollregion=self.tab_canvas.bbox('all'))
self.tab_scrollbar.config(command=self.tab_canvas.yview)
# Create the frame for the canvas window
self.tab_canvas_window = tk.Frame(self.tab_canvas)
self.tab_canvas.create_window(0, 0, window=self.tab_canvas_window)
# Grid the content and scrollbar
self.tab_canvas.grid(row=1, column=0)
self.tab_canvas_window.grid()
self.tab_scrollbar.grid(row=1, column=1, sticky='ns')
# Put stuff in the tab
for count in range(20):
self.text = 'Line ' + str(count)
self.line = tk.Label(self.tab_canvas_window, text=self.text)
self.line.grid(row=count, column=0)
self.root.geometry('{}x{}+{}+{}'.format(self.width, self.height, 100, 100))
return
# MAIN (MAIN) =======================================================
def main():
""" Run the app
"""
# # Create the screen instance and name it
root = tk.Tk()
# # This wll control the running of the app.
app = Main(root)
# # Run the mainloop() method of the screen object root.
root.mainloop()
root.quit()
# MAIN (STARTUP) ====================================================
# This next line runs the app as a standalone app
if __name__ == '__main__':
# Run the function name main()
main()
OK, so I think I understand now. The tabs are inside the notebook, and inseperable from the notebook. As such, the notebook will always be as wide as the frames within it. To get the effect I wanted I would need put a canvas into the notebook, and then add the tabs the the canvas. And that is not allowed. So back to the drawing board!
If the tabs are of 'constant' width and you know how many will fit the desired (fixed?)size of the window, you could create a "scrolling tabs" widget by hiding the ones that don't fit your width. Create two buttons, left and right that for example hides the one to the right and shows the next hidden one to the left.
If there a way to figure out the width of a tab (fontsize in the label, padding etc?) it could be done more 'dynamic'.
I would recommend combining the solutions from here: Is there a way to add close buttons to tabs in tkinter.ttk.Notebook? (to be able to close a tab) and here: https://github.com/muhammeteminturgut/ttkScrollableNotebook to use buttons instead of a scroll-bar to handle the width issue.
Two changes to get it to work are to load the "notebookTab" variable as the CustomNotebook and to put the closing icon on the left side by switching the order of the innermost children of style.layout in the first answer. This produces a slidable and closeable custom notebook type.
The follwing code
import Tkinter as tk
root = tk.Tk()
labelA = tk.Label(root, text="hello").grid(row=0, column=0)
labelB = tk.Label(root, text="world").grid(row=1, column=1)
root.mainloop()
produces
How can I add a partial border to the Label so that I have
I see a that borderwidth= is a possible option of Label but it handles the four borders.
NOTE: the question is not about padding the cell (which is the essence of the answer that was formerly linked in the duplication comment)
There's not an option or a real easy way to add a custom border, but what you can do is create a class that inherits from Tkinter's Frame class, which creates a Frame that holds a Label. You just have to color the Frame with the border color you want and keep it slightly bigger than the Label so it gives the appearance of a border.
Then, instead of calling the Label class when you need it, you call an instance of your custom Frame class and specify parameters that you set up in the class. Here's an example:
from Tkinter import *
class MyLabel(Frame):
'''inherit from Frame to make a label with customized border'''
def __init__(self, parent, myborderwidth=0, mybordercolor=None,
myborderplace='center', *args, **kwargs):
Frame.__init__(self, parent, bg=mybordercolor)
self.propagate(False) # prevent frame from auto-fitting to contents
self.label = Label(self, *args, **kwargs) # make the label
# pack label inside frame according to which side the border
# should be on. If it's not 'left' or 'right', center the label
# and multiply the border width by 2 to compensate
if myborderplace is 'left':
self.label.pack(side=RIGHT)
elif myborderplace is 'right':
self.label.pack(side=LEFT)
else:
self.label.pack()
myborderwidth = myborderwidth * 2
# set width and height of frame according to the req width
# and height of the label
self.config(width=self.label.winfo_reqwidth() + myborderwidth)
self.config(height=self.label.winfo_reqheight())
root=Tk()
MyLabel(root, text='Hello World', myborderwidth=4, mybordercolor='red',
myborderplace='left').pack()
root.mainloop()
You could simplify it some if you just need, for instance, a red border of 4 pixels on the right side, every time. Hope that helps.
I don't believe there's an easy way of just adding a left border. However, you can definitely fool by using a sunken label ;)
So for example:
root=Tk()
Label(root,text="hello").grid(row=1,column=1)
Label(root,text="world").grid(row=2,column=3)
Label(root,relief=SUNKEN,borderwidth=1,bg="red").grid(row=2,column=2)
Label(root).grid(row=2,column=1)
root.mainloop()
This will create a window like the one that you wanted to see.
I am using the ttk.Progressbar in my app. I have scoured the net for an answer but no avail.
I have the following code which is working well. But I want to change the thickness of the bar.
progressbar = ttk.Progressbar(myGui, orient=HORIZONTAL,
length=400, mode="determinate",
variable=value_progress,
)
progressbar.pack()
I want the length to still be 400, but from the top of the bar to the bottom, I wish to decrease that so its half or less then half. (I want my bar on a diet, so to say)
But I am beating my head against the wall to figure out a solution.
Andy ideas? Thanks in advance.
The ttk progress bar appears to lack the width option in Python.
Using a work around (here) for an issue with a Tkinter Button. From this I have been able to create a working solution.
The key to solving the issue was to add the progress bar to a window inside the canvas. Using a window inside the canvas doesn't cause the canvas to resize when the widget is added which means we can control the width of the progress bar.
I have created some working example code:
from ttk import Progressbar
import Tkinter
class Example(Tkinter.Frame):
def __init__(self, parent):
Tkinter.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
value_progress =50
self.parent.title("Progressbar Thingymawhatsit")
self.config(bg = '#F0F0F0')
self.pack(fill = Tkinter.BOTH, expand = 1)
#create canvas
canvas = Tkinter.Canvas(self, relief = Tkinter.FLAT, background = "#D2D2D2",
width = 400, height = 5)
progressbar = Progressbar(canvas, orient=Tkinter.HORIZONTAL,
length=400, mode="indeterminate",
variable=value_progress,
)
# The first 2 create window argvs control where the progress bar is placed
canvas.create_window(1, 1, anchor=Tkinter.NW, window=progressbar)
canvas.grid()
def main():
root = Tkinter.Tk()
root.geometry('500x50+10+50')
app = Example(root)
app.mainloop()
if __name__ == '__main__':
main()
So to sum up the progress bar is the same size but you just cant see half of it!
If you must use the xpnative theme or themes like it, then you will likely not have the option to change the thickness the conventional way. However if you use the default theme, you can configure the thickness with a style. There are likely other themes that let you do this as well, and if you're going to be playing around a lot with the look and feel of your program, you may wish to use these instead.
from Tkinter import *
from ttk import *
def main():
root = Tk()
s = Style()
s.theme_use("default")
s.configure("TProgressbar", thickness=50)
pb = Progressbar(root, style="TProgressbar")
pb.pack()
root.mainloop()
main()
You can just use the ipady option of pack manager.
progressbar = ttk.Progressbar(myGui, orient=HORIZONTAL,
length=400, mode="determinate",
variable=value_progress,
)
progressbar.pack(ipady=10)