Drawing on Tkinter grid - python

I couldn't find a way how to Draw line using grid. I want to have a line going from North to South seperating left and right frames.
self.left= Frame(self.tk, bg="black")
self.left.grid(column=0, row = 0, pady=5 ,padx=10, sticky=N)
self.center = Frame (self.tk ,bg= "black")
self.center.grid(column=1, row = 0, pady=5,padx=10, sticky=N)
self.right= Frame(self.tk, bg="black")
self.right.grid(column=2, row = 0, pady=5,padx=10, sticky=N)
I want something like
self.w.create_rectangle(self.centerwidth/2-2, 0, centerwidth/2+2, self.windowheigh, fill="#00CC00", outline = "#00CC00")

If you want to separate the left frame from the right one, you can use a separator from ttk module (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-Separator.html)
Here is an example:
# python3
import tkinter as tk
from tkinter.ttk import Separator, Style
# for python2 :
# import Tkinter as tk
# from ttk import Separator
fen = tk.Tk()
left = tk.Frame(fen, bg="black",width=100, height=100)
# to prevent the frame from adapting to its content :
left.pack_propagate(False)
tk.Label(left, text="Left frame", fg="white", bg="black", anchor="center", justify="center").pack()
left.grid(column=0, row = 0, pady=5 ,padx=10, sticky="n")
sep = Separator(fen, orient="vertical")
sep.grid(column=1, row=0, sticky="ns")
# edit: To change the color of the separator, you need to use a style
sty = Style(fen)
sty.configure("TSeparator", background="red")
right= tk.Frame(fen, bg="black",width=100, height=100)
right.pack_propagate(False)
tk.Label(right, text="Right frame", fg="white", bg="black").pack()
right.grid(column=2, row = 0, pady=5,padx=10, sticky="n")
fen.mainloop()

I don't know what you want exactly, but you can create a line like this.
from Tkinter import *
master = Tk()
w = Canvas(master, width=200, height=100)
w.pack()
w.create_line(100, 0, 100, 100)
#first 2 args are starting point of line and next 2 are ending point of line.
mainloop()
For adding other options, refer to canvas widget of tkinter

Related

Pack 4 frames inside the window (tkinter)

Good day everyone!
I need to place four frames inside the window as shown in the picture, and using the .pack() method.
Now I have 3 frames placed in the window (picture before), and I want to add another one by moving frame f11 to the right. I also attach a picture with how it should look as a result (picture after).
Current part of code:
f11 = LabelFrame(current_tab, borderwidth=2, pady=5, relief=GROOVE, labelwidget=lbl_frm_wdgt_founder)
f11.pack(side=BOTTOM, fill=BOTH)
frame_left = Frame(current_tab, borderwidth=0, relief=GROOVE)
frame_left.pack(side=LEFT, fill=BOTH)
frame_right = LabelFrame(current_tab, labelwidget=lbl_frm_wdgt_arb, borderwidth=2, relief=GROOVE)
frame_right.pack(side=RIGHT, fill=BOTH)
frame_bottom_left = Frame(current_tab, borderwidth=2, relief=GROOVE)
# frame_bottom_left.pack(???)
If you don't want to / can't use grid(), you can use tk.Frame() widgets as generic containers
import tkinter as tk # don't use star imports!
from tkinter import ttk
top_container = tk.Frame(current_tab)
top_container.pack(expand=True, fill=tk.X, side=tk.TOP)
# top container children
frame_left = Frame(
top_container,
borderwidth=0,
relief=tk.GROOVE
)
frame_left.pack(side=tk.LEFT, fill=tk.BOTH)
frame_right = LabelFrame(
top_container,
labelwidget=lbl_frm_wdgt_arb,
borderwidth=2,
relief=tk.GROOVE
)
frame_right.pack(side=tk.RIGHT, fill=tk.BOTH)
bottom_container = tk.Frame(current_tab)
bottom_container.pack(expand=True, fill=tk.X, side=tk.BOTTOM)
# bottom container children
frame_bottom_left = tk.Frame(
bottom_container,
borderwidth=2,
relief=tk.GROOVE
)
frame_bottom_left.pack(side=tk.LEFT, fill=tk.BOTH)
f11 = ttk.LabelFrame(
bottom_container,
borderwidth=2,
pady=5,
relief=tk.GROOVE,
labelwidget=lbl_frm_wdgt_founder
)
f11.pack(side=tk.RIGHT, fill=tk.BOTH)
Since the container frames are packed separately, you can use one on top and another on the bottom
P.S.: Try to avoid * star imports - make sure to properly namespace all constants e.g. tk.LEFT and classes e.g. ttk.LabelFrame

