Changing the colour of a Tkinter button in a function - python

I want to change the colour of a button when pressing a different button. The below code recreates the Attribrute Error.
Ideally the solution should be able to change all of the attributes of the button (see the attempted state change) but I didn't put this in the title because I don't know if 'attributes' is the right word.
import Tkinter
def tester():
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
button1 = Tkinter.Button(self,text=u"Button")
button1.grid(padx=5,pady=5)
button2 = Tkinter.Button(self,text=u"Change",command=self.colourer)
button2.grid(column=1,row=0,pady=5)
button3 = Tkinter.Button(self,text=u"Disabled",state='disabled')
button3.grid(column=1,row=0,pady=5)
def colourer(self):
self.button1.configure(bg='red')
# self.button1.config(bg='red') -- this gives same error
# self.button3.configure(state='normal') -- as does this
if __name__ == "__main__":
app = window(None)
app.title('Tester')
app.mainloop()
tester()
All of the ways suggested here give the same error: Changing colour of buttons in tkinter
Thanks

The root of your problem is that you're not defining self.button. You need to assign a value to that variable:
self.button = Tkinter.Button(...)

you need give self.button1 while declaring
if you see grid you gave same column name for button2 and button 3 so they overlap each other
try this
import Tkinter
def tester():
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
print self.grid()
self.button1 = Tkinter.Button(self,text=u"Button")
self.button1.grid(padx=5,pady=5)
self.button2 = Tkinter.Button(self,text=u"Change",command=self.colourer)
self.button2.grid(column=1,row=0,pady=5)
self.button3 = Tkinter.Button(self,text=u"Disabled",state='disabled')
self.button3.grid(column=2,row=0,pady=5)
def colourer(self):
self.button1.configure(bg='red')
# self.button1.config(bg='red') -- this gives same error
# self.button3.configure(state='normal') -- as does this
if __name__ == "__main__":
app = window(None)
app.title('Tester')
app.mainloop()
tester()

Related

Object oriented Tkinter

