resizable QtDialog

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
AtD
Posts: 50
Joined: Mon Mar 18, 2019 7:14 am

Re: resizable QtDialog

Post by AtD »

I agree, it seems strange at the start - but learning it will give you so much in terms of better code. Like a year ago I was in the same spot trying to understand classes in Python. I also read the examples you mentioned ... but only sitting down myself and writing my own class, initializing it, setting and getting values from "outside" made it clear to me how it works.
Also, coincidently, I worked through the FreeCAD CfdOF Workbench source code which pushed my understanding of it even further.
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

Re: resizable QtDialog

Post by DeepSOIC »

My opinion on instance attributes.

Code: Select all

class Person:
    className = "Person"

    def __init__(self, name):
        self.name = name
In this snippet, className is a class attribute. It is accessible as Person.className and as type(aperson).className. But also as aperson.className if there is no instance attribute named "className".

name is an instance attribute, which is only accessible as aperson.name (aka self.name, as self is just an instance of Person.

Just creating instance attributes in code is of course the way it's supposed to be done. But it's very tricky to figure out, what attributes are being used on instances of the class, and what are they used for; for that, one has to read and digest pretty much the whole implementation of the class.

Initializing all attributes in __init__ code is one step towards eliminating this problem. However, since __init__ can contain quite a lot of other code, it can yet again become painful to pick out all attributes and their meanings.

Later I found out, that I can use class attributes as a convenient place to list all the instance attributes. It gives quite some advantages: all attributes are listed in one and concise place; it is natural place to put them in c++; it's natural to put comments on what they are for; enables autocompletion. But one has to remember one big GOTCHA: you are defining class attribute, not instance attribute.

The reason why it even can be done, because when you write aperson.className = "something", python doesn't overwrite class attribute. Instead, it creates an instance attribute with the same name. And later, when you write if self.name == "", it will read out the instance attribute.

The GOTCHA is when you use mutable types. For example:

Code: Select all

class Person:
    props = []

    def add_prop(self, prop):
        self.props.append(prop) #actually changes the list associated with the class, i.e. affects all instances of Person
Overwriting the attribute makes the attribute to effectively become an instance variable. But mutating it (.append call) does not.

So, I settled for the following pattern for myself:

Code: Select all

class TempoVis(object):
    '''TempoVis - helper object to save visibilities of objects before doing
    ...'''

    document = None
    stack = None # reference to stack this TV is in

    data = None # dict. key = ("class_id","key"), value = instance of SceneDetail
    data_requested = None #same as data, but stores (wanted) values passed to modify()
    
    state = S_EMPTY
    
    tag = '' #stores any user-defined string for identification purposes
These are all instance variables there. For immutable values, I put them straight in, because there's effectively no difference in behavior. For mutables, I use None (instead of, for example, empty dict).
User avatar
Zolko
Veteran
Posts: 2213
Joined: Mon Dec 17, 2018 10:02 am

Re: resizable QtDialog

Post by Zolko »

DeepSOIC wrote: Mon Jan 27, 2020 2:56 pm Later I found out, that I can use class attributes as a convenient place to list all the instance attributes.
In my specific case, I draw a UI window to create a variable, and each time I call it the variable name should be empty. So the field should belong to the class but it's content to the instance. Each time I call the function the field should be empty .... and if I put everything as "self" this is not the case, each time the function "remembers" the previous variable's name, and I have to set it actively to empty. But when I don't put "self" everywhere then either it doesn't work at all, or some widgets are missing, or are drawn a twice the second time I call the function ...

What to put in __init__, what as "self", what as local variable that doesn't get destroyed before used ... has no first-hand logic that would be explained somewhere.
try the Assembly4 workbench for FreCAD — tutorials here and here
User avatar
Zolko
Veteran
Posts: 2213
Joined: Mon Dec 17, 2018 10:02 am

Re: resizable QtDialog

Post by Zolko »

Zolko wrote: Mon Jan 27, 2020 3:18 pm What to put in __init__, what as "self", what as local variable that doesn't get destroyed before used ... has no first-hand logic that would be explained somewhere.
so here is what I found out:
  • for resizable dialog build everyhting with layouts. The layout then needs to be aplied to the main widget (the functi

    Code: Select all

    class animateVariable( QtGui.QDialog ):
        ...
        self.mainLayout = QtGui.QVBoxLayout(self)
        ...
        self.setLayout(self.mainLayout)
    
  • adding widgets will add them logically (left-to-right...)
  • to embed layouts, create a new layout, create a new widget, add widgets to the layout, apply the layout to the widget, add widget to the main layout: for example to have 2 buttons

    Code: Select all

    self.buttonLayout = QtGui.QHBoxLayout(self)
    self.buttonLayout.addWidget(self.CloseButton)
    self.buttonLayout.addStretch()
    self.buttonLayout.addWidget(self.RunButton)
    self.buttonRow = QtGui.QWidget(self)
    self.buttonRow.setLayout(self.buttonLayout)
    self.mainLayout.addWidget(self.buttonRow)
    
  • I define the widget in a function drawUI(), which I call at the __init__
  • if a variable needs initialised it's done in the Activated() function
  • everything is (self) and self.
This makes very basic dialogs but at least they're consistent.
try the Assembly4 workbench for FreCAD — tutorials here and here
Post Reply