class main():
def __init__(self):
self.root = tk.Tk()
# Icono
self.notebook=ttk.Notebook(self.root)
self.notebook.pack(fill='both',expand='yes')
# Crear Frames blancos
tab_FBR1=tk.Frame(self.notebook,bg='white') #
tab_FBR2=tk.Frame(self.notebook,bg='white') #
tab_FBR3=tk.Frame(self.notebook,bg='white') #
#ASIGNACIÓN PESTAÑAS FBR1,FBR2 Y FBR3
self.notebook.add(tab_FBR1,text='FBR1') #
self.notebook.add(tab_FBR2,text='FBR2') #
self.notebook.add(tab_FBR3,text='FBR3') #
# Configurations FBR1, FBR2 y FBR3
self.window_FBR(tab_FBR1)
self.window_FBR(tab_FBR2)
self.window_FBR(tab_FBR3)
I want to create 3 windows calling a method called def window_FBR, to create 3 windows with their own variables.
def window_FBR(self,tab):
self.rcolor=tk.IntVar(value=4)
tk.Radiobutton(tab, text="Red", variable=self.rcolor, value=1, command=self.color_rojo(tab),font=("arial",10),bg=('white')).place(x=10,y=70)
However is not working, have you guys have some ideas about how to manage the variables, in the method to create different variables each time I am calling the method?
many thanks
I want to create a GUI in Tkinter with 3 windows.
I have a problem because when the variables are created, they did not start with the default value for the self.rcolor=tk.IntVar(value=4)
My solution for multiple windows that have their own variables is to create an independent class that takes its own parameters, and has its own subclasses if necessary, you can pass one or several different parameters in each case, hope that helps.
from tkinter import *
class SubWin(Tk):
def __init__(self, string, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('300x300')
label = Label(master=self, text=string)
label.pack()
class App(Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('300x300')
sub1 = SubWin(string='Helooo1')
sub2 = SubWin(string='Helooo2')
if __name__ == '__main__':
app = App()
app.mainloop()
Related
I'm using Tkinter's "validatecommand" function to validate inputs from a entry box. I want to pass my class object so that the validation-function can request information from the object. However, it seems that the validatecommand function turns everything I pass into strings. Because of this the validation-function now has __main__.foo object at 0x042981B0 but as string. How can I instead pass the original __main__.foo?
It currently looks like this (pseudo-code):
class foo(object):
def start(program):
self.stuff = 5 #stuff changes while the program is running
tkinter_stuff(program)
def tkinter_stuff(program):
Entry = tkinter.Entry(validatecommand = (window.register(validate_entry), '%P', program))
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
if int(entry) + program.get_stuff() <= 20:
return True
return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)
actual code:
import tkinter
class foo(object):
def __init__(self):
self.stuff = 5 #stuff changes while the program is running
def start(self, program):
tkinter_stuff(program)
def get_stuff(self):
return self.stuff
def tkinter_stuff(program):
window = tkinter.Tk(className = 'window')
window.geometry('50x50')
print(program, type(program))
Entry = tkinter.Entry(window, width = 10, validate = 'key', validatecommand = (window.register(validate_entry), '%P', program))
Entry.place(x = 10, y = 10)
window.update()
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
print(program, type(program))
if int(entry) + program.get_stuff() <= 20:
return True
return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)
Try this:
import tkinter as tk
class Entry(tk.Entry):
def __init__(self, master=None, args=tuple(), validatecommand=None, **kwargs):
if validatecommand is not None:
self.args = args
self.callers_function = validatecommand[0]
validatecommand = (root.register(self.validatecommand), *validatecommand[1:])
super().__init__(master, validatecommand=validatecommand, **kwargs)
def validatecommand(self, *args):
return self.callers_function(*args, *self.args)
class Foo:
def __init__(self):
pass
def validate_entry(entry, program):
print(type(entry), type(program))
return True
program = Foo()
root = tk.Tk()
# Make sure it's not `root.register(validate_entry)`:
entry = Entry(root, validate="key", validatecommand=(validate_entry, "%P"),
args=(program, ))
entry.pack()
root.mainloop()
I just made a wrapper class that will call the validatecommand with the args that were specified when creating the Entry.
This question already has answers here:
How to access variables from different classes in tkinter?
(2 answers)
Closed 6 years ago.
I have data in x and y arrays that I want to pass between two Python classes. When button is clicked I run command=lambda: controller.show_frame(PlotPage) to switch from SelectPage (which chooses data) to PlotPage (which plots x and y). I want x and y saved before the page switch or within the button lambda. Is saving the arrays as global variables the best way to pass the data to PlotPage, or is there a more convenient way to include these arrays in the button lambda function?
# possible global variables
global x = [stuff x]
global y = [stuff y]
class SelectPage(tk.Frame):
def __init__(self,parent,controller):
button = tk.Button(self,text="Plot",
command=lambda: controller.show_frame(PlotPage),
[some_lambda_here]) # Possible lambda addition
class PlotPage(tk.Frame):
def __init__(self,parent,controller):
[Tkinter plot intialization stuff]
plotData(x,y) # plotData creates the plot
Controller Class:
class Project:
def __init__(self, *args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
container = tk.Frame(self)
container.pack(side="top",fill="both",expand=True)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
self.frames = {}
for F in (SelectPage, PlotPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0,column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, container):
frame = self.frames[container]
frame.tkraise()
For communication between components, you should have a look at the Observer design pattern and MVC architecture. You could then structure the program along these lines (I'm skipping the Tk instructions here):
class Observable:
def __init__(self, signals):
# create signal map
def connect(self, signal, handler):
# append handler to the signal in the map
def emit(self, signal, *args, **kwargs):
# iterate over signal handlers for given signal
class Model(Observable):
def __init__(self):
super().__init__("changed")
self.x = []
self.y = []
def set_x(self, x):
self.x = x
self.emit("changed")
def append_x(self, value):
self.x.append(value)
self.emit("changed")
# same for y
class PlotView(SomeTKTClass):
def __init__(self, model):
self.model = model
model.connect("changed", lambda: self.plot(model.x, model.y))
def plot(self, x, y):
#some tk rendering here
# SelectPage and StartPage are defined somewhere.
class MainController(SomeTkClass):
def __init__(self):
# ...
self.model = Model()
self.startPage = StartPage() # I suppose
self.plotView = PlotView(self.model)
self.selectPage = SelectPage()
self.frames = {}
for view in {self.startPage, self.plotView, self.selectPage}:
self.frames[view.__class__] = view
# ...
self.show_frame(StartPage)
def show_frame(self, container):
frame = self.frames[container]
# ...
The implementation of the Observer pattern can be done in many ways. The one suggested here is simple. There are many ways to improve upon this rough sketch, but the idea is to let the observable model notify the view that its data has changed and can be redrawn in the plot.
So I have a class, Application, with 2 subclasses MyButton and MyLabel. Application also has
self.backgroundcolor = 'orange'
self.textcolor = 'black'
I want to use these two variables in my subclasses MyButton and MyLabel. So, I tried
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
but it doesn't work, saying that
TypeError: super(type, obj): obj must be an instance or subtype of type
But my Application class looks like
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
MORE STUFF HERE...
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
Nested class cant access directly attributes of outer class. You need to do it indirectly, for example:
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
# create button and label and pass the application instance
# so that they can reference its attributes and methods
self.my_button = MyButton(self)
self.my_label = MyLabel(self)
class MyButton(Button):
def __init__(self, app_instance, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = app_instance.backgroundcolor
self['fg'] = app_instance.textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, app_instance, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = app_instance.textcolor
So I have a class, Application, with 2 subclasses MyButton and MyLabel.
MyButton and MyLabel do not appear to be subclasses of Application. You can only call super() to access Application from subclasses.
For example, to make MyLabel a subclass of Application
class MyLabel(Application):
def __init__(self, *args, **kwargs):
...
In the call to super() you have to pass your type and "yourself" (self):
so instead of doing:
super(Application, self)
you should do:
super(MyButton, self)
Hence the error, obj must be an instance or subtype of type:
self is not an instance nor subtype of Application
There is a confusion in this example between a nested class, which is a class defined within another class, and a subclass, which is a class defined by extending an existing class.
In Python, class inheritance (upon which the super method is based) is accomplished by defining a class with the super class provided as a parameter:
class Application(Frame):
... #Some class definition
class MyButton(Application):
... #MyButton is now a subclass of Application.
But this is not what you want, since you want to inherit the behaviour of the Tkinter Button and Label classes for your MyButton and MyLabel classes respectively (rather than inheriting the behaviour of the Application class.
You original attempt using nested classes doesn't appear as necessarily a bad idea, since it would neatly package all of the behaviour of your class into one place, but it has serious drawbacks which probably aren't what you want.
For a start, you cannot reference an instance of the Application class from the nested classes without injecting it in somehow, such as during initialisation. You can, however, access the properties if they are class properties, defined in the Application class namespace, just like you nested classes. This is probably confusing so here is an example:
class Application(object):
classvar = "Foo" #This is a class level variable
class NestedClass(object):
#This is now a nested class, accessed using Application.NestedClass
def __init__(self, app):
#This will fail, classvar doesn't exist in this scope
self.var = classvar
#This will work if app is an instance of Application
#Or if app is the class itself
self.var = app.classvar
#This will always work, since it references the Application
#class directly but it won't capture changes introduced
#by an instance of Application, which is what you'll probably do
self.var = Application.classvar
Class level behaviour becomes very confusing due to scoping, and nested classes are even more confusing for nothing that can't be gained from implementing every class at the module level.
The best way to inject this kind of requirement is to do it the very way Tkinter does it itself. Hand off the Application instance as the master of the widget instance it creates. This is shown in Marcin's answer.
I have the following outline of a Python program (using PyQt4):
class Polygon( QtGui.QGraphicsItem ):
def __init__(self):
super(Polygon, self).__init__()
def addpoint( self, point ):
if last_point:
# trying to add auto-save here
class MainWidget(QtGui.QWidget):
openFileName = ""
list_of_polygons = []
def __init__(self):
super(MainWidget, self).__init__()
def openFile( self ):
call dialog
self.openFileName = ...
def saveFile( self ):
# needs to access a couple something with self.variables, like self.openFileName
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWidget()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Functionality is an image viewer where I'm creating polygons to tag object. I'd like to call an auto-save once a polygon has been created.
So for saving a polygon I need to call it from the saveFile function MainWidget class. My problem is that the saving functionality is implemented in the MainWidget class, and I don't know how to access them from inside the Polygon class.
What would be the best idea to do this? Should I make saveFile global? If yes, then how do I access the self. variables of the MainWidget?
You probably need to pass the widget object to the Polygon when you create it so that it knows what its "parent" widget object is:
class Polygon( QtGui.QGraphicsItem ):
def __init__(self, parent):
self.parent = parent
# ...
(and then foo = Polygon(your_widget) when you create it).
Then you can just call self.parent.saveFile(...) or whatnot.
I have a PySide (Qt) GUI which spawns multiple threads. The threads sometimes need to update the GUI. I have solved this in the following way:
class Signaller(QtCore.QObject) :
my_signal = QtCore.Signal(QListWidgetItem, QIcon)
signaller = Signaller()
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
signaller.my_signal.emit(self.item, icon)
#
# MAIN WINDOW
#
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# ...
# Connect signals
signaller.my_signal.connect(self.my_handler)
#QtCore.Slot(QListWidgetItem, QIcon)
def my_handler(self, item, icon):
item.setIcon(icon)
def do_something(self, address):
# ...
# Start new thread
my_thread = MyThread(newItem)
my_thread.start()
# ...
Is there an easier way? Creating the signals, handlers and connect them requires a few lines of code.
I started coding with PySide recently and I needed a equivalent of PyGObject's GLib.idle_add behaviour. I based the code off of your answer ( https://stackoverflow.com/a/11005204/1524507 ) but this one uses events instead of using a queue ourselves.
from PySide import QtCore
class InvokeEvent(QtCore.QEvent):
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
def __init__(self, fn, *args, **kwargs):
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
self.fn = fn
self.args = args
self.kwargs = kwargs
class Invoker(QtCore.QObject):
def event(self, event):
event.fn(*event.args, **event.kwargs)
return True
_invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker,
InvokeEvent(fn, *args, **kwargs))
Which is used the same way in the above answer link.
This is what I have so far. I wrote the following code somewhere in a helper module:
from Queue import Queue
class Invoker(QObject):
def __init__(self):
super(Invoker, self).__init__()
self.queue = Queue()
def invoke(self, func, *args):
f = lambda: func(*args)
self.queue.put(f)
QMetaObject.invokeMethod(self, "handler", QtCore.Qt.QueuedConnection)
#Slot()
def handler(self):
f = self.queue.get()
f()
invoker = Invoker()
def invoke_in_main_thread(func, *args):
invoker.invoke(func,*args)
Then my threads can very easily run code to update the GUI in the main thread. There is no need to create and connect signals for every operation.
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
invoke_in_main_thread(self.item.setIcon, icon)
I think something like this is quite nice.