I am trying to write a gui which as a class which is the main application. A single instance of that class is created in the main root. At the same time I want a submit button to be clicked where some values are verified before a further submission to write the data. I am trying to do this by creating a new class for the Toplevel pop up window. But I am not sure how best to structure this. Ideally an instance of the pop up window class would be created each time the button is selected. It seems like with the way I have structured it another instance of the main application class has been created. I am a little confused how to correctly do this using OOP.
Below is some sample code to illustrate the problem.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.title = "TITLE"
self.master = master
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.title = 'TITLE'
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
app.pack()
root.mainloop()
There is something missing from my understanding of the best approach to using OOP to structure a program like this.
Solution
Just make the submit_button class inherit the Window class and instantiate only the submiit_button class. With this, you don't need to instantiate the Window class. This allows for a special trick so that you can access the attributes of the submit_class class in the Window class without creating an instance. Just use self since it is actually an instance of the submit_class passed on to the Window class. Here is your code with that. There are many other suggestions and to know, see the code.
Suggestions
In the first place, why are you making submit_button a separate class? You could include it as a method of Window class. If you have good reason, it is ok but otherwise make it a method.
And also, why are you creating a root window and making the window class save it as an attribute? Just make the Window class inherit tk.Tk instead of tk.Frame. You can then create frame inside the __init__ function. Here is the code working code with these rectifications:
Code
import tkinter as tk
from tkinter import ttk
class Window(tk.Tk):
def __init__(self):
super().__init__()
self.title("TITLE")
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
class submit_button(Window):
def __init__(self):
super().__init__()
self.submit_pop_up = tk.Toplevel(self)
self.submit_pop_up.withdraw()
print(self.submit_pop_up)
def click_submit_button(self):
self.submit_pop_up.deiconify()
print('New PopUp')
if __name__ == "__main__":
app = submit_button()
app.mainloop()
Is that what you want? You should used self.master for all widgets.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.master.title( "TITLE")
self.submit = ttk.Button(self.master, text='SUBMIT', command=self.click_submit_button)
self.submit.grid(row=0, column=2, padx=20, pady=20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.master.title('TITLE')
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
#app.pack()
root.mainloop()
Result:

How to create a button on a new widget using grid() method in tkinter?

import tkinter as tk
class Main:
def __init__(self, parent):
self.parent = parent
self.button = tk.Button(text="Build", command=self.new_window)
self.button.grid(row=1, column=0)
def new_window(self):
self.window = tk.Tk()
self.app = Graph(self.window)
self.window.mainloop()
class Graph:
def __init__(self, parent):
self.parent = parent
self.new_button = tk.Button(text="text")
self.new_button.grid(in_=self.parent)
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == "__main__":
main()
Here's my code and I'm trying to create a button on a new widget using grid() + in_, but there is a problem - button dont creates on a new widget instead of this it creates on the main one.
In tkinter widgets are by default assigned to root window, Tk unless passed a parent widget as the first positional argument, which is omitted in:
self.new_button = tk.Button(text="Destroy", command=self.destroy)
You should replace it with:
self.new_button = tk.Button(parent, text="Destroy", command=self.destroy)

return self.func(*args) TypeError: menu() missing 1 required positional argument: 'self'

I keep getting this error and I cant seem to fix it, if anybody could help me I would really appreciate it. I have been looking at it for quite a while now and I can't seem to get my head around it, I am still quite new to programming in an Object Oriented Way.
Thanks & Merry Christmas
Welcome Page
from External_Menu import *
from tkinter import *
class Welcome(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.welcome_button()
self.pack()
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=ExternalMenu.menu)
self.welcome.pack()
self.pack()
if __name__ == "__main__":
root = Tk()
main = Welcome(root)
main.mainloop()
External Menu
from tkinter import *
class ExternalMenu(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.menu()
self.pack()
def menu(self):
self.external_menu_lbl = Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = Button(self, text="Sign Up")
self.sign_up_button.pack()
self.pack()
Your menu is a method so it needs an object it can manipulate in order to work. But you're trying to call it without an object created for it, essentially you're calling it like a function. First you need to create an object of the class that method is defined on:
an_ex_men = ExternalMenu(root)
and then you are able to call menu method on an_ex_men:
an_ex_men.menu()
But since you already call menu under your ExternalMenu's __int__ method it is called as soon as an object instance for that class is created. Shortly, you creating an ExternalMenu object is enough to achieve that as in, even without putting it to a variable to refer later:
ExternalMenu(root)
Since you want to swap between two windows you need an additional method to either hide or completely destroy the other window. Let's say you want to destroy the other window, and for that you could use a method defined for your button that does those 2 actions:
def welcome(self):
self.destroy()
ExternalMenu(root)
As in:
...
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=self.welcome)
self.welcome.pack()
def welcome(self):
self.destroy()
ExternalMenu(root)
...
Below example does exactly what you expect, first I created a parent class App as according to you the two frames you have are in the same level of hierarchy and having a parent for them is in my opinion better structured:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.welcome_frame = Welcome(self)
#I believe it's better to call geometry managers as parent
self.welcome_frame.pack()
#assigning parent method as button command as it affects siblings
self.welcome_frame.button['command'] = self.go_ex_men
def go_ex_men(self):
self.welcome_frame.destroy()
self.ex_men = ExternalMenu(self)
self.ex_men.pack()
class Welcome(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.button = tk.Button(self, text="Welcome!")
self.button.pack()
class ExternalMenu(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.external_menu_lbl = tk.Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = tk.Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = tk.Button(self, text="Sign Up")
self.sign_up_button.pack()
if __name__ == "__main__":
root = App()
root.mainloop()

Python Tkinter Input Box

Good day.
I am trying to create my own input box for use in my project.
basically what i am trying to do is run my main form which will call the second. the user will provide some data on the second and when the press the ok/close button on the second for the data will be passed back to the first. similar in functionality to the inputbox.
here is what i have created, but being new to python i am not sure where i am going wrong/nor can i quick figure out when to put the return.
My Class is here
import tkinter as tk
class MainWindow():
def __init__(self, parent):
top = self.top = tk.Toplevel(parent)
self.myLabel = tk.Label(top, text='Enter a Grouping Name')
self.myLabel.pack()
self.myEntryBox = tk.Entry(top)
self.myEntryBox.focus_set()
self.myEntryBox.pack()
self.mySubmitButton = tk.Button(top, text='OK', command=self.DestWin)
self.mySubmitButton.pack()
def DestWin(self):
self.top.destroy()
The method to call it is here
abc=configurator.MainWindow(root)
Not exactly sure what you are trying to achieve, but if you are trying to get values from one window to another, below you can find an extended example based on your code.
import tkinter as tk
class MainWindow():
def __init__(self, parent):
top = self.top = tk.Toplevel(parent)
self.myLabel = tk.Label(top, text='Enter a Grouping Name')
self.myLabel.pack()
self.myEntryBox = tk.Entry(top)
self.myEntryBox.focus_set()
self.myEntryBox.pack()
self.mySubmitButton = tk.Button(top, text='OK', command=self.DestWin)
self.mySubmitButton.pack()
def DestWin(self):
# call callback function setting value in MyFrame
self.callback(self.myEntryBox.get())
self.top.destroy()
def set_callback(self, a_func):
self.callback = a_func
class MyFrame(tk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.pack()
self.myLabel1 = tk.Label(parent, text='Click OK to enter the group name')
self.myLabel1.pack()
self.mySubmitButton1 = tk.Button(parent, text='OK', command=self.get_group_name)
self.mySubmitButton1.pack()
def get_group_name(self):
mw = MainWindow(None)
# provide callback to MainWindow so that it can return results to MyFrame
mw.set_callback(self.set_label)
def set_label(self, astr = ''):
self.myLabel1['text'] = astr
root = tk.Tk()
mf = MyFrame(root)
root.mainloop()
The screenshot:
The text from the right window, when OK is pressed, will be shown in the left window. This is achieved through callbacks. MainWindow takes a callback function, and when you press OK, it is executed. The callback is set_label from MyFrame.
Hope this helps.

Interacting with Tkinter window during a long process

I have a basic python class that creates a window using the standard Tkinter library:
import Tkinter
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=self.lock_func)
self.processBtn.pack()
app = GUI(None)
app.mainloop()
when I hit the Process button, the window doesn't respond.
I want to be able to close the program (using the x button) whene the lock_func is runing.
You could use a generator to hold the state within the loop, and use yield to relinquish control back to the main loop. Then use self.after to repeatedly call the generator's next method to simulate the effect of while True -- but doing it in a way which is friendly to Tkinter's main loop.
import Tkinter as tk
class App(object):
def __init__(self, master):
self.master = master
self.initialize()
def lock_func(self):
def step():
while True:
print("blah")
self.nextstep_id = self.master.after(1, nextstep)
yield
nextstep = step().next
self.nextstep_id = self.master.after(1, nextstep)
def stop(self):
self.master.after_cancel(self.nextstep_id)
print("stopped")
def initialize(self):
self.nextstep_id = 0
self.process_button = tk.Button(self.master, text="Process",
command=self.lock_func)
self.stop_button = tk.Button(self.master, text="Stop",
command=self.stop)
self.process_button.pack()
self.stop_button.pack(expand='yes', fill='x')
root = tk.Tk()
app = App(root)
root.mainloop()
You can use the window.update() method too keep your GUI active and functional after every time you change something on it. During the roots mainloop, this happens automatically but if you're prolonging the mainloop it's probably a good idea to do it manually your self. Put the window.update() in the loop that is taking a while. Note: window is a Tk() object
One way is to use threading:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.processBtn.pack()
app = GUI(None)
app.mainloop()
However, it will keep printing until you close the Python console.
To stop it, you can use another button that changes a variable:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.shouldPrint = True
self.initialize()
def lock_func(self):
while self.shouldPrint:
print "blah"
def setShouldPrint(self, value):
self.shouldPrint = value
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.stopBtn = Tkinter.Button(self, text = "Stop", command = lambda: self.setShouldPrint(False))
self.processBtn.grid(row = 1)
self.stopBtn.grid(row = 2)
app = GUI(None)
app.mainloop()

Categories