Hello everybody.
I am writing a very simple EDL importer for my company. The idea
is to append each video edit, mount the audios together and give an updated
montage of the movie under production.
All the stuff related to audio and video has been easy, and I must say
the API was easy to use. Sadly I reached a real obstacle trying to
adjust the user interface, and I need the help from this comunity.
After the loading of the EDL, frame start and end has varied and i want to
adjust the “Video Editing” screen timeline to frame the entire duration
of the movie. I want to send a bpy.ops.time.view_all() to that timeline area.
Each time I send the bpy.ops.time.view_all() message i got the infamous
wrong context error from poll functions. I’ve tried to fake a context using
this approach:
inner_context = bpy.context.copy()
inner_context['area'] = area_timeline
inner_context['region'] = region_window_timeline
inner_context['screen'] = video_screen
inner_context['space_data'] = time_space
bpy.ops.time.view_all('EXEC_DEFAULT', inner_context)
Please, notice this code is incomplete, I’m trying to show the approximation
and not a complete solution.
After some time i decided to write a new operator that only dumps
the received context:
# -*- coding: utf-8 -*-
bl_addon_info = {
'name': 'Debug instruments',
'author': 'the_pinata',
'version': (0, 1),
'blender': (2, 5, 3),
'api': 32411,
'description': 'Prints the execution context',
'warning': '',
'wiki_url': '',
'tracker_url': '',
'category': 'Debug'}
import bpy
class DebugDumpContext(bpy.types.Operator) :
bl_idname = "debug.dump_context"
bl_label = "Dump caller context"
def invoke(self, context, event) :
my_c = context.copy()
l_k = list (my_c.keys())
l_k.sort()
for k in l_k:
print ( k + ':' + str(my_c[k]))
return {"FINISHED"}
def register():
pass
def unregister():
pass
if __name__ == "__main__":
register()
And modified the interface to make it a menu option in timeline view
menu. By using this operator i was able to verify that i was sending the
correct objects in my custom context.
After that I dig into the sources looking the path is following the poll
function at view_all operators for timeline spaces and found this:
** Sources of view_all operator:
$SOURCES/source/blender/editors/space_time/time_ops.c
line 164
void TIME_OT_view_all (wmOperatorType *ot)
{
/* identifiers */
ot->name= "View All";
ot->idname= "TIME_OT_view_all";
ot->description= "Show the entire playable frame range";
/* api callbacks */
ot->exec= time_view_all_exec;
ot->poll= ED_operator_timeline_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
** Poll callback function
$SOURCES/source/blender/editors/screen/screen_ops.c
line 163
int ED_operator_timeline_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_TIME);
}
$SOURCES/source/blender/editors/screen/screen_ops.c
line 149
static int ed_spacetype_test(bContext *C, int type)
{
if(ED_operator_areaactive(C)) {
SpaceLink *sl= (SpaceLink *)CTX_wm_space_data(C);
return sl && (sl->spacetype == type);
}
return 0;
}
$SOURCES/source/blender/editors/screen/screen_ops.c
line 116
int ED_operator_areaactive(bContext *C)
{
if(CTX_wm_window(C)==NULL) return 0;
if(CTX_wm_screen(C)==NULL) return 0;
if(CTX_wm_area(C)==NULL) return 0;
return 1;
}
$SOURCE/source/blender/blenkernel/intern/context.c
line 371
wmWindow *CTX_wm_window(const bContext *C)
{
return C->wm.window;
}
line 203
bScreen *CTX_wm_screen(const bContext *C)
{
return C->wm.screen;
}
line 208
ScrArea *CTX_wm_area(const bContext *C)
{
return C->wm.area;
}
line 213
SpaceLink *CTX_wm_space_data(const bContext *C)
{
return (C->wm.area)? C->wm.area->spacedata.first: NULL;
}
I am not an expert in the kernel sources, but assuming that the coders
try to keep the same name and meanings along the APIs, it looks like the
poll function at view_all is accessing the window manager.
And I’m completely stuck now. Reading the blender architecture papers, I get the idea that operators can be called using custom contexts. I looked
around without any luck for examples of custom context, specially for
UI related operations.
Ok, I need your help:
Am I doing something awfully wrong?
Maybe I haven’t understand the principles of design of the API?
There is a way to send this ops to UI zones different than the active one?
Thank you very much, and sorry for the length of this, I wanted to give the
complete overview of what i done till date.
btw: I’m using the 2.55 version of Blender.