examples/usercmodule/cexample: Add more advanced native class.
This adds a separate `AdvancedTimer` class that demonstrates a few more advanced concepts usch as custom handlers for printing and attributes. Signed-off-by: Laurens Valk <laurens@pybricks.com>
This commit is contained in:
parent
7fe8f030ee
commit
19b1333cb1
@ -68,6 +68,87 @@ MP_DEFINE_CONST_OBJ_TYPE(
|
||||
locals_dict, &example_Timer_locals_dict
|
||||
);
|
||||
|
||||
// What follows is a *separate* class definition that demonstrates more
|
||||
// advanced techniques to implement other Python-like features, such as:
|
||||
//
|
||||
// - A custom representation for __repr__ and __str__.
|
||||
// - Custom attribute handling to create a read/write "property".
|
||||
//
|
||||
// It re-uses some of the elements of the basic Timer class. This is allowed
|
||||
// because they both use example_Timer_obj_t as the instance structure.
|
||||
|
||||
// Handles AdvancedTimer.__repr__, AdvancedTimer.__str__.
|
||||
static void example_AdvancedTimer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
|
||||
// Get the elapsed time. In this case, it's also a demonstration of calling
|
||||
// the equivalent of self.time() in the C API. This is not usually very
|
||||
// efficient, but it can sometimes be useful.
|
||||
mp_uint_t elapsed = mp_obj_get_int(example_Timer_time(self_in));
|
||||
|
||||
// We'll make all representations print at least the class name.
|
||||
mp_printf(print, "%q()", MP_QSTR_AdvancedTimer);
|
||||
|
||||
// Decide what else to print based on print kind.
|
||||
if (kind == PRINT_STR) {
|
||||
// For __str__, let's attempt to make it more readable.
|
||||
mp_printf(print, " # created %d seconds ago", elapsed / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles AdvancedTimer.seconds for reading and writing.
|
||||
static void example_AdvancedTimer_attribute_handler(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
|
||||
// In this example, we only want to handle the .seconds attribute in a
|
||||
// special way.
|
||||
if (attr != MP_QSTR_seconds) {
|
||||
// Attribute not found, continue lookup in locals dict. This way,
|
||||
// methods like .time() will be handled normally.
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get reference to AdvancedTimer instance.
|
||||
example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// Check if this is a read operation.
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
// It's read, so "return" elapsed seconds by storing it in dest[0].
|
||||
mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time;
|
||||
dest[0] = mp_obj_new_int_from_uint(elapsed / 1000);
|
||||
return;
|
||||
}
|
||||
// Check if this is a delete or store operation.
|
||||
else if (dest[0] == MP_OBJ_SENTINEL) {
|
||||
// It's delete or store. Now check which one.
|
||||
if (dest[1] == MP_OBJ_NULL) {
|
||||
// It's delete. But in this example we don't want to allow it
|
||||
// so we just return.
|
||||
return;
|
||||
} else {
|
||||
// It's write. First, get the value that the user is trying to set.
|
||||
mp_uint_t desired_ms = mp_obj_get_int(dest[1]) * 1000;
|
||||
// Use it to update the start time. This way, the next read will
|
||||
// report the updated time.
|
||||
self->start_time = mp_hal_ticks_ms() - desired_ms;
|
||||
|
||||
// Indicate successful store.
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This defines the type(AdvancedTimer) object.
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
example_type_AdvancedTimer,
|
||||
MP_QSTR_AdvancedTimer,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
attr, example_AdvancedTimer_attribute_handler,
|
||||
print, example_AdvancedTimer_print,
|
||||
make_new, example_Timer_make_new,
|
||||
locals_dict, &example_Timer_locals_dict
|
||||
);
|
||||
|
||||
// Define all attributes of the module.
|
||||
// Table entries are key/value pairs of the attribute name (a string)
|
||||
// and the MicroPython object reference.
|
||||
@ -77,6 +158,7 @@ static const mp_rom_map_elem_t example_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
|
||||
|
||||
|
||||
@ -22,3 +22,20 @@ t_end = timer.time()
|
||||
print(timer)
|
||||
print(0 <= t_start <= TOLERANCE_MS)
|
||||
print(SLEEP_MS - TOLERANCE_MS <= t_end <= SLEEP_MS + TOLERANCE_MS)
|
||||
|
||||
advanced_timer = cexample.AdvancedTimer()
|
||||
|
||||
time.sleep_ms(100)
|
||||
|
||||
print(repr(advanced_timer))
|
||||
print(str(advanced_timer))
|
||||
|
||||
print(advanced_timer.seconds)
|
||||
advanced_timer.seconds = 123
|
||||
print(advanced_timer.seconds)
|
||||
print(advanced_timer.time() < 123000 + TOLERANCE_MS)
|
||||
|
||||
try:
|
||||
advanced_timer.seconds = "bad input"
|
||||
except TypeError:
|
||||
print("TypeError")
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
<Timer>
|
||||
True
|
||||
True
|
||||
AdvancedTimer()
|
||||
AdvancedTimer() # created 0 seconds ago
|
||||
0
|
||||
123
|
||||
True
|
||||
TypeError
|
||||
|
||||
@ -12,5 +12,6 @@ print(cexample.__name__)
|
||||
d = dir(cexample)
|
||||
d.index("add_ints")
|
||||
d.index("Timer")
|
||||
d.index("AdvancedTimer")
|
||||
|
||||
print(cexample.add_ints(1, 3))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user