Tkinter: In a custom widget, how do I set the value of a custom variable through .configure? - class

I would like to use this command to set my custom variable, how to do this:
self.ent1.configure(my_custom_var='teste')
I want my custom variable to be part of the .configure dictionary
sample code:
from tkinter import *
class My_Entry(Entry):
def __init__(self, parent, my_custom_var='', *args, **kwargs):
super().__init__(parent, *args, **kwargs)
#print('my_custom value: ', my_custom_var)
print(self['my_custom_var'])
return
def configure(self, **kwargs):
super().configure(**kwargs)
print(kwargs) #<--- my custom var here in kwargs
#--------------
class Mainframe(Tk):
def __init__(self):
Tk.__init__(self)
#self.ent1 = My_Entry(self, my_custom_var='teste')
self.ent1 = My_Entry(self)
self.ent1.configure(show='*')
#self.ent1.configure(show='*', my_custom_var='teste')
self.ent1.pack()
return
if __name__== '__main__':
app = Mainframe()
app.mainloop()

Tkinter doesn't have a way to add an option that works exactly like the built-in options. However, you can override configure and cget to handle both your custom options and the default options.
Here's an example of one way to do it, though it's not the only way.
class My_Entry(tk.Entry):
# tuple of supported custom option names
custom_options = ("my_custom_var",)
def __init__(self, parent, *args, my_custom_var='', **kwargs):
super().__init__(parent)
self.configure(my_custom_var=my_custom_var, **kwargs)
def configure(self, **kwargs):
for key in self.custom_options:
if key in kwargs:
setattr(self, key, kwargs.pop(key))
if kwargs:
super().configure(**kwargs)
def cget(self, key):
if key in self.custom_options:
return getattr(self, key)
else:
return super().cget(key)
This lets you use either cget or directly access the class attribute:
entry = My_Entry(root, width=40, my_custom_var="Hello, world")
print(f"custom var via cget: {entry.cget('my_custom_var')}")
print(f"custom var via attribute: {entry.my_custom_var}")
And within the class, you can do likewise:
print(f"custom var via cget: {self.cget('my_custom_var')}")
print(f"custom var via attribute: {self.my_custom_var}")

Related

How to access/use variables in methods defined in a Mixin class?

I'm trying to break a tkinter app with several classes into multiple .py files. I'm using a Mixin class to import methods into each class. However, I'm struggling to access variables.
main.py contains a class creating the main window with a button to open a top level window and a button to get a variable from the Mixin.
# main.py
import tkinter
from tkinter import Tk
import customtkinter
import sys
sys.path.insert(1, "path/Classes")
from Classes import topLevel
# Main Window
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
# Top Level Button
self.topLevel_button = customtkinter.CTkButton(master=self,
text="Open",
command=self.openTopLevel)
self.topLevel_button.grid(row=0, column=0)
# Get Variable
self.get_button = customtkinter.CTkButton(master=self,
text="Get Variable",
command=self.getVariable)
self.get_button.grid(row=1, column=0)
def openTopLevel(self):
window = topLevel.topLevel(self)
def getVariable(self):
print(var) # Can't access var
if __name__ == "__main__":
app = App()
app.mainloop()
topLevel.py is a class creating the top level window. It contains a variable fileSep to be used by a method in the Mixin Class:
# topLevel.py
sys.path.insert(1, "path/Methods")
from Methods import _topLevel
class topLevel(customtkinter.CTkToplevel, _topLevel.Mixin):
def __init__(self, parent):
super().__init__(parent)
# Create Variable
global fileSep
fileSep = customtkinter.StringVar(value="Comma")
print("Inside Class: " + fileSep.get()) # Works
# Create Button
self.loadButton = customtkinter.CTkButton(master=self,
text="Load",
command=self.loadFile)
self.loadButton.grid(row=0, column = 0, sticky='nswe')
# Attempt to access variable
def access_method(self):
print("Access Method: " + self.fileSep.get())
And _topLevel.py contains the mixin class:
# _topLevel.py
class Mixin:
def loadFile(self):
# Trying to access fileSep variable
print("Inside Mixin: " + fileSep.get()) # Error
topLevel().access_method() # Error
# I'm trying to access this variable from a function in main.py
var = "Hello World"
I get the following errors because the variables aren't accessible.
NameError: name 'var' is not defined
NameError: name 'fileSep' is not defined
I've tried making variables global as well as creating methods inside the class ( access_method() ) to print the variables as described here https://www.geeksforgeeks.org/python-using-variable-outside-and-inside-the-class-and-method/, but get the error:
TypeError: __init__() missing 1 required positional argument: 'parent'
How do I access variables that are defined in a class in the Mixin class? How would I access a variable created by the loadFile function in the Mixin Class for use in methods in the class App?
Thanks to #acw1668 comment, I just needed to make fileSep an instance variable:
self.fileSep = customtkinter.StringVar(value="Comma")
And to access it in the mixin class:
print("Inside Mixin: " + self.fileSep.get())
Equally I could create an instance variable to assign variable var to. For example in the topLevel class I added:
self.a = ""
And then assigned var to this variable within the function:
var = "Hello World"
self.a = var

