How to create a new object in the .connect function with pyqt? - python

I want to create a new object of the Class Lamp with the name of the clicked item.
self.listWidget_lamps.clicked.connect(Hue = Lamp(name_item))
Since this is not working, I would like to now what the right way looks like and how I need to inherit the Lamp class to the class Ui_MainWindow(object):.
Here is my code (issue line 52):
https://pastebin.com/rjg96kuJ
Here is the init of the class Lamps:
class Lamp:
"""Class to control phillips hue lamps/groups"""
def __init__(self, name: str):
self.name = name
with open("data.json", "r") as self.file:
self.data = json.load(self.file)
self.brightness = self.data["lamps"][self.name]["brightness"]
self.xy = self.data["lamps"][self.name]["color"]

The input into ...cliked.connect() has to be a function - something that can be called with a pair of brackets. So Hue = ... will not work. Instead, use a lambda function or a function you have defined. Also note that to get the name of an item you should use ...itemClicked.connect() not ...clicked.connect() which passes the item clicked as a parameter to the function. This is (I believe) the shortest way of doing it, although very unreadable and not recommended:
self.listWidget_lamps.itemClicked.connect(lambda item: globals().update({"Hue": Lamp(item.text())}))
This is the recommended way:
Hue = None
def new_lamp(item):
global Hue
Hue = Lamp(item.text())
self.listWidget_lamps.itemClicked.connect(new_lamp)

Related

assign function w/multiple parameters to a variable —both inside a Python class

