This is a project using Python 3.6.2 and tkinter 36
So, the chunk of code posted below is a slightly trimmed version of a UI that I started making. After a bit of bodging and discovering the config method, I had my theme toggle function working.
The code below essentially toggles the background color of the Label and Text widgets with the config method.
This works, however, the buttons turn invisible until I mouseover them.
I've attempted using update and update_idletasks to no avail (there is the possibility that I haven't been using them correctly, as there was no example posted on usage in my sources.)
How do I update widgets when the parents change?
Is that would be difficult, would there be a less difficult workaround?
from tkinter import *
root = Tk()
root.minsize(width=600, height=400)
DarkTheme = ['#102030',
'#203040',
'#101010',
'#f0f0f0',
]
LightTheme = ['#c0d0e0',
'#d0e0f0',
'#f0f0f0',
'#101010'
]
Theme = DarkTheme
SBar = Label(root, width=22, fg=Theme[3], bg=Theme[0])
SBar.pack_propagate(0)
SBar.pack(side=LEFT, fill=Y)
Text = Text(root, fg=Theme[3], bg=Theme[2])
Text.pack_propagate(0)
Text.pack(side=RIGHT, fill=BOTH, expand=1)
def ThemeSet():
global SBar
global Text
SBar.config(fg=Theme[3], bg=Theme[0])
Text.config(fg=Theme[3], bg=Theme[2])
def SwapTheme():
global Theme
if Theme == DarkTheme:
Theme = LightTheme
ThemeSet()
elif Theme == LightTheme:
Theme = DarkTheme
ThemeSet()
themetoggle = Button(SBar, text = 'Theme Toggle', command = SwapTheme)
themetoggle.pack()
def AddLetter():
global Text
Text.insert(END, "hello, ")
Text.insert(END, "world")
addletter = Button(SBar, text = 'Add letter', command = AddLetter)
addletter.pack()
root.mainloop()
Related
I have a python script for teachers that works very well on my terminal and I'm trying to use Tkinter so I can share this script with other people. In the script, the user uploads a file and other information to an SQLite database.
Below is part of the script that shows just the content for the 1st tab (of 3). The problem is that when people interact with the database the tab needs to get refreshed to show something has happened and that the list of files in the database has changed.
Everything I've read shows you need a button to refresh. But that is not user-friendly. What people want to do is upload a file and then see that file in the list. Is this possible with Tkinter and if so how?
class AppWindow():
my_list = [4]
def __init__(self, parent):
global my_list
# Create the window
self.window = parent
self.window.geometry("700x600")
#self.center_window()
self.window.title("Test app")
# Create a text label and place it in the window
self.hello_label = tk.Label(self.window, text="Hello world!")
self.hello_label.place(x=20, y=20)
# Create 3 tabs
self.tab_container = tk.Frame(self.window)
self.tab_container.place(x=0,y=0,width=700,height=400)
self.tabs = ttk.Notebook(self.tab_container)
self.tab_2 = tk.Frame(self.tabs)
self.tabs.add(self.tab_2, text="Parameters")
self.tabs.place(x=0,y=0,height=400,width=700)
# Content for tab 2
self.label2 = tk.Label(self.tab_2, text="")
self.label201=tk.Label(self.tab_2, text="Put in your target GPA")
self.label201.place(x=50, y=50)
btn1 = tk.Button(self.tab_2,text="Target GPA", command=self.getGPA)
btn1.place(x=50, y=80)
for lst in self.my_list:
btn99=tk.Button(self.tab_2,text=lst)
btn99.grid()
def getGPA(self):
userInput = sd.askstring('User Input','Enter target GPA')
self.my_list.append(userInput)
if __name__ == "__main__":
root = tk.Tk()
app = AppWindow(root)
root.mainloop()
This is merely a guess because of the all the reasons I've already mentioned in comments under your question. Generally speaking, to update or what you call "refresh" a tab requires adding, changing, or deleting the widgets you put on it.
The code below is based what's currently in your code. Every time the Target GP button is clicked another Button is added to the self.tab_2 frame as well as appended to my_list. Changing the widgets doesn't have to be triggered by clicking on a button, it could be the result of some other event (such as the completion of a file upload in the background for example).
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.simpledialog as sd
class AppWindow():
def __init__(self, parent):
# Create the window
self.window = parent
self.window.geometry("700x600")
#self.center_window()
self.window.title("Test app")
# Create and initialize data list.
self.my_list = [4]
# Create a text label and place it in the window
self.hello_label = tk.Label(self.window, text="Hello world!")
self.hello_label.place(x=20, y=20)
# Create 3 tabs
self.tab_container = tk.Frame(self.window)
self.tab_container.place(x=0, y=0, width=700, height=400)
self.tabs = ttk.Notebook(self.tab_container)
self.tab_2 = tk.Frame(self.tabs)
self.tabs.add(self.tab_2, text="Parameters")
self.tabs.place(x=0, y=0, height=400, width=700)
# Content for tab 2
self.label201 = tk.Label(self.tab_2, text="Put in your target GPA")
self.label201.place(x=50, y=50)
btn1 = tk.Button(self.tab_2, text="Target GPA", command=self.getGPA)
btn1.place(x=50, y=80)
# Create a Button for each item currently in list.
for item in self.my_list:
btn = tk.Button(self.tab_2, text=item)
btn.grid()
def getGPA(self):
userInput = sd.askstring('User Input', 'Enter target GPA')
if userInput is None: # User closed the dialog or clicked Cancel?
return
self.my_list.append(userInput)
btn = tk.Button(self.tab_2, text=userInput) # Create another Button.
btn.grid()
if __name__ == "__main__":
root = tk.Tk()
app = AppWindow(root)
root.mainloop()
I tried creating a program that will take in the symptoms of a person and return the disease they have. This is the GUI part of the project.
from tkinter import *
root = Tk()
root.title("Health GUI")
root.geometry("1000x625")
symptoms_list = []
def print_symptoms():
print(symptoms_list)
def typeSymptoms():
gap3 = Label(text="").pack()
symptoms_entry = Text(width=50, height=20)
symptoms_entry.pack()
symptoms_list.append(symptoms_entry.get(1.0, END))
done_symptoms = Button(text="I have written my symptoms", width=25, height=5, command=lol)
done_symptoms.pack()
gap1 = Label(text="").pack()
title = Label(text="HEALTH GUI", font=30).pack()
gap2 = Label(text="").pack()
start_button = Button(text="Click here to start", width=30, height=5, command=typeSymptoms, font=20).pack()
root.mainloop()
Just for simplicity, I tried printing out the symptoms given by the user to the console but it gives me a list with '\n'. Please help. Thanks!(PS: I lerned Tkinter day before yesterday so I don't know much)
At the moment, your variable symptoms_list just holds the contents of the newly created Text widget, since you append this content at startup.
If you want to add the symptoms to the list, you need to have your function lol() that you call when pressing the button.
This function should look something like:
def lol():
symptoms_text = symptoms_entry.get(1.0, END)
symptoms_list = symptoms_text.split('\n')
print_symptoms()
However, your widgets and the symptoms_list would have to be global variables in order for this program to work. It would probably be better, while you are getting acquainted with Tkinter, to learn how to create a dialog as Class with attributes. That makes sharing values between methods so much easier.
I want to add a hover feature on a Tkinter button where if the user hovers the mouse cursor then description text displays. I also want to add some delay for that description to appear so that it would not be intrusive.
I can try using the "<Enter>" and "<Leave>" binding of the button to a function and make some "Label" appear in some corner of the app. But this approach may not be the most elegant.
This can be done very easily with tkinter. By adding Enter and Leave events to whatever you want to add a tooltip to, we can easily show/hide whatever we want, wherever we want. In my example I use a stripped-down tk.Toplevel so we can have a simple fade animation, and the tooltip wont be confined to the root window.
#widgets.py
import tkinter as tk, tkinter.ttk as ttk
from typing import Union
Widget = Union[tk.Widget, ttk.Widget]
class ToolTip(tk.Toplevel):
#amount to adjust fade by on every animation frame
FADE_INC:float = .07
#amount of milliseconds to wait before next animation state
FADE_MS :int = 20
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master)
#make window invisible, on the top, and strip all window decorations/features
self.attributes('-alpha', 0, '-topmost', True)
self.overrideredirect(1)
#style and create label. you can override style with kwargs
style = dict(bd=2, relief='raised', font='courier 10 bold', bg='#FFFF99', anchor='w')
self.label = tk.Label(self, **{**style, **kwargs})
self.label.grid(row=0, column=0, sticky='w')
#used to determine if an opposing fade is already in progress
self.fout:bool = False
def bind(self, target:Widget, text:str, **kwargs):
#bind Enter(mouseOver) and Leave(mouseOut) events to the target of this tooltip
target.bind('<Enter>', lambda e: self.fadein(0, text, e))
target.bind('<Leave>', lambda e: self.fadeout(1-ToolTip.FADE_INC, e))
def fadein(self, alpha:float, text:str=None, event:tk.Event=None):
#if event and text then this call came from target
#~ we can consider this a "fresh/new" call
if event and text:
#if we are in the middle of fading out jump to end of fade
if self.fout:
self.attributes('-alpha', 0)
#indicate that we are fading in
self.fout = False
#assign text to label
self.label.configure(text=f'{text:^{len(text)+2}}')
#update so the proceeding geometry will be correct
self.update()
#x and y offsets
offset_x = event.widget.winfo_width()+2
offset_y = int((event.widget.winfo_height()-self.label.winfo_height())/2)
#get geometry
w = self.label.winfo_width()
h = self.label.winfo_height()
x = event.widget.winfo_rootx()+offset_x
y = event.widget.winfo_rooty()+offset_y
#apply geometry
self.geometry(f'{w}x{h}+{x}+{y}')
#if we aren't fading out, fade in
if not self.fout:
self.attributes('-alpha', alpha)
if alpha < 1:
self.after(ToolTip.FADE_MS, lambda: self.fadein(min(alpha+ToolTip.FADE_INC, 1)))
def fadeout(self, alpha:float, event:tk.Event=None):
#if event then this call came from target
#~ we can consider this a "fresh/new" call
if event:
#indicate that we are fading out
self.fout = True
#if we aren't fading in, fade out
if self.fout:
self.attributes('-alpha', alpha)
if alpha > 0:
self.after(ToolTip.FADE_MS, lambda: self.fadeout(max(alpha-ToolTip.FADE_INC, 0)))
#main.py ~ EXAMPLE USAGE OOP
import tkinter as tk
from widgets import ToolTip
class Root(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
#instantiate ToolTip
tt = ToolTip(self)
#create first button and bind a tooltip to it
btn = tk.Button(self, text='hover')
btn.grid(column=0, row=0)
tt.bind(btn, 'first button is hovered')
#create second button and bind a tooltip to it
btn2 = tk.Button(self, text='hover2')
btn2.grid(column=1, row=0)
tt.bind(btn2, 'second button is hovered')
if __name__ == "__main__":
root = Root()
root.title("ToolTip Example")
root.mainloop()
#main.py ~ EXAMPLE USAGE PROCEDURAL
import tkinter as tk
from widgets import ToolTip
if __name__ == "__main__":
root = tk.Tk()
root.title("ToolTip Example")
#instantiate ToolTip
tt = ToolTip(root)
#create first button and bind a tooltip to it
btn = tk.Button(root, text='hover')
btn.grid(column=0, row=0)
tt.bind(btn, 'first button is hovered')
#create second button and bind a tooltip to it
btn2 = tk.Button(root, text='hover2')
btn2.grid(column=1, row=0)
tt.bind(btn2, 'second button is hovered')
root.mainloop()
Here is a small snippet using Pmw (python mega widgets) for the tool tips.
Firstly start by installing it:
pip install Pmw
Then here is a snippet to understand what Pmw can do:
from tkinter import *
import Pmw
root = Tk()
Pmw.initialise(root) #initializing it in the root window
l = Label(root,text='Random Text')
l.pack()
b = Button(root,text='Hover me')
b.pack()
tooltip_1 = Pmw.Balloon(root) #Calling the tooltip
tooltip_1.bind(b,'This is the hover Text\nHope you get an idea of whats going on here.') #binding it and assigning a text to it
root.mainloop()
Hope this gives you a better idea. Keep in mind that Pmw could create a mess while converting the py to an exe later(if you have any intentions to). There is a way around in tho.
Cheers
I am fairly new to python and am currently working on a school project, my aim is to create a search bar that can be used to search a data file, however I am struggling to get the search bar to work correctly. I am using the tkinter entry widget.
When I call .get(), the string in the entry widget is not printed. Here is my code...
from tkinter import *
def searchButton():
text = searched.get()
print (text)
def drawStatWindow():
global searched
statWindow = Tk()
statWindow.title("View Statistics")
statWindow.config(bg = "grey")
statWindow.geometry('800x900')
searched = StringVar()
searchBox = Entry(statWindow, textvariable = searched)
searchBox.place(x= 450, y=50, width = 200, height = 24)
enterButton = tkinter.Button(statWindow, text ="Enter", command =searchButton)
enterButton.config(height = 1, width = 4)
enterButton.place(x=652, y=50)
drawStatWindow()
When I type a string into the entry widget and press the enter button, nothing happens.
Like I say I am not very experienced and this is my first project, but after reading about the tkinter entry widgets I can't understand why this won't work.
I am using python V3.4.0
Thanks.
Your code lacks a call to mainloop(). You could try adding it to the end of the drawStatWindow() function:
statWindow.mainloop()
You might want to restructure your code into a class. This allows you to avoid using global variables and generally provides better organisation for your application:
from tkinter import *
class App:
def __init__(self, statWindow):
statWindow.title("View Statistics")
statWindow.config(bg = "grey")
statWindow.geometry('800x900')
self.searched = StringVar()
searchBox = Entry(statWindow, textvariable=self.searched)
searchBox.place(x= 450, y=50, width = 200, height = 24)
enterButton = Button(statWindow, text ="Enter", command=self.searchButton)
enterButton.config(height = 1, width = 4)
enterButton.place(x=652, y=50)
def searchButton(self):
text = self.searched.get()
print(text)
root = Tk()
app = App(root)
root.mainloop()
You have to add mainloop() because tkinter needs it to run.
If you run code in IDLE which use tkinter then IDLE runs own mainloop() and code can work but normally you have to add mainloop() at the end.
And you have to remove tkinter in tkinter.Button.
from tkinter import *
def searchButton():
text = searched.get()
print(text)
def drawStatWindow():
global searched
statWindow = Tk()
statWindow.title("View Statistics")
statWindow.config(bg="grey")
statWindow.geometry('800x900')
searched = StringVar()
searchBox = Entry(statWindow, textvariable=searched)
searchBox.place(x= 450, y=50, width=200, height=24)
# remove `tkinter` in `tkinter.Button`
enterButton = Button(statWindow, text="Enter", command=searchButton)
enterButton.config(height=1, width=4)
enterButton.place(x=652, y=50)
# add `mainloop()`
statWindow.mainloop()
drawStatWindow()
No need to use textvariable, you should use this:
searchBox = Entry(statWindow)
searchBox.focus_set()
searchBox.place(x= 450, y=50, width = 200, height = 24)
then you will be able to use searchBox.get(), that will be a string.
I am currently trying to make a GUI to an existing python program using Tkinter. The program gives the user two options from which the user must choose to either accept or decline. Before using Tkinter the options were placed in the terminal and awaited for a raw_input. (y/n). How can I make this so the canvas text updates with the new data and awaits for the users button click?
To make my question more specific: How can I run another programs code while the Tkinter mainloop is running and make these two interact?
Example code below.
from Tkinter import *
root = Tk()
root.resizable(width=False, height=False)
root.geometry('{}x{}'.format(500,550))
root.wm_title("Tkinter test")
BtnFrame = Frame (root)
BtnFrame.pack(side = BOTTOM)
BtnFrame.place(y=450, x=20)
canvas_1 = Canvas(root, width = "200", height ="300")
canvas_2 = Canvas(root, width = "250", height ="300")
canvas_1.pack(side = LEFT)
canvas_2.pack(side = RIGHT)
textfield_1 = canvas_1.create_text(100,50)
textfield_2 = canvas_2.create_text(100,50,)
def update_textfiel_1(text):
global textfield_1
canvas_1.delete(textfield_1)
textfield = canvas.create_text(100,50,text = text)
def update_textfiel_2(text):
global textfield_2
canvas_2.delete(textfield_2)
textfield1 = canvas1.create_text(100,50,text = text)
Accept = Button(BtnFrame, text="Accept", width=25)
Decline = Button(BtnFrame, text="Decline", width=25)
Accept.pack(side = LEFT)
Decline.pack(side = RIGHT)
root.mainloop()
First off you have some inconsistent variable names in your update_textfiel functions, you can greatly simplify it by using .itemconfigure (documentation for methods on canvas widget)
def update_textfiel_1(new_text):
canvas_1.itemconfigure(textfield_1, text=new_text)
def update_textfiel_2(new_text):
canvas_2.itemconfigure(textfield_2, text=new_text)
If I understand correctly you want a way to have a function that will simply wait for the user to press one of the buttons and then return the result, this is very easy with tkMessageBox:
question = """Do you accept {}?
if you say no you will instead get {}"""
#this message can GREATLY be improved
# But I really don't understand what you are using it for...
def user_confirmation(name1, name2):
response = tkMessageBox.askyesno("Accept or Decline",question.format(name1,name2))
print(response)
if response: # == True
return name1
else:
return name2
I have not yet found a way to make a blocking function that works with the window you have currently...