Making a rectangle border around text in Textbox python tkinter

I want to have a rectangle border around certain text that is added to the text box from the end and will be placed at the center.
For example:
Unfortunately, I can't find a way to do that, because I
don't know how to place texts at the center of the line in the text box, and don't know how to surround a text with a rectangle.
You can wrap a Label with border between a space and newline with justify='center' in a Text widget.
Below is an example:
import tkinter as tk
root = tk.Tk()
textbox = tk.Text(root, width=30, height=10)
textbox.pack()
textbox.tag_config('center', justify='center')
def center_label(textbox, **kwargs):
textbox.insert('end', ' ', 'center')
lbl = tk.Label(textbox, bd=3, relief='solid', **kwargs)
textbox.window_create('end', window=lbl)
textbox.insert('end', '\n\n')
center_label(textbox, text='hello', width=10, font='Arial 12 bold')
center_label(textbox, text='............', width=20)
textbox.insert('end', '\nhello\n')
root.mainloop()
Result:
Try putting the text box into it's own frame.
Some thing like this:
from Tkinter import *
root = Tk()
labelframe = LabelFrame(root, text="LabelFrame")
labelframe.pack()
text = Label(labelframe, text="Text inside labelframe")
text.pack()
root.mainloop()
You can add the border to the Entry using relief = "solid", centre the text with outline and you can use grid to align the widgets the way you want.
import tkinter as tk
root = tk.Tk()
root.geometry("400x200")
root.grid_columnconfigure(0, weight = 1)
ent1 = tk.Entry(root, relief = "solid", justify = "center")
ent1.insert(0, "hello")
ent1.grid(row = 0, column = 0, pady = 10)
ent2 = tk.Entry(root, relief = "solid", justify = "center")
ent2.insert(0, ".......")
ent2.grid(row = 1, column = 0, pady = 10)
lab1 = tk.Label(root, text = "hello")
lab1.grid(row = 2, column = 0, sticky = "w")
lab2 = tk.Label(root, text = "hello")
lab2.grid(row = 3, column = 0, sticky = "w")
root.mainloop()
Most of this is straightforward, the root.grid_columnconfigure line makes the grid take up the full width of the root window by giving the first column a weight of 1. The result is very similar to your example:
You could create an Entry widget in the textbox using text.window_create(). You could customize the border of the Entry widget, and you would be able to type text inside it. To make it look more like part of the textbox, you should register events so when the user presses Right and the caret is one character left of the Entry, give the Entry focus using focus_set. You could do the same with the Left.

tkinter: How can I use one vertical scroll bar to scroll two canvases?