While my code works, I'd like to clean it a bit.
I've built a couple of functions based on thethe File().trashUntrash method,
and I've done the same for other methods in the class I'm working on:
class File():
def__init__(self):
self.this = this
self.that = that
def trashUntrash(self, fileId, bool):
return file.update(fileId, isTrashed=bool)
#these two are wrappers for self.trashUntrash:
trash = lambda self, fileId: self.trashUntrash(fileId, True)
untrash = lambda self, fileId: self.trashUntrash(fileId, False)
#these other lambdas correspond to other methods:
fileTitle = lambda self, id: self.fileInfo(prop="title", spreadsheetId=id)
fileIds = lambda self, id: self.fileInfo(prop="fileId", spreadsheetId=id)
addPage = lambda self, id, title: self.action(i=0, ssId=id, title=title)
delPage = lambda self, id, pageId: self.action(i=1, ssId=id, pageId=pageId)
renameFile = lambda self, id, pageId, title: self.action(i=2, id=id ...)
So I tried assigning the method to variables that I'd then use in the rest of my code:
trash = self.trashUntrash(fileId, True)
untrash = self.trashUntrash(fileId, False)
# and so on...
... as it looks shorter and more easily readable (my goal). But... I get NameError: name 'self' is not defined. Removing self (which of course, doesn't make sense):
trash = trashUntrash(fileId, True)
untrash = trashUntrash(fileId, False)
# etc.
... will on the other hand, produce NameError: name 'fileId' is not defined. If on the other hand, I simply state:
trash = self.trashUntrash
untrash = self.trashUntrash
# etc... doing this one DOES work BUT
# w/o a chance to pass params, which defeats my intent.
... I'll get no errors, but I'll have to manually pass args, including the ones I'm using as default, making the function-to-variable assignment pointless.
So my question: Is it possible to assign a method along with its params to a variable inside a class, and how would you do it?
BTW, I'm using Python 3.10.8 (Nov 1 2022, GCC 12.2.0 on Linux); thank you in advance!
I've already gotten the job done via lambda functions;
however, they get quite lengthy as I'm using
multiple positional and keyword args. I'd like a way to make these assignments shorter to read.
In the class use partialmethod to define your shortcut. If a further shortcut is needed you can assign the partial method to a local variable:
from functools import partialmethod
class File():
def trash_untrash(self, file_id, is_trashed):
print(f"{file_id = }, {is_trashed = }")
trash = partialmethod(trash_untrash, is_trashed=True)
f = File()
f.trash(file_id=10)
trash = f.trash
trash(file_id=10)
Output:
file_id = 10, is_trashed = True
file_id = 10, is_trashed = True

Something like setter and getter for a python dictionary

I have the following problem:
A class contains a dict of dicts of dicts ... e.g.
class Try():
def __init__(self):
self._tryDict = {'first':{'b':{'bla':'x'},'c':1},'second':{'b':15,'c':1}}
#getter
def tryDict....
#tryDict.setter
def tryDict....
I would like to have now something like setter and getter to change a variable at a certain level of the dict and retrun the corresponding dict a view levels above (and only if i set a new value)
e.g.
try = Try()
try.tryDict['first']['b']['bla']
returs: 'x'
try.tryDict['first']['b']['bla']='z'
changes 'x' to 'z' and returns the content of try.tryDict['first'] ({'b':{'bla':'x'},'c':1})
To understand why I need this:
The dict is actually a list of file-praser-functions and the content is also stored as a dict.
i.e.
dict = {'file-1':dict-1, 'file-1':dict-1, 'file-1':dict-1, ...}
with dict['file-1']['content-3']=1 I set the new content
and with dict['file-1'].write(), the content of the file is updated in the file. The dict is a class instance and I would like to immediately wirte the change to the file.
many thx for your help!
BR, maths
OK so what I implemented it now this way:
Each file got setter and getter function
class dicts():
.....
# to show the content
#getter
def tryDict(self):
return tryDict.file
# to sett some content
#tryDict.setter
def tryDict(self, entry):
if len(entry)==2:
name, newEntry = entry
tryDict.file[name]= newEntry
if len(entry)==3:
....
if i whan to get the entry:
tryDicis = dicts()
tryDicis.trydict # shows the dictionary
i can now set in different levels like:
tryDicis = dicts()
tryDicis.trydict = (name-level-1, name-level-2,..., newEntry)
This is maybe not the most elegant way, however it is easy to implement and good enough in my case.
BR, maths

Can dynamically created class methods know their 'created' name at runtime?

I have a class which I want to use to extract data from a text file (already parsed) and I want do so using dynamically created class methods, because otherwise there would be a lot of repetitive code. Each created class method shall be asociated with a specific line of the text file, e.g. '.get_name()' --> read a part of 0th line of text file.
My idea was to use a dictionary for the 'to-be-created' method names and corresponding line.
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def __get_line(cls):
name = sys._getframe().f_code.co_name
line = fn_names[name] # <-- causes error because __get_line is not in fn_names
print(sys._getframe().f_code.co_name) # <-- '__get_line'
print(inspect.currentframe().f_code.co_name) # <-- '__get_line'
return print(cls.file[line][0].split('=')[1])
for key, val in fn_names.items():
setattr(Filer, key, __get_line)
f = Filer(test_file)
f.get_author()
f.get_date()
When I try to access the method name to link the method to the designated line in the text file, I do get an error because the method name is always '__get_line' instead of e.g. 'get_author' (what I had hoped for).
Another way how I thought to solve this was to make '__get_line' accepting an additional argument (line) and set it by passing the val during 'the setattr()' as shown below:
def __get_line(cls, line):
return print(cls.file[line][0].split('=')[1])
and
for key, val in fn_names.items():
setattr(Filer, key, __get_line(val))
however, then Python complains that 1 argument (line) is missing.
Any ideas how to solve that?
I would propose a much simpler solution, based on some assumptions. Your file appears to consist of key-value pairs. You are choosing to map the line number to a function that processes the right hand side of the line past the = symbol. Python does not conventionally use getters. Attributes are much nicer and easier to use. You can have getter-like functionality by using property objects, but you really don't need that here.
class Filer():
def __init__(self, file):
self.file = file
for line in file:
name, value = line[0].split('=', 1)
setattr(self, name.lower(), value)
That's all you need. Now you can use the result:
>>> f = Filer(test_file)
>>> f.author
'Donald Duck'
If you want to have callable methods exactly like the one you propose for each attribute, I would one-up your proposal and not even have a method to begin with. You can actually generate the methods on the fly in __getattr__:
class Filer():
def __init__(self, file):
self.file = file
def __getattr__(self, name):
if name in fn_names:
index = fn_names[name]
def func(self):
print(self.file[index][0].split('=', 1)[1])
func.__name__ = func.__qualname__ = name
return func.__get__(self, type(self))
return super().__getattr__(name)
Calling __get__ is an extra step that makes the function behave as if it were a method of the class all along. It binds the function object to the instance, even through the function is not part of the class.
For example:
>>> f = Filer(test_file)
>>> f.get_author
<bound method get_author of <__main__.Filer object at 0x0000023E7A247748>>
>>> f.get_author()
'Donald Duck'
Consider closing over your keys and values -- note that you can see the below code running at https://ideone.com/qmoZCJ:
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def getter(key, val):
def _get_line(self):
return self.file[val][0].split('=')[1]
return _get_line
for key, val in fn_names.items():
setattr(Filer, key, getter(key, val))
f = Filer(test_file)
print("Author: ", f.get_author())
print("Date: ", f.get_date())

Adding a new object to a class with user-input(input) in python

I am trying to add new objects to a class(emne) but the new instances of the class needs to be created using user input. So i need a way to be able to chose the name for the object and set some of the values of the objects with user input.
I have already tried to create a function that passes the value of the user input into a x = emner(x) to create it but it only returns:
AttributeError: 'str' object has no attribute 'fagKode'
so i think my issue is that the value of the input is created as a string so that it is not understood as a way to create the function
emne=[]
class Emne:
def __init__(self,fagKode):
self.fagKode = fagKode
self.karakter = ""
emne.append(self)
def leggTilEmne():
nyttEmne = input("test:")
nyttEmne=Emne(nyttEmne)
expected result is that the code creates a new instance of the class.
If by choosing a name you mean your fagKode attribute, what you need is:
fagKode = input('Enter code: ')
Emne(fagKode)
You're adding the instances of Enme to the list in the constructor, so you don't need to save them to a variable.
Alternatively, you can handle that in the function:
emne=[]
class Emne:
def __init__(self,fagKode):
self.fagKode = fagKode
self.karakter = ""
def leggTilEmne():
nyttEmne = input("test:")
enme.append(Emne(nyttEmne))
I'm not sure what exactly you are asking, since you haven't responded to the comments. So,
emne=[]
class Emne:
def __init__(self,fagKode):
self.fagKode = fagKode
self.karakter = ""
emne.append(self)
def leggTilEmne(self, value): # <--- is this what you want
self.nyttEmne= Emne(value)
This is an example of when to use a class method. __init__ should not be appending to a global variable, though. Either 1) have the class method append to a class attribute, or 2) have it return the object and let the caller maintain a global list.
emne = []
class Emne:
emne = []
def __init__(self, fag_kode):
self.fag_kode = fag_kode
self.karakter = ""
#classmethod
def legg_til_emne_1(cls):
nytt_emne = input("test:")
cls.emne.append(cls(nytt_emne))
#classmethod
def legg_til_emne_2(cls):
nyttEmne = input("test:")
return cls(nyttEmne)
Emne.legg_til_emne_1() # Add to Emne.emne
e = Emne.legg_til_emne_2()
emne.append(e)

