I am doing a simple project in school and I need to make six different buttons to click on. The buttons must have different sizes, but I can't find how do do it. I have made the button by using:
def __init__(self, master):
super().__init__(master)
self.grid()
self.button1 = Button(self, text = "Send", command = self.response1)
self.button1.grid(row = 2, column = 0, sticky = W)
I imagine that something like:
self.button1.size(height=100, width=100)
would work, but it doesn't and I cannot find how to do it anywhere.
I am using Python 3.3.
Configuring a button (or any widget) in Tkinter is done by calling a configure method
"config"
To change the size of a button called button1 you simple call
button1.config( height = WHATEVER, width = WHATEVER2 )
If you know what size you want at initialization these options can be added to the constructor.
button1 = Button(self, text = "Send", command = self.response1, height = 100, width = 100)
I've always used .place() for my tkinter widgets.
place syntax
You can specify the size of it just by changing the keyword arguments!
Of course, you will have to call .place() again if you want to change it.
Works in python 3.8.2, if you're wondering.
Related
I am using tkinter to make a simple drawing tool. I am trying to put an image, fill_bucket.png, on the button. However, when I run the program, the button is incredibly small, like it has no content, and there is not image.
From looking around, it seems like the issue is probably Python's garbage collection, however I have tried many ways of fixing this and nothing seems to work. The simplest thing I have tried is:
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.fillButton = Button(self.button_layout, image=self.fill_bucket_photo, compound='top', font=('Arial', 25), width=1, height=1, command=lambda: self.selectTool('fill'))
However this does not work. I have also tried this solution, which I found here:
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
However when I do self.fillButton.image after assigning self.fillButton, I get an Attribute error for the image attribute.
Everything that I am doing is inside a class, and I can define normal buttons with text fine.
Additionally, doing
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.label = Label(image=self.fill_bucket_photo).grid(row=1, column=1)
shows the image normally.
Full example. On my computer, this code creates a window that contains one blank button with no image on it:
from tkinter import *
class App():
def __init__(self):
self.root = Tk()
self.root.resizable(False, False)
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.fillButton = Button(self.root, image=self.fill_bucket_photo, compound='top', font=('Arial', 25), width=40, height=40).pack()
self.root.mainloop()
app = App()
What are some possible solutions to this?
To increase my understanding and usability of python I have been building a text editor from scratch. What I'm trying to do now is allow the user to change the size of the font. I have this working using an OptionMenu but by using that I have to have a drop down on the interface. What I'm trying to do is somehow put the option menu into a menu on the top bar (with file_menu = Menu(root)) and make it look like a cascade. I don't have my exact code as I'm on mobile and away from my laptop, but I've been thinking about how to do this and I can't figure it out And wasn't able to find it searching earlier. Thanks in advance!
EDIT: I figured out how to do it, and I apologize for not having my original code. My original code was something like the answer posted below me, but I wanted to let the user select font sizes ranging from 8-40 so having
font_size.add_command(label='8', command=lambda: font_size_changer(8))
font_size.add_command(label='10', command=lambda: font_size_changer(10))
font_size.add_command(label='12', command=lambda: font_size_changer(12))
Just looked bad for how many I wanted. I ended up not doing the OptionMenu and IntVar() and went with a for loop like so:
def font_size_changer(clicked_size):
global current_font_size
current_font_size = clicked_size
text.config(font=(current_font, current_font_size, style_combo))
sizes_list = [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
font_size = Menu(menu)
font_menu.add_cascade(label="Font Size", menu=font_size)
for x in range(0, len(sizes_list)):
font_size.add_command(label=str(sizes_list[x]), command=lambda: font_size_changer(sizes_list[x]))
Whilst you could use an OptionMenu to do this Tkinter actually has a native Menu object which we could use to achieve similar results much more cleanly and with less code.
This is actually not too tricky to pull off, see below for an example:
import tkinter as tk
class App:
def __init__(self, root):
self.root = root
self.label = tk.Label(self.root, text="Lorem Ipsum", font=("Comic Sans MS", 44))
self.menubar = tk.Menu(self.root)
self.menu = tk.Menu(self.root, tearoff=0)
self.menu.add_command(label="Small", command=lambda:self.label.config(font=("Comic Sans MS", 22)))
self.menu.add_command(label="Medium", command=lambda:self.label.config(font=("Comic Sans MS", 44)))
self.menu.add_command(label="Big", command=lambda:self.label.config(font=("Comic Sans MS", 66)))
self.menubar.add_cascade(label="Size", menu=self.menu)
self.root.config(menu=self.menubar)
self.label.pack()
root = tk.Tk()
App(root)
root.mainloop()
So let's break this down.
We create two Menu objects menubar and menu. We then store all of the commands inside of menu, this creates a fleshed out Menu object with a few entries in them.
Each command is setup to change the font size of label
We then add a cascade item to menubar selecting the predefined menu as our Menu object for the cascade.
root.config(menu=self.menubar is then used to "draw" the Menu object on our window.
This results in a cascading menu at the top of the screen where each option in the menu allows us to change the font size of a label object.
I've created a couple of buttons using my program and I've made them include images. However, I wish to now remove the border that remains (see http://i.imgur.com/XRlmq39.png for screenshot).
The code for the "back" button as an example:
backbutton = ttk.Button(mainframe, command=homereturn)
backbuttonimage = PhotoImage(file="back.gif")
backbutton.config(image=backbuttonimage)
backbutton.pack()
backbutton.grid(column=0, row=1)
Any help would be greatly appreciated.
backbutton = tk.Button(..., highlightthickness = 0, bd = 0)
This works for Python 3.x, I tried...
icon = PhotoImage(file="lock.png")
self.company_lock_button = Button(self.control_lock_frame, image = icon, highlightthickness = 0, bd = 0)
self.day_lock_button = Button(self.control_lock_frame, image = icon)
self.hour_lock_button = Button(self.control_lock_frame, image = icon)
If you are using images to define a custom button, use the standard button class rather than the ttk button class. This will allow you to set the borderwidth attribute to zero:
import tkinter as tk
...
backbutton = tk.Button(..., borderwidth=0)
(unrelated: there's no point in calling backbutton.pack() and immediately follow it with backbutton.grid(...) - you can only use one for a particular widget, and the last one you call is the one that has any effect. In this case the call to pack is completely useless)
Using a new Mac I had the same issue and none of the other answers fully removed the border surrounding the button. I found that setting highlightbackground to the canvas's background color did the trick.
I had the same issue with imaged buttons. Neither bd=0 nor highlichtthickness=0 alone helped. I even suspected the relief-option to play it's part.
What finally helped (with me): pady=0, padx=0
So for the question asked: backbutton.config(image=backbuttonimage, highlichtthickness=0, pady=0, padx=0) could work.
You can use highlightthickness=0 and bd=0
None of the solutions provided so far worked in my case.
(I can only suspect that this is related to the new MacOS GUI style policy of recent versions (I'm using Big Sur). Also other style options don't work on MacOS, e.g. the relief option doesn't have any effect on the appearance of a button.)
Here is my solution: when you put an image to a button, the options width and height take pixels as unit. When you chose small values, i.e. 10x10, the border seems to be covered by the image. Voilá!
Here's one way you could do it..
Use ttk.Style() to set the button's background color to the color of mainframe.
root_color = "red" #Just an example, can't remember the default tk window color
mainframe = tk.Tk() #or whatever mainframe is
mainframe.configure(bg = root_color)
style = ttk.Style()
style.configure('TButton', background = root_color)
backbutton = ttk.Button(mainframe, command=homereturn)
backbuttonimage=PhotoImage(file="back.gif")
backbutton.config(image=backbuttonimage)
backbutton.pack()
backbutton.grid(column=0, row=1)
As a side note, you don't have to specify style = .. in your ttk button here because you are configuring the default TButton style ttk uses. If you defined a custom style for this button you would have to specify this in the keyword arguements for the button.
An example would be giving your button rounded edges instead of using images to achieve the desired effect.
If you are using the import function as:
from tkinter import *
Button(main_scr, text = "OK", bg = "yellow", bd = 0, command = delete1).pack()
The bd = 0 would not show the border.
Not all themes support changing borderwidth. Try using tkinter.tk instead of using ttk.Button.
Use keyword borderwidth=0 for Button's parameter.
You cannot use both backbutton.pack() and backbutton.grid(). You can select one of them.
Snippet:
from tkinter import ttk
import tkinter as tk
mainframe = tk.Tk()
def homereturn():
print('hello')
backbutton = tk.Button(mainframe, command=homereturn, borderwidth=0)
backbuttonimage = tk.PhotoImage(file="p1.png")
backbutton.config(image=backbuttonimage)
backbutton.pack()
mainframe.mainloop()
Screenshot w/out border:
I'm trying to source out code for a GUI program. I made a simple test and I cannot change the text value on the GUI, no error and nothing happens. Some issue with the mainloop of Tkinter?
serial.py:
import gui
gui.chiplabel.config(text="A.3f V" )
gui.py:
from Tkinter import *
root = Tk()
chiplabel = Label(root, relief=RIDGE, width = 9 , text ="Unknown",
padx=0, pady=0).grid(row = 0,column=5, sticky =W)
root.mainloop()
You have two main problems with your code. It needs to be restructured, and you're making a very common mistake with laying out your widgets.
Organizing your code
The way you have your code structured, your call to configure happens after mainloop exits, and after the widgets have been destroyed. You need to reorganize your code so that the call to mainloop is the last line of code that is executed.
In my opinion this is best accomplished by using classes and objects, but that's not strictly necessary. You simply need to not have any code after you call mainloop.
Laying out the widgets
The problem is this line:
chiplabel = Label( root, relief=RIDGE, width = 9 , text ="Unknown", padx=0, pady=0).grid(row = 0,column=5, sticky =W)
In python, when you do x=y().z(), x is given the value of z(). So, when you do chiplabel = Label(...).grid(...), chiplabel is given the value of grid(...). Grid always returns None, so chiplabel will always be None. Because of this, you can't reconfigure it because you've lost the reference to the widget.
The solution is to create the widget and lay out the widget in two steps.
One way to do this would be to create the UI in a class, e.g.:
import Tkinter as tk # note don't use wildcard imports
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.chiplabel = tk.Label(
self,
padx=0,
pady=0,
relief=tk.RIDGE,
text="Unknown",
width=9,
) # note alphabetical order and consistent spacing
self.chiplabel.grid(
column=5,
row=0,
sticky=tk.W,
) # note grid is separate step
and don't run it in-place, so that you can import the class without running anything. Then your serial.py looks more like:
from gui import GUI
interface = GUI()
interface.chiplabel.config(text="A.3f V")
interface.mainloop()
If you want multiple frames, you could do something like Switching between frames in tkinter menu.
I'm adding several widgets to a Frame which is located in a tix.NoteBook. When there are too much widgets to fit in the window, I want to use a scrollbar, so I put tix.ScrolledWindow inside that Frame and add my widgets to this ScrolledWindow instead.
The problem is that when using the grid() geometry manager, the scrollbar appears, but it is not working (The drag bar occupies the whole scroll bar).
from Tkinter import *
import Tix
class Window:
def __init__(self, root):
self.labelList = []
self.notebook = Tix.NoteBook(root, ipadx=3, ipady=3)
self.notebook.add('sheet_1', label="Sheet 1", underline=0)
self.notebook.add('sheet_2', label="Sheet 2", underline=0)
self.notebook.add('sheet_3', label="Sheet 3", underline=0)
self.notebook.pack()
#self.notebook.grid(row=0, column=0)
tab1=self.notebook.sheet_1
tab2=self.notebook.sheet_2
tab3=self.notebook.sheet_3
self.myMainContainer = Frame(tab1)
self.myMainContainer.pack()
#self.myMainContainer.grid(row=0, column=0)
scrwin = Tix.ScrolledWindow(self.myMainContainer, scrollbar='y')
scrwin.pack()
#scrwin.grid(row=0, column=0)
self.win = scrwin.window
for i in range (100):
self.labelList.append((Label(self.win)))
self.labelList[-1].config(text= "Bla", relief = SUNKEN)
self.labelList[-1].grid(row=i, column=0, sticky=W+E)
root = Tix.Tk()
myWindow = Window(root)
root.mainloop()
Whenever I change at least one of the geometry managers from pack() to grid(), the problem occurs. (Actually, I'd prefer using grid() for all containers.)
When I don't use the NoteBook widget, the problem does not occur either. The other examples here all seem to rely on pack().
Any ideas?
Many thanks,
Sano
I solved it without using ´tix.scrolledWindow´. Instead, I went for the autoscrollbar suggested by Fred Lundh here.
The main problem was the adaption to the NoteBook widget. First, I tried to put the scrollbar to the root, so that they would surround the whole window. Now, I wanted to change the hook for the scrollbar whenever I changed a tab, but the ´raisecmd´ of the Notebook did not work. Next, I thought of using the configure event on each tab - whenever a new tab is raised, its size changes and configure is called.
Well, after much trying without ever being satisfied I changed my approach and put the scrollbars inside of the tabs. The tabs and all subcontainers must get the ´grid_columnconfigure(0, weight=1)´ and ´grid_rowconfigure(0, weight=1)´ settings, or else they will not grow with the tabs.