Locust: request_success.fire does not do anything

I made a wrapper for a Poly API function. The wrapper is supposed to fire some Locust events. An API function "authorize" is called successfully (if wrong parameters are supplied to "authorize" then it throws an exception in the console). It looks though that Locust event function is not. The statistics is not reflected in the Locust interface, like nothing is happening.
import time
from business_logic_refactored import BusinessLogic
from locust import User, task, between, events
class PolyClinet(BusinessLogic):
_locust_environment = None
def __getattr__(self, name):
#func = BusinessLogic.__getattr__(self, name)
func = BusinessLogic.authorize
def wrapper(*args, **kwargs):
start_time = time.time()
try:
result = func(*args, **kwargs)
events.request_success.fire(request_type="TEST", name=name, response_time=total_time, response_length=0)
self._locust_environment.events.request_success.fire(request_type="TEST", name=name, response_time=total_time, response_length=0)
except Fault as e:
total_time = int((time.time() - start_time) * 1000)
self._locust_environment.events.request_failure.fire(request_type="TEST", name=name, response_time=total_time, exception=e)
else:
total_time = int((time.time() - start_time) * 1000)
events.request_success.fire(request_type="grpc", name=name, response_time=total_time, response_length=0)
###self._locust_environment.events.request_success.fire(request_type="TEST", name=name, response_time=total_time, response_length=0)
# In this example, I've hardcoded response_length=0. If we would want the response length to be
# reported correctly in the statistics, we would probably need to hook in at a lower level
return wrapper
class PolyUser(User):
abstract = True
def __init__(self, *args, **kwargs):
super(PolyUser, self).__init__(*args, **kwargs)
self.client = PolyClient()
self.client._locust_environment = self.environment
class ApiUser(PolyUser):
#task(10)
def get_time(self):
self.client.authorize("user", "password","URL")
UPD 06/11/20:
Basically, the problem is that .getattr is only called on a non-existing function. Here is the script to try and see the difference/problem:
class BusinessLogic():
def __getattr__(self, name):
return name
def authorize(self):
print("authorize")
class PolyClinet(BusinessLogic):
_locust_environment = None
def __getattr__(self, name):
print("getattr")
func = BusinessLogic.__getattr__(self, name)
def wrapper(*args, **kwargs):
print("wrapper")
func()
return wrapper
class PolyUser():
abstract = True
def __init__(self, *args, **kwargs):
super(PolyUser, self).__init__(*args, **kwargs)
self.client = PolyClinet()
#self.client._locust_environment = self.environment
class ApiUser(PolyUser):
def get_time(self):
print("get_time")
self.client.authorize()
def get_time2(self):
print("get_time2")
self.client.authorize2()
c= ApiUser()
c.get_time()
print("*******")
c.get_time2()
To log an event, you’re supposed to do self.environment.events.request_success.fire(...) in your User class
See https://github.com/SvenskaSpel/locust-plugins/blob/2958d265857a8730af3b6cd9fd05f8839ec8f383/locust_plugins/users.py#L70 for example.
I’m not sure what happens when you call events.request_success directly. Maybe it should throw an exception.. It could probably be a lot better documented as well...
Edit: The root problem was that BusinessLogic has its own regular (non-getattr) methods, in which case, subclassing it will make a client unable to wrap the BusinessLogic method (because the client's getattr will not get called) The example in the documentation should be improved. https://github.com/locustio/locust/issues/1423
In my case the problem was the Locust version. In version 1.4.4 events.request_success.fire works (and named EventHook) but in version 1.5.1 it should be just request object and events.request_success.fire has already DeprecatedEventHook name.
https://docs.locust.io/en/stable/api.html#locust.event.Events.request

python initialize class and base class using class method

I am trying to initialize a derived class from text file input. A simple example of what I am trying to do:
file.txt:
1
2
main.py:
class Base:
def __init__(self, val1):
self.val1 = val1
def input_from_text(cls, init_deque):
#return cls(init_deque.popleft())
class Derived(Base):
def __init__(self, val1, val2):
Base.__init__(self, val1)
self.val2 = val2
def input_from_text(cls, init_deque):
#initialize base and derived here and return derived
def main(argv=None):
initialized_derived = Derived.input_from_text(deque(open("file.txt")))
assert initialized_derived.val1 is 1
assert initialized_derived.val2 is 2
Is there a good way to do this? Basically looking for something similar to what you would find in C++ with:
//calls operator>>(Base) then operator>>(Derived)
cin >> initialized_derived;
This way each class is nicely encapsulated and the base/derived classes don't need to know anything about each other (excepting __init__ which knows the number of args base takes).
Just realized that I was going about this the wrong way. Simple fix is to do something like:
class Base:
def __init__(self):
pass
def input_from_text(self, init_deque):
self.val1 = init_deque.popleft()
class Derived(Base):
def __init__(self):
Base.__init__(self)
def input_from_text(self, init_deque):
Base.input_from_text(self, init_deque)
self.val2 = init_deque.popleft()

Splitting python class into multiple files

I wondered if anyone could tell me is it possible to divide the following code into two python classes which will then be placed in two different files:
from GUI import mainGUI
from GUI import subwindowGUI
class MyApp(QMainWindow, mainGUI.Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
# mainGUI contains QMdiarea in which it opens
# a subwindow by calling a function MySubWin(self)
# QMdiarea name is 'body'
self.OpenSub.triggered.connect(self.MySubWin)
def MySubWin(self):
self.MySubWin = QWidget()
self.MySubWin.setObjectName('Module window')
self.myTabs = QtabWidget(self.MySubWin)
self.myTabs.setObjectName('myTabs')
# now I call the function that will display
# the gui elements inside this 'Module window'
self.create_module_gui()
self.body.addSubWindow(self.MySubWin)
self.MySubWin.showNormal()
def create_module_gui(self, *args):
module_gui = subwindowGUI.Ui_ModuleWindow()
module_gui.setupUi(module_gui)
self.myTabs.addTab(module_gui, _('New tab'))
self.myTabs.setCurrentWidget(module_gui)
As you can see from the code above my functions are interacting with the main window gui elements. I wanted to move these functions that are related to this specific module into a separate file for the maintaining purposes. That's why I'm asking you to help me on how to achieve that if it's even possible. Thanks in advance, Tomislav.
#Mel:
If I for move those functions into another file:
myFunctions.py
class MyFunctions(object):
def MySubWin(self):
self.MySubWin = QWidget()
self.MySubWin.setObjectName('Module window')
self.myTabs = QtabWidget(self.MySubWin)
self.myTabs.setObjectName('myTabs')
# now I call the function that will display
# the gui elements inside this 'Module window'
self.create_module_gui()
self.body.addSubWindow(self.MySubWin)
self.MySubWin.showNormal()
def create_module_gui(self, *args):
module_gui = subwindowGUI.Ui_ModuleWindow()
module_gui.setupUi(module_gui)
self.myTabs.addTab(module_gui, _('New tab'))
self.myTabs.setCurrentWidget(module_gui)
and then I import that file into my main file.
import myFunctions
class MyApp(QMainWindow, mainGUI.Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
# mainGUI contains QMdiarea in which it opens
# a subwindow by calling a function MySubWin(self)
# QMdiarea name is 'body'
self.OpenSub.triggered.connect(MyFunctions.MySubWin)
What I end up with is the error saying that MyFunctions object has no attribute body.
1st I must say that you won't gain any maintenance out of this ... but for example lets move the create_module_gui to another class
in a new file: creategui.py
class CreateGUI(object):
def create_module_gui(self, *args):
module_gui = subwindowGUI.Ui_ModuleWindow()
module_gui.setupUi(module_gui)
self.myTabs.addTab(module_gui, _('New tab'))
self.myTabs.setCurrentWidget(module_gui)
in your code:
from GUI import mainGUI
from GUI import subwindowGUI
from creategui import CreateGUI
class MyApp(QMainWindow, mainGUI.Ui_MainWindow, CreateGUI):
#yay you have your create_module_gui method now...
Now you can also just put create_module_gui as a function in another file and call it with self as the first param...
See this solution: https://stackoverflow.com/a/47562412/10155767
In your case, don't make a class in myFunctions.py. Instead, define the functions at the top level. Thus myFunctions.py should look like:
def MySubWin(self):
...
def create_module_gui(self, *args):
...
Then in your original file, import the functions within the class like
class MyApp(QMainWindow, mainGUI.Ui_MainWindow):
from myFunctions import MySubWin, create_module_gui
def __init__(self, parent=None):
...

How to disable class instantiation in Python?

Sometimes I create a class and its only used to store some values or static methods and I never want to create an instance.
Is there some way to express this in Python3?
Eg:
class MyClass:
#staticmethod
def hello():
print("world")
# is there a better way to do this?
def __init__(self):
raise Exception("instantiation isnt supported for this class!")
# OK
MyClass.hello()
# NOT OK
c = MyClass()
You can use the objectless baseclass found in this answer (which is actually an answer to a different question).
class objectless:
def __new__(cls, *args, **kwargs):
raise RuntimeError('%s should not be instantiated' % cls)
class UninstantiateableClass(objectless):
#classmethod
def foo(cls):
return 'bar'
...
Note that in python2, objectless should explicitly subclass object, in order to be a new-style class.