How to make nested enum also have value

Consider the following code example:
from enum import Enum
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
How do I make Inside have the value 'inside' whilst also being a nested enum for accessing Downstairs and Upstairs?
Desired input:
print(Location.Inside)
print(Location.Inside.value)
print(Location.Inside.Downstairs)
print(Location.Inside.Downstairs.value)
Desired output:
Location.Inside
inside
Location.Inside.Downstairs
downstairs
UPDATE 1:
Some more context to my specific problem:
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Human:
def __init__(self, location):
self.location = location
def getLocationFromAPI():
# this function returns either 'inside' or 'outside'
# make calls to external API
return location # return location from api in str
def whereInside(human):
if human.location != Location.Inside:
return None
# here goes logic that determines if human is downstairs or upstairs
return locationInside # return either Location.Downstairs or Location.Upstairs
location_str = getLocationFromAPI() # will return 'inside' or 'outside'
location = Location(location_str) # make Enum
human = Human(location) # create human with basic location
if human.location == Location.Inside:
where_inside = whereInside(human)
human.location = where_inside # update location to be more precise
The problem is when I create the Human object I only know of a basic location, as in 'inside' or 'outside'. Only after that can I update the location to be more precise.
You can accomplish this by embedding an enum.Enum inside another like so: (just watch out for names conflicting)
from enum import Enum
class _Inside(Enum):
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Location(Enum):
Outside = 'outside'
Inside = _Inside
print(Location.Inside.value.Downstairs.value)
downstairs
it may be a bit late and the one who asked the question is no longer necessary, but I leave it here in case someone wants to take a look at it, and even if it has already been validated as one, although the same comment that it is not completely complete .
But I have been thinking about it and in the end I have solved it by looking at the same documentation XD.
You cannot extend classes of Enums, but you can extend methods, I have followed this way and the only thing I have done has been to override the new and init methods, the use case can be modified, this is only to nest enumerators.
from enum import Enum
class SuperNestedEnum(Enum):
def __new__(cls, *args):
obj = object.__new__(cls)
value = None
# Normal Enumerator definition
if len(args) == 1:
value = args[0]
# Have a tuple of values, first de value and next the nested enum (I will set in __init__ method)
if len(args) == 2:
value = args[0]
if value:
obj._value_ = value
return obj
def __init__(self, name, nested=None):
# At this point you can set any attribute what you want
if nested:
# Check if is an Enumerator you can comment this if. if you want another object
if isinstance(nested, EnumMeta):
for enm in nested:
self.__setattr__(enm.name, enm)
class Homework(Enum):
Task = "5"
class Subjects(SuperNestedEnum):
Maths = "maths"
English = "english"
Physics = "nested", Homework
class School(SuperNestedEnum):
Name = "2"
Subjects = "subjects", Subjects
Ignore the use case because it doesn't make sense, it's just an example
>>> School.Name
<School.Name: '2'>
>>> School.Subjects
<School.Subjects: 'subjects'>
>>> School.Subjects.value
'subjects'
>>> School.Subjects.Maths
<Subjects.Maths: 'maths'>
>>> School.Subjects.Physics.value
'nested'
>>> School.Subjects.Physics.Task
<Homework.Task: '5'>
>>> School.Subjects.Physics.Task.value
'5'
If anyone has similar issues and just wants a simple solution for the topic without patching any functions or additional imports for enums containing strings, follow these steps:
Create the value enums, in your lower hierarchy, like:
class __private_enum1__(str, enum.Enum):
VAL11 = "abc"
VAL12 = "def"
class enum2(str, enum.Enum):
VAL21 = "123"
VAL22 = "456"
Create a base class (a container) for these enums. Where you can either import the enums classes or simply directly acccess the enums.
class myValues:
VAL11 = __private_enum1__.VAL11
VAL12 = __private_enum1__.VAL12
VALS2X = enum2
Then you can access your values by:
print(myValues.VAL11.value)
print(myValues.VAL2X.VAL21.value)
.value is not necessary here but it shows that you both access the string inside the enum for passing it to other functions but also the enum itself, which is pretty neat. So basically, first create the values, then the structure. That way you have a class but it provides you the basic functionality of enums and you can nest them as deep as you want to without further imports.

Categories

Resources