In my UI I need two canvases, each with their own horizontal scroll bar, but with a single scroll bar controlling the vertical movement of both canvases.
The following code creates two canvases, each with their own horizontal and vertical scrollbars. How can I get rid of one of the vertical scrollbars? Apologies for the length of code, it's as canonical as I could make it.
from tkinter import *
from tkinter import ttk
class MainUI:
def __init__(self, root):
mainframe = Frame(root)
mainframe.grid(column=0, row=0, sticky=(N,W,E,S))
# set up the first canvas, with large red triangle
canvas1 = Canvas(mainframe, background="turquoise")
canvas1.grid(column=1, row=1, sticky=(N,S,W,E))
canvas1_hscroller = Scrollbar(mainframe, orient="horizontal",command=canvas1.xview)
canvas1_hscroller.grid(column=1, row=2, sticky=(W,E))
canvas1.configure(xscrollcommand=canvas1_hscroller.set)
canvas1_vscroller = Scrollbar(mainframe, orient="vertical",command=canvas1.yview)
canvas1_vscroller.grid(column=2, row=1, sticky=(N,S))
canvas1.configure(yscrollcommand=canvas1_vscroller.set)
points = [0,0,1000,800,800,1000]
triangle = canvas1.create_polygon(points,fill="red")
canvas1.configure(scrollregion=canvas1.bbox("all"))
# set up the second canvas, with large green triangle
canvas2 = Canvas(mainframe, background="orange")
canvas2.grid(column=3, row=1, sticky=(N,S,W,E))
canvas2_hscroller = Scrollbar(mainframe, orient="horizontal",command=canvas2.xview)
canvas2_hscroller.grid(column=3, row=2, sticky=(W,E))
canvas2.configure(xscrollcommand=canvas2_hscroller.set)
canvas2_vscroller = Scrollbar(mainframe, orient="vertical",command=canvas2.yview)
canvas2_vscroller.grid(column=4, row=1, sticky=(N,S))
canvas2.configure(yscrollcommand=canvas2_vscroller.set)
points = [0,0,1000,800,800,1000]
triangle = canvas2.create_polygon(points,fill="green")
canvas2.configure(scrollregion=canvas2.bbox("all"))
root = Tk()
ui = MainUI(root)
root.mainloop()
First make the canvases attributes of the instance:
...
self.canvas1 = Canvas(mainframe, background="turquoise")
...
self.canvas2 = Canvas(mainframe, background="orange")
and use the self. prefix everywhere where you reference previously canvas1 and canvas2
Then define a new method
def v_scroll(self, *args):
self.canvas1.yview(*args)
self.canvas2.yview(*args)
Then just set the scrollbar command to that method:
canvas_vscroller = Scrollbar(..., command=self.v_scroll)
Additionally, don't use from tkinter import *, it is bad practice to use * when importing modules. Import only what you need. For example in this case
from tkinter import Tk, Frame, Canvas, Scrollbar
Complete example (using import tkinter as tk which is even better):
import tkinter as tk
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
# set up the first canvas, with large red triangle
self.canvas1 = tk.Canvas(self, background="turquoise")
self.canvas1.grid(column=0, row=0, sticky='news')
self.canvas1_hscroller = tk.Scrollbar(self, orient="horizontal", command=self.canvas1.xview)
self.canvas1_hscroller.grid(column=0, row=1, sticky='we')
self.canvas1.configure(xscrollcommand=self.canvas1_hscroller.set)
points = [0, 0, 1000, 800, 800, 1000]
self.canvas1.create_polygon(points, fill="red")
self.canvas1.configure(scrollregion=self.canvas1.bbox("all"))
# set up the second canvas, with large green triangle
self.canvas2 = tk.Canvas(self, background="orange")
self.canvas2.grid(column=1, row=0, sticky='news')
self.canvas2_hscroller = tk.Scrollbar(self, orient="horizontal", command=self.canvas2.xview)
self.canvas2_hscroller.grid(column=1, row=1, sticky='we')
self.canvas2.configure(xscrollcommand=self.canvas2_hscroller.set)
points = [0, 0, 1000, 800, 800, 1000]
self.canvas2.create_polygon(points, fill="green")
self.canvas2.configure(scrollregion=self.canvas2.bbox("all"))
self.v_scroller = tk.Scrollbar(self, orient='vertical', command=self.v_scroll)
self.v_scroller.grid(column=2, row=0, sticky='ns')
self.canvas1.config(yscrollcommand=self.v_scroller.set)
def v_scroll(self, *args):
self.canvas1.yview(*args)
self.canvas2.yview(*args)
root = MainWindow()
root.mainloop()
And btw, rows and columns start from 0, so first row and column would be row=0, column=0 accordingly

How do I adjust this frame with scrollbar inside the black space?

