I'm making a chess board in Tkinter with chess piece images to get used to the module, and have success with placing the first N-1 images in a row in an NxN grid.
However, upon coding an Nth image on any row, the row in question shrinks vertically.
I'm trying to use an OOP approach and am thinking there may be a method i'm misusing or missing entirely.
I've tried adjusting the arguments for the grid() method such as row and column, but they don't appear to affect the result. Adjusting the width and height of the buttons did not do anything significant. I used a 4x3 grid as a testing example and the images fit with the grid if I leave an empty button without an image in a row.
import tkinter as tk
class ChessBoard(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self,parent,*args,**kwargs)
self.parent = parent
self.button_identities = [[[] for x in range(3)] for y in range(3)]
self.pictures = {"Rook":tk.PhotoImage(file="rook.png")}
self.initialize_board()
def initialize_board(self):
for r in range(3):
for c in range(3):
button = tk.Button(self.parent,bg="white",\
width=10,height=5)
button.grid(row=r,column=c,sticky="sewn")
self.button_identities[r][c] = button
self.setup_starting_positions()
def setup_starting_positions(self):
# Commenting out one of the lines in this method keeps the example row intact
# In this example I'm trying to make 3 images fit in a single row without the row shrinking
self.button_identities[0][0].config(image=self.pictures["Rook"])
self.button_identities[0][1].config(image=self.pictures["Rook"])
self.button_identities[0][2].config(image=self.pictures["Rook"])
class MainApplication(tk.Frame):
# The Main Application hosts the Chessboard frame
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self,parent,*args,**kwargs)
self.parent = parent
self.parent.geometry('{}x{}'.format(1000,800))
self.chessboard = ChessBoard(self)
self.chessboard.grid_propagate(False)
self.chessboard.grid(row=1,column=1,columnspan=3)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).grid(row=0,column=0)
root.mainloop()
I expected the buttons to show some sign of changing when I tried editing the grid() methods or width/height arguments when initializing a Button, but nothing remarkable seemed to change. I feel as though I'm not understanding how the Grid manager works in this case. Sorry for the lack of images.
In empty Button (or with text) height=5 means 5 lines of text. When you put image then it means 5 pixels.
When you have empty button in row then it has size 5 lines and it is the highest widget in row so other buttons are resized to row size. When you add last button then all buttons has height 5 pixels and there is bigger widget to resize row.
So you have to set width and height in pixels and put all images.
Or simple remove width, height from buttons and they will use width, height of image.
button = tk.Button(self.parent, bg="white")
See: Button
Related
I have some code that is supposed to put a label into a frame that is set to a certain size. But when it inserts it, the frame shrinks to the smallest size that is just big enough to fit the label in. What am I doing wrong?
Here's my code for the frame:
problem_fr = LabelFrame(root,text="Solve this problem:",height=200,width=400)
problem_fr.grid(row=0,column=0,columnspan=3)
Here's my code to put the label into the frame:
def erase_widgets(frame):
for widgets in frame.winfo_children():
widgets.destroy()
def draw_problem(problem):
erase_widgets(problem_fr)
curr_prob = Label(problem_fr,text=problem)
curr_prob.grid(row=0,column=0)
I want to create a side bar like the one ABOVE that is collapsible.
In the collapsed form, the side bar will only have icons for the option and when you hover your mouse over the bar then it will expand showing you the description/name of the icon.
If you click on the icon then it will take you to a function designated to that icon such as settings page.
On the right I will have the main home page with a lot of buttons.
I have no clue how you would do this so please help me start this off and I should manage with the rest.
Is it also possible to do this with only the basic Tkinter or do I need to import more modules via pip (not preferable)
So far I have this:
def loginnow(name):
global login
login.destroy()
login= Tk()
screen_width = login.winfo_screenwidth()
screen_height = login.winfo_screenheight()
screen_height=str(screen_height)
screen_width=str(screen_width)
screen = screen_width+"x"+screen_height
login.geometry(screen)
login.title("Logged in as %s"%name)
The side bar will include
Settings = https://www.iconpacks.net/icons/2/free-settings-icon-3110-thumb.png
I will manage with the other icons, once I get a general idea.
I want the menu to be visible without having to click on something for it to appear.
Explanation:
This can actually be achieved by playing around with bindings. Take a look at this rough example below:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.geometry('600x600')
min_w = 50 # Minimum width of the frame
max_w = 200 # Maximum width of the frame
cur_width = min_w # Increasing width of the frame
expanded = False # Check if it is completely exanded
def expand():
global cur_width, expanded
cur_width += 10 # Increase the width by 10
rep = root.after(5,expand) # Repeat this func every 5 ms
frame.config(width=cur_width) # Change the width to new increase width
if cur_width >= max_w: # If width is greater than maximum width
expanded = True # Frame is expended
root.after_cancel(rep) # Stop repeating the func
fill()
def contract():
global cur_width, expanded
cur_width -= 10 # Reduce the width by 10
rep = root.after(5,contract) # Call this func every 5 ms
frame.config(width=cur_width) # Change the width to new reduced width
if cur_width <= min_w: # If it is back to normal width
expanded = False # Frame is not expanded
root.after_cancel(rep) # Stop repeating the func
fill()
def fill():
if expanded: # If the frame is exanded
# Show a text, and remove the image
home_b.config(text='Home',image='',font=(0,21))
set_b.config(text='Settings',image='',font=(0,21))
ring_b.config(text='Bell Icon',image='',font=(0,21))
else:
# Bring the image back
home_b.config(image=home,font=(0,21))
set_b.config(image=settings,font=(0,21))
ring_b.config(image=ring,font=(0,21))
# Define the icons to be shown and resize it
home = ImageTk.PhotoImage(Image.open('home.png').resize((40,40),Image.ANTIALIAS))
settings = ImageTk.PhotoImage(Image.open('settings.png').resize((40,40),Image.ANTIALIAS))
ring = ImageTk.PhotoImage(Image.open('ring.png').resize((40,40),Image.ANTIALIAS))
root.update() # For the width to get updated
frame = Frame(root,bg='orange',width=50,height=root.winfo_height())
frame.grid(row=0,column=0)
# Make the buttons with the icons to be shown
home_b = Button(frame,image=home,bg='orange',relief='flat')
set_b = Button(frame,image=settings,bg='orange',relief='flat')
ring_b = Button(frame,image=ring,bg='orange',relief='flat')
# Put them on the frame
home_b.grid(row=0,column=0,pady=10)
set_b.grid(row=1,column=0,pady=50)
ring_b.grid(row=2,column=0)
# Bind to the frame, if entered or left
frame.bind('<Enter>',lambda e: expand())
frame.bind('<Leave>',lambda e: contract())
# So that it does not depend on the widgets inside the frame
frame.grid_propagate(False)
root.mainloop()
I have explained the code using comments to understand on-the-go. The icons where taken from Flat Icons. As can be seen, it has its own disadvantage, but you can quite mimic something closer to what you have shown, with this. You can improve this more by creating custom widgets to hold both icon and text when expanded and so on.
Output:
Updated image code(without PIL):
home = PhotoImage(file='home.png') # Make sure the image size is comparable to the minimum frame width
settings = PhotoImage(file='settings.png')
ring = PhotoImage(file='ring.png')
Although tkinter doesn't have anything like this built-in, you have all of the tools to implement it. You start by creating a frame to hold the sidebar, and then bind to the <Enter> and <Leave> events on the frame to show and hide it.
There are at least three ways to show and hide it. For example, if each item was a button with an image and text and added to the frame with pack you could simply add or remove the text portion of the button to cause it to shrink or example.
Or, if using grid and creating the icon and text as separate widgets, you could use grid_remove to remove everything in the second column, causing the frame to shrink.
Or, you could use place to add the sidebar to the root window, and use place to change the width of the frame when you show or hide it.
I am trying to place images next to each other side by side but the labels overlap and the label sticks out.
from tkinter import *
root = Tk()
root.geometry("1000x700")
root.resizable(0, 0)
##############################################
TFi = PhotoImage(file="images/Topframe.png")
TF = Label(root, image=TFi)
TF.place(x=-3, y=-3)
BFi = PhotoImage(file="images/Botframe.png")
BF = Label(root, image=BFi)
BF.place(x=-3, y=650)
LF1i = PhotoImage(file="images/LeftFrame1.png")
LF1 = Label(root, image=LF1i)
LF1.place(x=-3, y=50)
##############################################
root.mainloop()
Is it possible to place an image in Tkinter without a Label or canvas
Your most common choices for labels are a canvas or a label. You can also put images on buttons, and embed them in a text widget.
The best choice for creating labels that are next to each other are to use pack or grid. pack is good if you're making a single horizontal or vertical grouping, but grid is better if you're making both rows and columns of widgets.
You can use place, but that requires that you do all of the math to compute the location of each image, and usually results in a user interface that isn't very resilient to changes in screen resolution. It also sometimes ends up causing you to have to do changes to every widget even if you only want to tweak the layout slightly.
My guess for why they overlap is that you aren't aware that the coordinates you give place by default specify the center of the image rather than the upper-left corner. You can specify which part of the image is at the given coordinate with the anchor option.
the label sticks out.
I'm not exactly sure what you mean by that, but if you mean that it has a 3D appearance, you can control that by giving it a borderwidth of zero and/or a relief of "flat".
I am trying to make a tkinter label containing text with width and height defined by pixel dimensions. It is made clear that a label widget containing text's size is defined in terms of its text size in the docs:
If the label displays text, the size is given in text units. If the label displays an image, the size is given in pixels (or screen units). If the size is set to 0, or omitted, it is calculated based on the label contents.
I have tried using this information to achieve what I am trying to do. I want to create labels of fixed width 700 and variable height as I am creating a timetable application. Here's an example of what I have tried so far:
import tkinter as tk
root = tk.Tk()
root.geometry("800x600")
height = 20
label = tk.Label(root,width=700,bg="white",text="test",borderwidth=0,font=("Calibri",height))
label.place(x=10,y=10)
root.mainloop()
This almost achieves what I want in terms of height, but I would expect the height to be 1 pixel when height = 1, but it is actually 5 pixels (I've got very good eyesight!). As for width, its completely off the screen as its 700 times the width of a character.
Using individual frames as each "label" and then creating label widgets as children of these frames does not work either, as the frame just gets resized to fit the label. My question is: is there a way to create a text-containing label sized by pixel dimensions?
I have found a solution in my case. I used a frame, disabled its pack_propagate and made a label the child of this frame. I hope others will find it useful. This question helped me a lot.
import tkinter as tk
root = tk.Tk()
root.geometry("800x600")
height = 20
label_frame = tk.Frame(root,width=700,height=height,bg="white")
label_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(label_frame,bg="white",fg="black",text="test",font=("Calibri",15)).pack()
label_frame.place(x=10,y=10)
root.mainloop()
doing a timetable is something that lends itself to using the grid packing method, which has a grid_columnconfigure and a grid_rowconfigure method which can be used to set the minimum and maximum size of a row or column, and the rate at which they expand:
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="Test").grid(column=1, row=1, sticky="nesw")
root.grid_columnconfigure(1, minsize=700)
root.mainloop()
I'm new to Python, and I'm trying to write a program that displays an 8x8 grid. The vertical lines are supposed to be red and the horizontal lines are supposed to be blue. But I can't seem to figure it out. I know it must be in a loop, but I'm not sure even where to start. Please help!
Here's my code so far:
from tkinter import *
class Canvas:
def __init__(self):
self.window = Tk()
self.window.title("Grid")
self.canvas = Canvas(window, width = 200, height = 200,
bg = "white")
self.canvas.pack()
def drawGrid(self):
self.canvas.create_line()
Thanks!
Take a look at http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_line-method for details about the create_line() method.
You need two arguments: the coordinates of the line and a fill color. The coordinates are a list of [x0, y0, x1, y1], and they correspond to pixel values with an origin at the top-left corner of the parent widget, so to draw a horizontal green line on your 200x200 Canvas, you'd write:
self.canvas.create_line(0,0,200,200, fill='green')
To create a grid of lines, a for or while loop would work, which modified the list of coordinates at every iteration and passed it to a new create_line() function at the end of each loop.
That should get you started.