Editing: The idea is to have a “canvas” where users place data flow functions (any python function actually), connect them together,
Here was the question I posed to Dan:
Imagine we have the canvas:
foo
/ \
bar baz
\ /
zoo
We want to be able to add and remove functions from the canvas (For example, delete baz). This brings up the question of what the “primary” model is for the dependency graph. Is it the Block? If so, is there an easy way to remove a sub-block from a Block? At first blush, this seemed hairy, but it seems a little strange to have a different model keep track of the same stuff and then generate a Block for execution every time that it changes...
So,
- Would it be hard to remove a sub-block from a Block instance?
- Is that the correct model, or should we have another manage to structure, and just use the block for execution?
Dan informed me that blocks are designed so that their sub-blocks can be manipulated. For example, you can do the following:
b1 = Block(“a=foo(b)”) b2 = Block(“c=bar(b)”)
main_block = Block() main_block.sub_blocks = [b1,b2] function_graph(main_block) {‘foo’: [], ‘bar’: [‘foo’]}
Similarly, deleting a block from the sub_blocks list removes the code from the main_block. I believe this means this behavior makes Blocks suitable for the model that can sit under our “BlockCanvas” for displaying/creating/manipulating a Block in a visual programming window.
The problem I haven’t analyzed yet is, how do we match blocks with code snippets that generated them. Blocks can’t currently generate their code (though it shouldn’t be hard to add according to Dan – and easier still in 2.5). They do however have a UUID that hopefully will allow us to do what we need.
We want to be able to go back and forth between edited code and blocks. Currently, blocks only track the dependencies in code. However, it is important for us to also know the function that will execute for each block in code. This is so that, when people edit the block diagram, we can bring up information about the code that will execute form that block.
I think we should also support people just editing code, but without having the actual functions imported or even known. For example, the following code can be analyzed as a block:
c = foo(a,b) d = foo(c,a)
and it should be valid for someone to enter it in the code window. I’m not sure what we do about executing this. Perhaps we put a ‘!’ icon on the function block. Then, when people click on it, it’ll show the available functions that best match the name for them to choose from.
More thought on this...
The Display() class now ties the code and block together accurately. Editing either one should produce a consistent view in the other. However, all is far from perfect. Whenever you edit one of the views, it generates a completely new set of code for the other view. This doesn’t result in anything incorrect as far as the views go, but it does result in extra layouts, and it may result in us re-running the entire workflow every time we change any bit of code. That would be bad.
So, the desire is to figure out how to do updates on a much more granular level. One way is to tie each line of the code to the a sub_block in the graph. Another is to check each line in the code against the code that would be generated by a particular block in the graph. This actually sounds fairly promising. The question of what to do with identical blocks is interesting. This is sorta a useless duplication, so we may just mark one of the blocks as redundant or get rid of it...
If a user adds comments to the code view and then adds a new function block using the graph canvas, all the comments are lost. This is because a new set of code is generated from the Block every time it is changed. The code generated from the block is built from an AST which doesn’t contain the comment information. It also doesn’t contain blank line information.
Note that strings do make it into the AST, even if they don’t do anything, so one option is to use strings as comments.
Containers resize to fit the bounds of their components. They also offset contained components to have their position relative to themselves.