I want to fit this frame with scrollbar(refer the provided image) in the black space present in the provided image. How do I do that. The frame should totally cover the black space.
The code for program.The image
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand="false", padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y")
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(side=RIGHT,expand="false", padx=0, pady=200)
for i in range(50):
Button(myframe, image=ML,bg="gray6",bd=0).pack()
mainloop()
EDIT:
Music_Label
Mainscreen bg
So after trying to understand the underlying problem for a while I have reached the conclusion, that the problem is with the fact that the button are being drawn in the myframe and the myframe is outside the mycanvas which contains the scrollbar. So by changing the master widget for the button from myframe to mycanvas, the problem gets fixed now the scrollbar is adjacent to the buttons. BUT, Also now the button r shifted the side since while packing the wrapper1, you did side = RIGHT, so I would also suggest that you use place here instead of pack, since pack depends on the space available and is not reliable if you are using a Background for your GUI and want the buttons within a portion of it.
I have changed the following lines -:
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Changed to mycanvas from myframe
and
wrapper1.place(x = {YOUR X, WHERE THE BOX STARTS}, y = {YOUR Y, WHERE THE BOX STARTS}) # Use place instead..
You can use pack also, and change the padx and pady arguments but it will be tricky to get it to work always as expected.
The fixed code is this -:
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand=False, padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y", expand = False)
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(expand=True, padx=0, pady=200) # Use place instead
for i in range(50):
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Change this to mycanvas from myframe
mainloop()

Make button on opposite side of tkinter GUI

When I put in my code for this name picker, the gui opens and the two buttons are on top of each other. I understand why this is happening but I do not know how to move it.
This is what it looks like
This is what I want it to look like
Code:
from tkinter import *
import tkinter
import random
names =['Sledge','Thatcher','Ash','Thermite','Twitch','Montagne','Glaz','Fuze','Blitz','IQ','Buck','Blackbeard','Capitão','Hibana']
name = ["Smoke","Mute","Castle","Pulse","Doc","Rook","Kapkan","Tachanka","Jäger","Bandit","Frost","Valkyrie","Caveira","Echo"]
def pickName():
nameLabel.configure(text=random.choice(names))
def pickName1():
nameLabel.configure(text=random.choice(name))
root = tkinter.Tk()
root.title("Operator Picker")
root.geometry("400x100")
nameLabel = tkinter.Label(root, text="", font=('Helvetica', 32))
nameLabel.pack()
Grid()
pickButton1 = tkinter.Button(text="Pick Attack", command=pickName)
pickButton1.pack()
pickButton1.place(bordermode=OUTSIDE,height=100, width= 100)
pickButton2 = tkinter.Button(text="Pick Defend", command=pickName1)
pickButton2.pack()
pickButton2.place(bordermode=OUTSIDE,height=100, width= 100)
root.mainloop()
You don't need to pack and place your buttons, you can just pack them. If you want them on opposite sides, try this
pickButton1 = tkinter.Button(root, text="Pick Attack", command=pickName, height=100, width=100)
pickButton1.pack(side=tkinter.LEFT)
pickButton2 = tkinter.Button(root, text="Pick Defend", command=pickName1, height=100, width=100)
pickButton2.pack(side=tkinter.RIGHT)
Edit:
When you define the width and height of an object containing text, it is measured in lines and characters instead of pixels, if you want those exact sizes, you need to add frames
f1 = tkinter.Frame(root, height=100, width=100) #defines frame size in pixels
f1.pack(side=tkinter.LEFT) #packs on the left
f1.pack_propagate(0) #tells frame not to let children control size
pickButton1 = tkinter.Button(f1, command=pickName, text="Pick Attack")
pickButton1.pack(fill=tkinter.BOTH, expand=1) #takes up all available space
f2 = tkinter.Frame(root, height=100, width=100)
f2.pack(side=tkinter.RIGHT)
f2.pack_propagate(0)
pickButton2 = tkinter.Button(f2, command=pickName1, text="Pick Defend")
pickButton2.pack(fill=tkinter.BOTH, expand=1)

Categories