I just picked up python recently and I've been working on a project called "ToDoList.py".It's finished but I want to add a button to change the theme of the GUI using tkinter / ttk but its not working.
This is the error:
Traceback (most recent call last):
File "todolist.py", line 64, in <module>
lbl_title = Label(root, text="ToDoList", bg="white")
File "C:\Users\Sam\AppData\Local\Programs\Python\Python37-32\lib\tkinter\ttk.py", line 761, in __init__
Widget.__init__(self, master, "ttk::label", kw)
File "C:\Users\Sam\AppData\Local\Programs\Python\Python37-32\lib\tkinter\ttk.py", line 559, in __init__
tkinter.Widget.__init__(self, master, widgetname, kw=kw)
File "C:\Users\Sam\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 2296, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: unknown option "-bg"
I don't understand why this error is possible since i haven't adjusted the widgets yet
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
from ttkthemes import themed_tk as tk
import random
import tkinter.messagebox
#--------root style
root = Tk()
#--------root backgroud
root.configure(bg="white")
#--------root title
root.title("Reminder")
#--------root size
root.geometry("225x300")
#--------create empty list
tasks = []
#--------fuction
def darkmd():
root.get_themes()
root.set_theme("equilux")
#--------command
lbl_title = Label(root, text="ToDoList", bg="white")
lbl_title.grid(row=0, column=0)
lbl_display = Label(root, text="", fg="black", bg="white")
lbl_display.grid(row=0, column=1)
txt_input = Entry(root, width=20, fg="black", bg="white")
txt_input.grid(row=1, column=1)
bt_add_task = Button(root, text="Add Task", fg="black", bg="white", command = add_task)
bt_add_task.grid(row=1, column=0)
bt_del_all = Button(root, text="Del all", fg="black", bg="white", command = del_all)
bt_del_all.grid(row=2, column=0)
bt_del_one= Button(root, text="Del", fg="black", bg="white", command = del_one)
bt_del_one.grid(row=3, column=0)
bt_sort_asc = Button(root, text="Sort (ASC)", fg="black", bg="white", command = sort_asc)
bt_sort_asc.grid(row=4, column=0)
bt_sort_desc = Button(root, text="Sort (DESC)", fg="black", bg="white", command = sort_desc)
bt_sort_desc.grid(row=5, column=0)
bt_total_task = Button(root, text="Num Of Task", fg="black", bg="white", command = total_task)
bt_total_task.grid(row=6, column=0)
bt_darkmd = Button(root, text="Darkmode", fg="black", bg="white", command = darkmd)
bt_darkmd.grid(row=7, column=0)
lb_tasks = Listbox(root,fg="black", bg="white")
lb_tasks.grid(row=2, column=1, rowspan=9)
#--------main
root.mainloop()
As an alternative to ThemedTk, you can use ThemedStyle. This way your code will be exactly like if you were using one of the standard ttk themes except that you define your style with style = ThemedStyle(root) instead of style = Style(root). Then you simply use style.theme_use(<theme name>) to change theme and you can list the available themes with style.theme_names().
from tkinter import ttk
import tkinter as tk
from ttkthemes import ThemedStyle
#--------root style
root = tk.Tk()
#--------root backgroud
root.configure(bg="white")
#--------root title
root.title("Reminder")
#--------root size
root.geometry("225x300")
# white theme
style = ThemedStyle(root)
style.theme_use('arc') # white style
#--------create empty list
tasks = []
#--------function
def darkmd():
style.theme_use("equilux") # only changes the theme of the ttk widgets
# change style of tk widgets manually:
bg = style.lookup('TLabel', 'background')
fg = style.lookup('TLabel', 'foreground')
root.configure(bg=style.lookup('TLabel', 'background'))
lb_tasks.configure(bg=bg, fg=fg)
#--------command
lbl_title = ttk.Label(root, text="ToDoList")
lbl_title.grid(row=0, column=0)
lbl_display = ttk.Label(root, text="")
lbl_display.grid(row=0, column=1)
txt_input = ttk.Entry(root, width=20)
txt_input.grid(row=1, column=1)
bt_add_task = ttk.Button(root, text="Add Task")
bt_add_task.grid(row=1, column=0)
bt_del_all = ttk.Button(root, text="Del all")
bt_del_all.grid(row=2, column=0)
bt_del_one = ttk.Button(root, text="Del")
bt_del_one.grid(row=3, column=0)
bt_sort_asc = ttk.Button(root, text="Sort (ASC)")
bt_sort_asc.grid(row=4, column=0)
bt_sort_desc = ttk.Button(root, text="Sort (DESC)")
bt_sort_desc.grid(row=5, column=0)
bt_total_task = ttk.Button(root, text="Num Of Task")
bt_total_task.grid(row=6, column=0)
bt_darkmd = ttk.Button(root, text="Darkmode", command=darkmd)
bt_darkmd.grid(row=7, column=0)
lb_tasks = tk.Listbox(root, fg="black")
lb_tasks.grid(row=2, column=1, rowspan=9)
#--------main
root.mainloop()
Clear theme:
Dark theme:
Note that only the ttk widgets become dark after setting the theme to "equilux". So you need to manually change the colors of your tk widgets in darkmd() (like I did for root and lb_tasks).
Comment: HowTo using: ttkthemes
To use ttkthemes change to the following:
No style.theme_use(... statement, as this is alredy done in __init__(....
from ttkthemes import ThemedTk
class App(ThemedTk):
def __init__(self):
super().__init__("equilux")
# ATTENTION!!
# The following could fail as i couldn't test with `ThemedTk`
# ATTENTION!!
style = ttk.Style(self)
style.configure("TLabel", background="white")
Question: How to add themes?
First you have to understand, not to mix tkinter and tkinter.ttk widgets in a uncontrolled way. Only tkinter.ttk widgets can be styled using theme and style.
TkDocs - Tk Tutorial - Styles and Themes
tkinterbook - Widget Styling
Use only the following common import statements
import tkinter as tk
import tkinter.ttk as ttk
To instantiate a ttk widget use:
Note: You can't use bg= on a ttk widget!
lbl_title = ttk.Label(root, text="ToDoList")
Application wide usage:
Note: It's important to do all style definition once and before any widget instantiation.
class App(tk.Tk):
def __init__(self):
super().__init__()
style = ttk.Style(self)
style.theme_use('clam')
style.configure("TLabel", background="white")
self.title("Tkinter Style")
self.geometry("225x300")
lbl_title = ttk.Label(self, text="ToDoList")
lbl_title.grid(row=0, column=0)
if __name__ == "__main__":
App().mainloop()
Tested with Python: 3.5
After reading your comment, I thought my input may help.
First, identify your OS. Windows/Mac
Second, open IDLE and then open IDLE preferences
You will see the settings and in "Highlights", below the radio buttons for the themes, you will see a drop box that allows you to switch your themes from IDLE Dark to Classic and New!
Related
An example of what I want to get:
Main program:
from tkinter import *
import tkinter as tk
import module
main_window = Tk()
def createCanvas():
canvas = Canvas(main_window, bg="grey")
canvas.grid(row=1, column=0, columnspan=2)
def resetCanvas():
canvas.destroy()
createCanvas()
button1 = Button(main_window, text="launch module", command=module.moduleFunction)
button1.grid(row=0, column=0)
button2 = Button(main_window, text="reset canvas", command=resetCanvas)
button2.grid(row=0, column=1)
createCanvas()
main_window.mainloop()
Module:
from tkinter import *
import tkinter as tk
from tkinter import Toplevel
def moduleFunction():
child_window = Toplevel()
def resetCanvasFromModule():
# THE QUESTION!
resetCanvas()
button3 = Button(child_window, text="reset canvas from module", command=resetCanvasFromModule)
button3.grid(row=0, column=0)
Obviously this doesn't work, since resetCanvas() isn't defined inside the module, but if I define it, it will try to destroy canvas, which isn't defined in the module, so it won't work either.
So, what would I have to do to get the effect I want?
P.S.: I have tried with 'global', without success.
Can't you just pass the element?
like this
from tkinter import *
canvas = None
def createCanvas():
global canvas
canvas = Canvas(main_window, bg="grey")
canvas.grid(row=1, column=0, columnspan=2)
def ResetCanvasFromModule():
global canvas
canvas.forget()
createCanvas()
button1 = Button(main_window, text="create canvas", command=createCanvas)
button1.grid(row=0, column=0)
button2 = Button(main_window, text="reset canvas", command=ResetCanvas)
button2.grid(row=0, column=1)
Please let me know If this helped you, if not comment so I can edit my answer
Thanks for your help guys.
Passing resetCanvas() as a parameter to moduleFunction(), and a lambda as button1 command, works fine.
This is my working example code(maybe in the future it can be useful to a newbie like me):
Main code:
from tkinter import *
import tkinter as tk
import module
main_window = Tk()
def createCanvas():
global canvas
canvas = Canvas(main_window, bg="grey")
canvas.grid(row=1, column=0, columnspan=2)
return
canvas
def resetCanvas():
canvas.destroy()
createCanvas()
button1 = Button(main_window, text="launch module", command=lambda:[module.moduleFunction(resetCanvas)])
button1.grid(row=0, column=0)
button2 = Button(main_window, text="reset canvas", command=resetCanvas)
button2.grid(row=0, column=1)
createCanvas()
main_window.mainloop()
Module:
from tkinter import *
import tkinter as tk
from tkinter import Toplevel
def moduleFunction(resetCanvas):
child_window = Toplevel()
def resetCanvasFromModule():
resetCanvas()
button3 = Button(child_window, text="reset canvas from module", command=resetCanvasFromModule)
button3.grid(row=0, column=0)
Thanks a lot.
i'm working on downloading manager python gui app using Tkinter and halfway there my code started to look very messy so i decided to seperate functions on different file and then import it:
my main code:
from tkinter import *
from functions import add_download
root = Tk()
root.title("The Pownloader!")
canvas = Canvas(root, width=700, height=500).pack()
# Buttons:
ADD_BUTTON = Button(root, text="ADD", bd=4, height=2, width=5, command=add_download)
SETTINGS_BUTTON = Button(root, text="SETTINGS", bd=4, height=2, width=5)
ABOUT_BUTTON = Button(root, text="ABOUT", bd=4, height=2, width=5)
EXIT_BUTTON = Button(root, text="EXIT", bd=4, height=2, width=5, command=quit)
# Mini-Buttons:
PAUSE_MINI_BUTTON = Button(root, text="PAUSE", font=(None, "8"), height=2, width=3)
RESUME_MINI_BUTTON = Button(root, text="RESUME", font=(None, "8"), height=2, width=3)
REMOVE_MINI_BUTTON = Button(root, text="REMOVE", font=(None, "8"), height=2, width=3)
# Side_Mini_Buttons:
DOWNLOAD_WINDOW = Button(root, text="Downloads", font=(None, "8"), height=3, width=6)
ERRORS_WINDOW = Button(root, text="Failed", font=(None, "8"), height=3, width=6)
COMPLETED_WINDOW = Button(root, text="Completed", font=(None, "8"), height=3, width=6)
# Positionning Buttons:
ADD_BUTTON.place(x=70, y=20)
SETTINGS_BUTTON.place(x=145, y=20)
ABOUT_BUTTON.place(x=220, y=20)
EXIT_BUTTON.place(x=295, y=20)
PAUSE_MINI_BUTTON.place(x=290, y=455)
RESUME_MINI_BUTTON.place(x=340, y=455)
REMOVE_MINI_BUTTON.place(x=390, y=455)
DOWNLOAD_WINDOW.place(x=1, y=100)
ERRORS_WINDOW.place(x=1, y=160)
COMPLETED_WINDOW.place(x=1, y=220)
# Download Frame:
DOWNLOAD_LIST_LABEL = Label(root, text="Download List:")
DOWNLOAD_LIST_LABEL.place(x=70, y=80)
DOWNLOAD_ENTRIES = Listbox(root, width=70, height=19)
DOWNLOAD_ENTRIES.place(x=70, y=100)
# Main Loop:
root.mainloop()
However my functions.py code looks like this:
def add_download():
# Defining The Pop-up frame:
top = Toplevel(root, width = 420, height = 150)
top.title("New Download")
# Putting on widgets:
link = StringVar()
LINK_LABEL = Label(top, text = "Paste Link:")
FIELD_ENTRY = Entry(top, width = 40, textvariable=link)
def on_click():
link_to_verify = (link.get()).strip()
if len(link_to_verify)>15:
if link_to_verify[0:11]=="http://www.":
DOWNLOAD_ENTRIES.insert(0, link_to_verify)
else:
print("Stupid")
else:
print("not a valid link")
BUTTONS_WIDGET = Frame(top)
ADD_BUTTON = Button(BUTTONS_WIDGET, text = "Add", width=10, command=on_click)
CANCEL_BUTTON = Button(BUTTONS_WIDGET, text = "Cancel", width=10, command=top.destroy)
# Positionning everythig:
LINK_LABEL.grid(column=0,row=0)
FIELD_ENTRY.grid(column=1,row=0)
BUTTONS_WIDGET.grid(column=1,row=2)
ADD_BUTTON.grid(column=0,row=0)
CANCEL_BUTTON.grid(column=1,row=0)
basically i wanted the function to call and show a pop-up window, i'm sure this could done in a million times better but i'm just learning, however i receive an error says:
Toplevel is not defined
Every file needs to import tkinter.
In addition, any variables in the main file which are needed by the imported functions need to be passed into the functions. For example, you should define add_download to accept the root window as a parameter.
def add_download(root):
...
Then, in the main program, pass root as that parameter:
ADD_BUTTON = Button(root, ..., command=lambda: add_download(root))
You will need to build a class to manage it.
Inside run.py:
import tkinter as tk
from interface import GUI
root = tk.Tk()
GUI(root)
Then inside your interface.py script you can call in additional modules:
import tkinter as tk
from aux import AuxGUI
from menu import MenuGUI
class GUI:
def __init__(self, master):
self.master = master
self.GUI_list = []
self.AuxGUI = AuxGUI(self.master, self.GUI_list) # Additional module
self.MenuGUI = MenuGUI (self.master, self.GUI_list) # Additional module
Then you can use OOP to access functions or objects to dynamically interact with each other.
self.GUI_list.append(self.AuxGUI)
self.GUI_list.append(self.MenuGUI)
Inside menu.py identify the correct index from the GUI_list:
import tkinter as tk
class MenuGUI:
def __init__(self, master, GUI_list):
self.master = master
self.AuxGUI = GUI_list[0]
I'm trying to make a simple calculator but when I try to configure my entry with 'relief' method it is showing the error _tkinter.TclError: unknown option "-relief".
I don't know why this is showing this error
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
win.geometry('400x400')
win.title('Simple Calculator')
win.configure(bg='Peach puff')
value = tk.StringVar()
entry = ttk.Entry(win, font=('Helvetica', 35, 'bold'), justify='right', textvariable=value, relief=tk.SUNKEN)
entry.pack(padx=30, pady=15, side='top', fill='both')
def spin():
pass
button_images = {'one': tk.PhotoImage(file=r'C:\Users\hruthik\Desktop\Pics\numbers\one.png')}
b_one = ttk.Button(win, image=button_images['one'], command=spin, borderwidth=0)
b_one.pack(pady=20)
win.resizable(False, False)
win.mainloop()
The ttk package widgets have different options than the standard tkinter widgets. For example, the ttk.Entry widget has no option for relief, and the ttk.Button widget has no option for borderwidth. Try using the tk.Entry and tk.Button widgets instead, if you want to specify these options
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
win.geometry('400x400')
win.title('Simple Calculator')
win.configure(bg='Peach puff')
value = tk.StringVar()
entry = tk.Entry(win, font=('Helvetica', 35, 'bold'), justify='right', textvariable=value, relief=tk.SUNKEN)
entry.pack(padx=30, pady=15, side='top', fill='both')
def spin():
pass
button_images = {'one': tk.PhotoImage(file=r'C:\Users\hruthik\Desktop\Pics\numbers\one.png')}
b_one = tk.Button(win, image=button_images['one'], command=spin, borderwidth=0)
b_one.pack(pady=20)
win.resizable(False, False)
win.mainloop()
I need a python messagebox with custom buttons.
I need something like this. I need it to return the button clicked. Like If I clicked 'A' it would return 'A'
I know about tkinter but I want to use this with pygame and can't get it to work.
Here is what I made with tkinter, not the perfect messagebox, but why not?
For the theme used you have to install it first, like:
pip install ttkthemes
Then the code
# imports
from tkinter import *
from tkinter import ttk
from ttkthemes import themed_tk as tktheme
from PIL import ImageTk, Image
from tkinter import messagebox
# making new themed window
root = tktheme.ThemedTk()
root.title('Take selection')
root.get_themes()
root.set_theme('vista')
# message to be shown by the box
message = 'Here is a custom messagebox.'
# defining functions
def click1():
root.destroy()
return 'A'
def click2():
root.destroy()
return 'B'
def click3():
root.destroy()
return 'C'
# creating white frame
frame1 = Frame(height=139, width=440, bg='white')
frame1.grid(row=0, column=0)
# creating gray frame
frame2 = ttk.Frame(height=50, width=440)
frame2.grid(row=1, column=0)
# importing the image, any image can be used(for the question mark)
dir = Image.open('Blue_question.png')
dir = dir.resize((50, 50), Image.ANTIALIAS)
img_prof = ImageTk.PhotoImage(dir)
img_label = Label(root, image=img_prof, bg='white')
img_label.grid(row=0, column=0, sticky=W, padx=25)
# defining main label
cust_messagebox = Label(root, text=message, font=('Arial', 10), bg='white')
cust_messagebox.grid(row=0, column=0, sticky=W, padx=(95, 0))
# defining buttons
button1 = ttk.Button(root, text='A', command=click1)
button1.grid(row=1, column=0, sticky=W, padx=30, ipadx=10)
button2 = ttk.Button(root, text='B', command=click2)
button2.grid(row=1, column=0, ipadx=10)
button3 = ttk.Button(root, text='C', command=click3)
button3.grid(row=1, column=0, sticky=E, padx=(0, 30), ipadx=10)
# keeping the app alive
root.mainloop()
With bigger messages you might want to increase the width of the frames and the play with the padding of the widgets too.
If any doubts or errors, do let me know.
Cheers
Everything looks right, but when I run the program the buttons and the websites open at the same time and then the buttons don't work?
import webbrowser
from tkinter import *
from tkinter import ttk
root = Tk()
style = ttk.Style()
open_facebook = webbrowser.open('http://www.facebook.com')
open_google = webbrowser.open('http://www.google.com')
open_yahoo = webbrowser.open('http://www.yahoo.com')
open_youtube = webbrowser.open('http://www.youtube.com')
style.configure("TButton",
font="Serif 18",
padding=10)
main_frame = Frame(root)
main_frame.grid(row=0, columnspan=4)
button_facebook = ttk.Button(main_frame, text='Facebook', command=open_facebook).grid(row=1, column=0)
button_google = ttk.Button(main_frame, text='Google', command=open_google).grid(row=1, column=1)
button_yahoo = ttk.Button(main_frame, text='Yahoo', command=open_yahoo).grid(row=1, column=2)
button_youtube = ttk.Button(main_frame, text='Youtube', command=open_youtube).grid(row=1, column=3)
root.mainloop()
I couldn't manage to make it work with the code you had presented, but I could make it work using lambda in the command portion of the button. This was the only way I could ensure that the web browser didn't open the sites until the buttons were pressed.
import webbrowser
from tkinter import *
from tkinter import ttk
root = Tk()
style = ttk.Style()
style.configure("TButton",
font="Serif 18",
padding=10)
main_frame = Frame(root)
main_frame.grid(row=0, columnspan=4)
button_facebook = ttk.Button(main_frame, text='Facebook', command= lambda:
webbrowser.open('http://www.facebook.com'))
button_google = ttk.Button(main_frame, text='Google', command= lambda:
webbrowser.open('http://www.google.com'))
button_yahoo = ttk.Button(main_frame, text='Yahoo', command= lambda:
webbrowser.open('http://www.yahoo.com'))
button_youtube = ttk.Button(main_frame, text='Youtube', command= lambda:
webbrowser.open('http://www.youtube.com'))
button_facebook.grid(row=1, column=0)
button_google.grid(row=1, column=1)
button_yahoo.grid(row=1, column=2)
button_youtube.grid(row=1, column=3)
root.mainloop()