I have two separate columns of checkbuttons in tkinter in python with identical labels.
My goal is to disable those in one of the columns when the same item is selected in another column.
I can do this outside of classes and functions, but am having trouble calling the variables within the functions.
When I plug in static values for the dynamic parameters and remove the loop on the command function creation to disable the buttons, it works perfectly. Whenever I use the loop, however, I receive an "AttributeError: 'tool' object has no attribute 'dsblr0'" response.
How can I access the loop-created functions, such that I can disable the boxes in the second column whenever the same items are checked in the first column?
Any help is greatly appreciated!
My current code:
from tkinter import *
from tkinter import messagebox
buttonnames = []
class tool(Frame):
def get_button_names(self):
self.buttonnames=['b1','b2','b3','b4','b5']
global buttonnames
for item in self.buttonnames:
buttonnames.append(item)
def __init__(self, parent):
'''
Constructor
'''
Frame.__init__(self, parent)
self.parent=parent
self.get_button_names()
self.display_new_window()
for i in range(len(buttonnames)):
exec('''def dsblr{0}(self):
self.bcb{0}.config(state=DISABLED if self.var{0}.get() else NORMAL)'''.format(i,))
def display_new_window(self):
""" Transpose tools
"""
self.parent.title("Tool")
self.parent.grid_rowconfigure(0, weight=1)
self.parent.grid_columnconfigure(0, weight=1)
for i,column in enumerate(self.buttonnames):
exec('self.var{0}=IntVar()'.format(i,))
exec('self.bvar{0}=IntVar()'.format(i,))
exec('self.cb{0} = Checkbutton(self.parent, text=column, variable=self.var{0},command=self.dsblr{0})'.format(i,))
exec("self.cb{0}.grid(row=i+1,column=0,sticky='w')".format(i,))
exec('self.bcb{0} = Checkbutton(self.parent, text=column, variable=self.bvar{0})'.format(i,))
exec("self.bcb{0}.grid(row=i+1,column=1,sticky='w')".format(i,))
def main():
root=Tk()
d=tool(root)
root.mainloop()
if __name__=="__main__":
main()
Related
When I click on a label to open an external window (tertiary.py), the window opens normally but is empty inside. I don't get errors, but I don't see Tkinter widgets and miscellaneous items.
This is the secondary window where there is the label on which I click in order to open an external window. I add, for information purposes only, that this window is called secondary because it is displayed in a frame (with a vertical menu on the left) where the main window is called home = tk.Tk(). The secondary window opens and displays correctly in the frame.
Inside the secondary window there is the label thanks to which I want to open an external window (not in the frame) called tertiary
How can I solve?
secondary.py
import tkinter as tk
from tkinter import *
from tkinter import ttk
import other_options
from other_options import form_other_options
class Page2(tk.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
self.configure(bg='white')
bar1=Frame(self, width=2200, height=35, background="#960000", highlightbackground="#b40909", highlightthickness=0) #grigio chiaro #b40909
bar1.place(x=0, y=0)
other_options = Label(self, text="Other Options", bg="#960000", foreground='white', font='Ubuntu 10')
other_options.place(x=1025, y=7)
other_options.bind("<Button-1>", lambda event: other_options.form_other_options(self))
tertiary.py
from tkinter import *
from tkinter import ttk
import tkinter as tk
def form_other_options(parent):
other_options = tk.Toplevel(parent)
other_options.title("Other options")
other_options.geometry("1000x800")
other_options.config(bg="white")
other_options.state("normal")
other_options.transient(parent)
class checkbox():
def __init__(self, master, **kw):
super().__init__(master, **kw)
labelframe1 = LabelFrame(self, text="checkbox", width=600,height=190, bg="white", foreground='#e10a0a')
labelframe1.place(x=10, y=13)
Checkbutton1 = IntVar()
Button1 = Checkbutton(self, text = "option1",
variable = Checkbutton1,
onvalue = 1,
offvalue = 0,
height = 2,
width = 10)
Button1.pack()
other_options.mainloop()
The first reason why the window is empty is that you never create an instance of the class checkbox, so the code that creates the widgets never runs.
So, the first step is to make sure you create an instance of that class:
other_options = tk.Toplevel(parent)
...
cb = checkbox(other_options)
When you do that, you will discover other bugs in your code. The first positional argument when creating a tkinter widget must be another widget. If you want the widget to be inside a Toplevel, the parent needs to be the toplevel or a descendant. You're using self which isn't a widget at all.
A simple fix is to use master instead of self, since you're passing the toplevel window as the master parameter:
labelframe1 = LabelFrame(master, ...)
Button1 = Checkbutton(master, ...)
You also need to remove the call to other_options.mainloop(). Unless there is a very compelling reason to do otherwise, an entire application should only call mainloop() exactly once.
There are other problems in the code that, strictly speaking, aren't related to the question being asked. For example, you're importing tkinter twice: once as import tkinter as tk and once as from tkinter import *. You only need to import tkinter once, and PEP8 recommends to use the first form:
import tkinter as tk
You'll need to add the prefix tk. to every place where you call a tkinter function. This will help make your code easier to understand and helps prevent pollution of the global namespace.
You also should use PEP8 naming standards. Specifically, class names should begin with an uppercase character. This will also make your code easier to understand.
There's also no reason why your checkbox class should be indented. You should move it out of the function.
Also, instead of checkbox not inheriting from anything, and then creating a LabelFrame, you should just inherit from LabelFrame. That way you can use your class more like a real widget, and would enable you to use self rather than master when creating the other widgets.
class Checkbox(tk.LabelFrame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
...
button1 = tk.Checkbutton(self, ...)
...
There are probably other problems, but those are the most obvious.
I am wondering if there is a way, in tkinter or using any other python module, to make it so you keep a label or any other element in every window made by just using something like a function that makes the label within the window? I've tried this:
#Modules
import tkinter as tkin
#The initializer window
class Test:
def __init__(self):
#Initializes the main window
self.start = tkin.Tk()
self.start.title('Test')
self.label_thing()
#Makes a label
def label_thing(self):
self.label1 = tkin.Label(text='Not Button')
self.label1.pack()
I don't know if I made any errors or if this isn't a thing you can do but I'd also like to refrain from having the label localized to this window and having to remake the code for every window.
Let us assume you have a button that creates windows, you would pass this window as an argument to the function that creates the label, so like:
import tkinter as tk # Fixed weird import naming
class Test:
def __init__(self):
self.start = tk.Tk()
self.start.title('Test')
self.label_thing(self.start) # Label on the main window
tk.Button(self.start,text='Click me',command=self.create_win).pack()
self.start.mainloop()
def label_thing(self, master):
self.label1 = tk.Label(master, text='Not Button')
self.label1.pack()
def create_win(self):
new = tk.Toplevel()
self.label_thing(new) # Label on the new window
if __name__ == '__main__':
Test()
As you can see, as long as you press the button, new windows are created, and all those windows have the label on them, dynamically.
I have written some code primarily to be used with the console, but was asked to create a simple GUI for it for ease of use. In it, I am setting up the main frame with widgets, and using the widget command to call upon the function that I import. However, the imported functions and modules all write to the output console. Is there a means of returning the output string/console output to be updated in the GUI as the subprocess runs?
Example script:
import Tkinter as *
import myModule1
class MyGUI(Frame):
def __init__(self):
# Initialization stuff.
self.initGUI()
def initGUI(self):
self.downloadButton = Button(text="Download data.")
self.downloadButton["command"] = myModule1.function
# This function from myModule1 will constantly print to the console as the process is performed - is there any way to have this displayed in the GUI as a ScrollBar?
.....
Alternatively, is there a way to make a dialog window show up while the process is running? I ask because I have embedded a lot of print statements in the myModule1 and its submodules that return what is going on to the console. It would be nice to display those for the users one the GUI is working and I convert it to a .exe for ease of use of those who will be using it.
Thank you.
EDIT: An example of what myModule1.function can look like:
import otherModule
def function1():
log = otherModule.function2():
if log == True:
print "Function 2 worked."
elif log == False:
print "Function 2 did not work."
However, function2 in otherModule prints to console as it performs its calculations. That is not explicitly shown here, but the console output would essentially be a series of calculations followed by the example if/elif clause shown above.
It won't be extremely simple, but one way to do this is create a method or function in your GUI that writes to a text widget or some other widget whose content can be updated easily. I'll call it outputMethod. Then, pass this function or method to myModule1.function(outputMethod). Within the module1.function, replace print statements with the outputMethod call, providing the appropriate parameters to it. Without seeing module1.function, it's difficult to provide a complete example that would work. Added the following example once the OP posted myModule1 sample code.
from Tkinter import *
import myModule1
class MyGUI(Frame):
def __init__(self, parent):
# Initialization stuff.
self.initGUI(parent)
def initGUI(self, parent):
Frame.__init__(self, parent)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(1, weight=1)
self.pack(expand='yes', fill='both')
self.downloadButton = Button(self, text="Download data.")
self.downloadButton["command"] = lambda m=self.outputMethod: myModule1.function(m)
self.text = Text(self)
self.downloadButton.grid(row=0, column=0)
self.text.grid(row=1, column=0, sticky='news')
self.sy = Scrollbar(self, command=self.text.yview)
self.text['yscrollcommand'] = self.sy.set
self.sy.grid(row=1, column=1, sticky='nsw')
def outputMethod(self, the_output):
self.text.insert('end', the_output + '\n')
# Scroll to the last line in the text widget.
self.text.see('end')
if __name__ == '__main__':
# Makes the module runable from the command line.
# At the command prompt type python gui_module_name.py
root = Tk()
app = MyGUI(root)
# Cause the GUI to display and enter event loop
root.mainloop()
Now for module1...
def function(log):
# Note that log is merely a pointer to 'outputMethod' in the GUI module.
log("Here is a string you should see in the GUI")
log("And this should appear after it.")
# Now demonstrate the autoscrolling set up in outputMethod...
for i in range(50):
log("Inserted another row at line" + str(i + 2))
I have an auto generated code which generates a GUI that has various widgets in it. One of the widget is a ScrolledListBox. A part of the code is shown below:
class New_Toplevel_1:
def __init__(self, top=None):
self.Scrolledlistbox4.configure(background="white")
self.Scrolledlistbox4.configure(font="TkFixedFont")
self.Scrolledlistbox4.configure(highlightcolor="#d9d9d9")
self.Scrolledlistbox4.configure(selectbackground="#c4c4c4")
self.Scrolledlistbox4.configure(width=10)
I want to access the Scrolledlistbox4 from outside this class. So for example, I would like to write to write a function that updates the ScrolledListBox whenever I call it. I am relatively new to python and would like to know how can I accomplish this.
You need to first create a Scrolledlistbox4 object as an attribute:
self.scrolled_listbox = Scrolledlistbox4(...)
then you can do all configures in outermost scope like:
a = New_Toplevel_1()
a.scrolled_listbox.configure(background='white')
...
In below example "Outside Button" changes the text option of a class' button from the outside:
import tkinter as tk
class FrameWithButton(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.btn = tk.Button(root, text="Button")
self.btn.pack()
root = tk.Tk()
an_instance = FrameWithButton(root)
an_instance.pack()
def update_button():
global an_instance
an_instance.btn['text'] = "Button Text Updated!"
tk.Button(root, text="Outside Button", command=update_button).pack()
root.mainloop()
I have a question. I thought that a class can be based on an object or previously define class. When I change it into class Application(object): it doesn't work. Can you tell me why it didn't work and why did the code below works or why did class Application(Frame) works? Frame is not a previously define object and not object. Sorry for my English. Here is my code:
# Lazy Buttons
# Demonstrates using a class with tkinter
from tkinter import *
class Application(Frame): #
""" A GUI application with three buttons. """
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
""" Create three buttons that do nothing. """
# create the first Button
self.bttn1 = Button(self, text= "I do nothing.")
self.bttn1.grid()
# create second button
self.bttn2 = Button(self)
self.bttn2.grid()
self.bttn2.configure(text="Me too!")
# create third button
self.bttn3 = Button(self)
self.bttn3.grid()
self.bttn3["text"] = "Same here!"
# main
root= Tk()
root.title("Lazy Buttons")
root.geometry("200x85")
app = Application(root)
root.mainloop()
Frame is a previously defined class. It's part of tkinter, which you imported on your first line.
Your Application class extends Frame, which means that it gets methods from Frame and can do everything that a Tk Frame can do, like show widgets. If you don't extend Frame, and only extend object instead, this will not work.
It might be clearer to replace...
from tkinter import *
with...
import tkinter as tk
and fix your references to Tk's classes (they would become tk.Button, tk.Frame and tk.Tk).