Inherit Magic Class¶
Class inheritance is fundamental in object-oriented languages. It makes class definition much clearer in many cases.
Magic-class is designed to make GUI structures connected with the structure of class itself, so how to deal with class inheritance is not a well-defined feature by default. Here are some points that you have to keep in mind before making abstract classes.
Warning
In the current version (0.5.19), integer indexing is not safe if a magic class
inherits some other classes. To access chind widgets, use str
(such as
ui["X"]
) instead of int
(such as ui[1]
)
The Order of Widget¶
First, let's consider following example.
from magicclass import magicclass
class Base:
def common_function(self):
"""Do some common things."""
@magicclass
class Main(Base):
def main_function(self):
"""Main one."""
ui = Main()
ui.show()
Note
Do NOT decorate Base
class with @magicclass
, otherwise constructor will
raise TypeError
. You only have to decorate the final concrete classes.
It is obvious that created GUI will have two buttons named "common function" and "main function", but it is not clear which is upper and which is lower.
In magic-class, methods defined in base classes will appear upper than those in subclasses. In the case of example, GUI will look like:
[ common function ]
[ main function ]
Field Objects in the Base Class¶
You may want to add widgets using MagicField
(see Use Fields in magic-class). MagicField
behaves similarly as methods. In the following example,
from magicclass import magicclass, field
class Base:
x = field(int)
@magicclass
class Main(Base):
y = field(str)
Two widgets, x
and y
will be packed in the Main
GUI, in order x
, y
.
However, if you want to use Bound
to bind parameter to method or connect callback
function to a field, you must re-define fields in the subclasses.
1. Bind methods¶
This will not work
from magicclass import magicclass, field, Bound
class Base:
x = field(int)
@magicclass
class Main(Base):
def func(self, value: Bound(x)):
"""Do something"""
This will work
from magicclass import magicclass, field, Bound
class Base:
x = field(int)
@magicclass
class Main(Base):
x = field(int)
def func(self, value: Bound(x)):
"""Do something"""
2. Define Callbacks¶
This will not work
from magicclass import magicclass, field
class Base:
x = field(int)
@magicclass
class Main(Base):
@x.connect
def _callback(self):
"""Do something"""
This will work
from magicclass import magicclass, field
class Base:
x = field(int)
@magicclass
class Main(Base):
x = field(int)
@x.connect
def _callback(self):
"""Do something"""
Note
These caveats are quite natural considering the concept of scope in Python. When you define a variable in a class, it is not available from other classes until class definition finishes.
class A:
x = 1
class B(A):
print(x)
NameError: name 'x' is not defined
This is because class inheritance has not finished yet in the line print(x)
.
Nesting Magic Classes¶
Nesting magic classes (see Nest magic-classes) is useful for designing layout of widgets. You don't have to worry about inheriting base class with a nested magic class.
class Base(MagicTemplate):
# All of these widgets and their layout will be inherited to subclasses
result = field(str)
@magicclass
class X(MagicTemplate):
def func(self): ...
@X.wraps
def func(self):
self.result.value = self.__class__.__name__
@magicclass
class A(Base):
pass
Predefinition of Methods and Fields¶
Most of the time you want to inherit a class is when you want to prepare a template of multipule GUIs. As mentioned above, methods and fields that are defined in the base class will packed before those in the subclasses. This is not desirable if you want the subclasses share same header and footer and make the middle widgets variable.
Just like using wraps
method (see Call Parent Methods from its Child), the pre-definition strategy is
also useful here. First arrange all the widgets in the base class, and specifically
define the real widgets in the subclasses.
class Base(MagicTemplate):
header = field("this is header", default_factory="Label")
x = ... # pre-definition
footer = field("this is footer", default_factory="Label")
@magicclass
class A(Base):
def x(self):
"""Do something"""
@magicclass
class B(Base):
x = field(int)
