I am building a QWidget in PySide, and running into an issue when trying to share data between pages.
To summarize I utilize user inputs from earlier pages to construct a list of custom objects, which I need to share with the following page.
At the beginning of my code I construct a custom object, with an attribute called .name (among other attributes)
class MyCustomClass():
def __init__(self, name, other_attributes)
self.name = name
...set other attributes
In my QWizard I open a file and make a list of names to match with another list of MyCustomClass objects. I then display the names alongside the matched name of the corresponding MyCustomClass object and prompt the user to confirm (or change), before moving to the next page.
Each match is stored as a tuple(name, MyCustomClass) and added to a list. I then wish to read this list from the next page in order to perform more operations. I'm trying to use .registerField, but I'm unsure of how to properly do so. My attempt is below.
First I make a QWizardPage, perform some code and then construct my matches. I made a function to return the value and used this for the .registerField
class ConfirmMatches(QWizardPage):
def __init__(self):
...
def initializePage(self):
# Code to make display and operations and make list of matches
...
self.matches = matches
self.registerField("matches", self, "get_matches")
def get_matches(self):
return self.matches
Then from my next page, I try to call the field, but I only return a None object.
class NextPage(QWizardPage):
def __init__(self):
...
def initializePage(self):
# Get relevant fields from past pages
past_matches = self.field("matches")
type(past_matches) is None, even though when I print self.matches in the previous page it clearly displays them all.
What am I doing wrong with the registerField?
Is there an easier way to share this type of data between pages?
I actually solved it myself. I was on the right track, just missing a few things, but I'll catalog here for anyone else with similar problems.
Like I said I have a list of matched objects, where each match is a list of a name, and the object that was found to correspond to that name, i.e. match = [name, MyCustomClass]
class ConfirmMatches(QWizardPage):
# Function to change list
def setList(self, new_list):
self.list_val = new_list
if self.list_val != []:
self.list_changed.emit()
# Function to return list
def readList(self):
return self.list_val
def __init__(self):
self.list_val = [] # Create initial value
# Code to initialize displays/buttons, and generate matches
...
# Here I assign the matches I made "matches", to the QProperty "match_list"
self.setList(matches)
# Then register field here.
# Instead of the read function, I call the object itself (not sure why, but it works)
self.registerField("registered_list", self, "match_list")
# Define "match_list" as a QProperty with read and write functions, and a signal (not used)
match_list = Property(list, readList, setList)
listChanged = Signal()
I made the list a QProperty and wrote the Read and Write functions, as well as a Signal (not used). Then, when registering the field, instead of putting the Read function (readList), I put the QProperty itself (match_list). Not sure why it works, but this conceivable could be used to register other custom objects.
If you want to explicitly set the value of the field matches in your ConfirmMatches page, you would need to do one of a few things:
Make an explicit call to self.setField any time your matches change.
Emit a signal every time your matches are changed after registering the property
Store your matches in one of the standard Qt inputs, like QLineEdit, and use that widget in the registerField call.
If you check the docs for QWizardPage.registerField, what it does is register to grab the named property of the passed in widget when the widget's signal is emitted. The way your code is now, you would need to add a signal to your ConfirmMatches page that would be emitted whenever your matches variable changes. Otherwise, your page doesn't know when the field should be updated.
Related
Suppose I define "Class original:" and create a class attribute "one = 4." Then I create an instance of the class "First = original()." My understanding is that First now contains a pointer to original and "First.one" will return "4." However, suppose I create "Second = original()" and then set "Second.one = 5." What exactly happens in memory? Does a new copy of Class original get created with a class attribute of 5?
I've created a Class original with class attribute one. I then created two instances of this class (First and Second) and verified that id(First.one) and id(Second.one) are pointing to the same place. They both return the same address. However, when I created Third=original() and set Third.one = 5 and then check id(Thrid.one) it appears to be pointing somewhere else. Where is it pointing and what happened? When I check original.one it still returns "4" so obviously the original object is not being modified. Thanks.
It appears you are asking about a piece of code similar to this:
class Original:
def __init__(self, n):
self.one = n
first = Original(4)
second = Original(4)
third = Original(5)
print(id(first.one))
# 140570468047360
print(id(second.one))
# 140570468047360
print(id(third.one))
# 140570468047336
Suppose I define "Class original:" and create a class attribute "one = 4." Then I create an instance of the class "First = original()." My understanding is that First now contains a pointer to original
No. The variable references the instance you created, not the class. If it referenced the class, there would be no way for you to get at the instance you just created.
The instance will, somewhere in its object header, of course contain a pointer to its class. Without that pointer, method lookup wouldn't be possible, since you wouldn't be able to find the class from the instance.
and "First.one" will return "4."
Yes. The attribute one of first contains a pointer to the object 4 (which is an instance of the class int).
[Note that technically, some Python implementations will perform an optimization and actually store the object 4 directly in the attribute instead of a pointer to the object. But that is an internal implementation detail.]
However, suppose I create "Second = original()" and then set "Second.one = 5." What exactly happens in memory? Does a new copy of Class original get created with a class attribute of 5?
No. Why would you need a separate copy of the class? The methods are still the same for both instances. In fact, that is precisely the reason why methods in Python take the instance as their first argument! That way, there need only be one method. (This is actually the same in every OO language, except that in most other languages, this argument is "invisible" and only accessible using a special keyword like self in Ruby, Smalltalk, Self, and Newspeak or this in Java, C#, and Scala.)
I've created a Class original with class attribute one. I then created two instances of this class (First and Second) and verified that id(First.one) and id(Second.one) are pointing to the same place. They both return the same address. However, when I created Third=original() and set Third.one = 5 and then check id(Thrid.one) it appears to be pointing somewhere else.
It is not quite clear to me what your question is here. first.one and second.one both point to 4, so they both point to the same ID since they both point to the same object. third.one points to 5, which is obviously a different object from 4, so naturally, it has a different ID.
It is, in fact, one of the requirement of IDs that different objects that exist at the same time must have different IDs.
Where is it pointing and what happened?
Again, it is not quite clear what you are asking.
It is pointing at 5, and nothing happened.
When I check original.one it still returns "4" so obviously the original object is not being modified.
Indeed, it isn't. Why would it be?
This is the property definition I use to lazy load the element attribute of my Element class, borrowed from This post on Python lazy loading.
#property
def element(self):
if not hasattr(self, "_element"):
self._element = self.wait.wait_for_element_to_be_visible(self.locator)
return self._element
It looks for the attribute _element if it doesn't find it then it goes and looks for it. If the attribute is there then it just returns the attribute without looking for it as its already been loaded in.
I've changed the if to be:
if self._element is None:
self._element = self.wait.wait_for_element_to_be_visible(self.locator)
return self._element
Is it more pythonic to see if the attribute exists, or to set _element to None in the __init__ method and then check for that instead?
Doing it the second way also seems better for debugging purposes in IntelliJ as there seems to be a strange loading issue that freezes the tests if I expand my Element object and it starts introspection to display attributes and their values.
My definition of a pythonic solution is the one that's most likely to help a future maintainer understand what's going on when they come across this code (most times it's the same person who wrote it but long after they have it in their memory). Here's some things I might ask myself if I was coming in cold.
1. hasattr
self._element is not in __init__, is this because it's set somewhere else?
Every access checks for the attribute, does that mean it's expecting it to be deleted at some point so it can refresh? Why else is it checked every time?
hasattr will search up the chain of MRO, are we actually looking for something?
2. is None
Found self._element in __init__ being set to None. Am I allowed to set it to None again to get fresh values? The _ in _element is hinting no.
2a. is _Missing
If None is a valid result, use a sentinel instead.
class _Missing:
pass
def __init__():
self._element = _Missing
3. One shot descriptor
Property produces what is known as a data descriptor. It will ignore attributes of the instance and go straight to the classes dictionary (otherwise __set__ won't be able to do it's thing). Here we make our own non-data descriptor (only defines __get__), which won't skip the instances dictionary. This means the instances attribute can be set. Deleting the instance attribute will result in a refreshed value on the next invocation.
class LazyLoadElement:
def __get__(self, instance, owner):
instance.element = instance.wait.wait_for_element_to_be_visible(instance.locator)
return instance.element
class MyClass:
element = LazyLoadElement()
What's the intention of the LazyLoadElement? Name says it all
Can it refresh to another value? Nope, it's set after the first call and removes itself.
Pythonic is a little arm wavy because it needs to make assumptions about who is reading the code at a future date. Out of the 2 original options, number 2 seems most likely to get maintainers up to speed. 3 is my fav though, mainly because once it's loaded there's no more function calls.
I'm having trouble getting my head around assigning a function to a variable when the function uses arguments. The arguments appear to be required but no matter what arguments I enter it doesn't work.
The scenario is that I'm creating my first GUI which has been designed in QT Designer. I need the checkbox to be ticked before the accept button allows the user to continue.
Currently this is coded to let me know if ticking the checkbox returns anything (which is does) however I don't know how to pass that result onto the next function 'accept_btn'. I thought the easiest way would be to create a variable however it requires positional arguments and that's where I'm stuck.
My code:
class MainWindow(QtWidgets.QMainWindow, Deleter_Main.Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
self.ConfirmBox.stateChanged.connect(self.confirm_box)
self.Acceptbtn.clicked.connect(self.accept_btn)
def confirm_box(self, state):
if self.ConfirmBox.isChecked():
print("checked")
else:
print("not checked")
checked2 = confirm_box(self, state)
def accept_btn(self):
if checked2 == True:
print("clicked")
else:
print("not clicked")
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
The code gets stuck on 'checked2' with the error:
NameError: name 'self' is not defined
I thought there might be other solutions for running this all within one function but I can't seem to find a way whilst the below is required.
self.ConfirmBox.stateChanged.connect(self.confirm_box)
Would extra appreciate if anyone could help me understand exactly why I need the 'self' argument in the function and variable.
Thanks in advance,
If you just need to enable a button when the checkbox is checked, it can be easily done within the signal connection:
self.ConfirmBox.toggled.connect(self.Acceptbtn.setEnabled)
QWidget.setEnabled requires a bool argument, which is the argument type passed on by the toggled signal, so the connection is very simple in this case.
Apart from this, there are some mistakes in your understanding of classes in Python: it seems like you are thinking in a "procedural" way, which doesn't work well with general PyQt implementations and common python usage, unless you really need some processing to be done when the class is created, for example to define some class attributes or manipulate the way some methods behave. But, even in this case, they will be class attributes, which will be inherited by every new instance.
The line checked2 = confirm_box(self, state) will obviously give you an error, since you are defining checked2 as a class atribute. This means that its value will be processed and assigned when the class is being created: at this point, the instance of the class does not exist yet, Python just executes the code that is not part of the methods until it reaches the end of the class definition (its primary indentation). When it reaches the checked2 line, it will try to call the confirm_box method, but the arguments "self" and "state" do not exist yet, as they have not been defined in the class attributes, hence the NameError exception.
Conceptually, what you have done is something similar to this:
class SomeObject(object):
print(something)
This wouldn't make any sense, since there is no "something" defined anywhere.
self is a python convention used for class methods: it is a keyword commonly used to refer to the instance of a class, you could actually use any valid python keyword at all.
The first argument of any class method is always the reference to the class instance, the only exceptions are classmethod and staticmethod decorators, but that's another story. When you call a method of an instanciated class, the instance object is automatically bound to the first argument of the called method: the self is the instance itself.
For example, you could create a class like this:
class SomeObject(object):
def __init__(Me):
Me.someValue = 0
def setSomeValue(Myself, value):
Myself.someValue = value
def multiplySomeValue(I, multi):
I.setSomeValue(I.someValue * multi)
return I.someValue
But that would be a bit confusing...
Is there any way to know the context in which an object is instantiated? So far I've been searching and tried inspect module (currentcontext) with poor results.
For example
class Item:
pass
class BagOfItems:
def __init__(self):
item_1 = Item()
item_2 = Item()
item_3 = Item()
I'd want to raise an exception in the instantiation of item_3 (because its outside a BagOfItems), while not doing so in item_1 and item_2. I dont know if a metaclass could be a solution to this, since the problem occurs at instantiation not at declaration.
The holder class (BagOfItems) can't implement the check because when Item intantiation happens outside it there would be no check.
When you instantiate an object with something like Item(), you are basically doing type(Item).__call__(), which will call Item.__new__() and Item.__init__() at some point in the calling sequence. That means that if you browse up the sequence of calls that led to Item.__init__(), you will eventually find code that does not live in Item or in type(Item). Your requirement is that the first such "context" up the stack belong to BagOfItem somehow.
In the general case, you can not determine the class that contains the method responsible for a stack frame1. However, if you make your requirement that you can only instantiate in a class method, you are no longer working with the "general case". The first argument to a method is always an instance of the class. We can therefore move up the stack trace until we find a method call whose first argument is neither an instance of Item nor a subclass of type(Item). If the frame has arguments (i.e., it is not a module or class body), and the first argument is an instance of BagOfItems, proceed. Otherwise, raise an error.
Keep in mind that the non-obvious calls like type(Item).__call__() may not appear in the stack trace at all. I just want to be prepared for them.
The check can be written something like this:
import inspect
def check_context(base, restriction):
it = iter(inspect.stack())
next(it) # Skip this function, jump to caller
for f in it:
args = inspect.getargvalues(f.frame)
self = args.locals[args.args[0]] if args.args else None
# Skip the instantiating calling stack
if self is not None and isinstance(self, (base, type(base))):
continue
if self is None or not isinstance(self, restriction):
raise ValueError('Attempting to instantiate {} outside of {}'.format(base.__name__, restriction.__name__))
break
You can then embed it in Item.__init__:
class Item:
def __init__(self):
check_context(Item, BagOfItems)
print('Made an item')
class BagOfItems:
def __init__(self):
self.items = [Item(), Item()]
boi = BagOfItems()
i = Item()
The result will be:
Made an item
Made an item
Traceback (most recent call last):
...
ValueError: Attempting to instantiate Item outside of BagOfItems
Caveats
All this prevents you from calling methods of one class outside the methods of another class. It will not work properly in a staticmethod or classmethod, or in the module scope. You could probably work around that if you had the motivation. I have already learned more about introspection and stack tracing than I wanted to, so I will call it a day. This should be enough to get you started, or better yet, show you why you should not continue down this path.
The functions used here might be CPython-specific. I really don't know enough about inspection to be able to tell for sure. I did try to stay away from the CPython-specific features as much as I could based on the docs.
References
1. Python: How to retrieve class information from a 'frame' object?
2. How to get value of arguments passed to functions on the stack?
3. Check if a function is a method of some object
4. Get class that defined method
5. Python docs: inspect.getargvalues
6. Python docs: inspect.stack
I am trying to create a nested class to perform sum or multiplication of the arguments passed in each subclass.
The below example helps me perform action within the class, however I am unable to find any documentation which would help me with inheriting the attributes from the Parent Class to child.
Recently I came across an article which highlights "nested classes can't access any members of their outer classes at compile-time.". Is there a better way to pass the values between Classes? I tried using global variables, but would like to avoid setting many global variables while I scale this logic to extract my entire datacenter's inventory, perform some calculations and again pass to another class.
class Class1:
firstnumber=0
def __init__(self,arg):
self.firstnumber=arg
class Class2:
def __init__(self,arg):
self.secondnumber=arg
def sumit(self):
return Class1.firstnumber+Class1.Class2.secondnumber
print(Class1(5).firstnumber)
print(Class1(6).Class2(4).secondnumber)
print(Class1(4).Class2(10).sumit())
I would like to perform calculations with
Class1(variable1).Class2(variable2).Class3(variable3).sum() or
Class1(variable1).Class2(variable2).Class3(variable3).multiple() and eventually be able to do following
Datacenter('DC1').GetServer('ServerName').GetStorageCapacity('NFS').Used()
Datacenter('DC1').GetServer('ServerName').GetStorageCapacity('NFS').Free()
http://momentaryfascinations.com/programming/bound.inner.classes.for.python.html
i may be wrong but to my understanding anything you put in between the class() and the init statement is permanent and unchangable. you shouldn't need to create seperate classes for each number. create different instances of the same class.
class numbers:
def __init__(self,arg):
self.arg = arg
c1 = numbers(3)
c2 = numbers(5)
i don't know how you would add the arg variables together maybe someone else can fill in what i'm missing.