55BlocklyBlockGenerators = dict () ## Blocks share a single namespace in Blockly
66_blockly_selected_block = None ## for triggering on_click_callback
77
8+ with javascript : BlocklyBlockInstances = {} ## block-uid : block instance
9+ _blockly_instances_uid = 0
10+
11+ with javascript :
12+ NEW_LINE = String .fromCharCode (10 )
13+
14+ def __blockinstance ( result , block_uid ):
15+ with javascript :
16+ BlocklyBlockInstances [ block_uid ].pythonjs_object = result
17+ return result
18+
819def initialize_blockly ( blockly_id = 'blocklyDiv' , toolbox_id = 'toolbox' , on_changed_callback = None ):
920 print 'initialize_blockly'
1021 if len ( BlocklyBlockGenerators .keys () ):
@@ -80,13 +91,14 @@ class BlocklyBlock:
8091 . a bare block can have no inputs or output, and no previous or next statement notches.
8192
8293 '''
83- def __init__ (self , name , title = None , color = None , category = None ):
84- self .setup ( name , title = title , color = color , category = None )
85-
86- def setup (self , name , title = None , color = None , category = None ):
87- if name in BlocklyBlockGenerators : raise TypeError
88- BlocklyBlockGenerators [ name ] = self
89- self .name = name
94+ def __init__ (self , block_name = None , title = None , color = None , category = None ):
95+ self .setup ( block_name = block_name , title = title , color = color , category = None )
96+
97+ def setup (self , block_name = None , title = None , color = None , category = None ):
98+ if block_name is None : block_name = '_generated_block' + str (len (BlocklyBlockGenerators .keys ()))
99+ if block_name in BlocklyBlockGenerators : raise TypeError
100+ BlocklyBlockGenerators [ block_name ] = self
101+ self .name = block_name
90102 self .title = title
91103 self .color = color
92104 self .category = category
@@ -98,24 +110,30 @@ def setup(self, name, title=None, color=None, category=None):
98110 self .stack_output = False
99111 self .is_statement = False
100112 self .external_function = None
113+ self .external_javascript_function = None
101114 self .on_click_callback = None
102115
116+ def javascript_callback (self , jsfunc ):
117+ print '@callback' , jsfunc
118+ self .set_external_function ( jsfunc .NAME , javascript = True )
119+ with javascript : arr = jsfunc .args_signature
120+ for i in range (arr .length ): self .add_input_value ( arr [i ] )
121+ return jsfunc
122+
103123 def callback (self , jsfunc ): ## decorator
104124 print '@callback' , jsfunc
105125 self .set_external_function ( jsfunc .NAME )
106126 with javascript : arr = jsfunc .args_signature
107- print 'arr' , arr
108- for i in range (arr .length ):
109- self .add_input_value ( arr [i ] )
127+ for i in range (arr .length ): self .add_input_value ( arr [i ] )
110128 return jsfunc
111129
112130 def set_on_click_callback (self , callback ):
113131 self .on_click_callback = callback
114132
115- def set_external_function (self , func_name ):
116- self .external_function = func_name
117- if not self .title :
118- self .title = func_name
133+ def set_external_function (self , func_name , javascript = False ):
134+ if javascript : self .external_javascript_function = func_name
135+ else : self .external_function = func_name
136+ if not self . title : self .title = func_name
119137
120138 def set_output (self , output ):
121139 if self .stack_input : raise TypeError
@@ -147,15 +165,17 @@ def add_input_value(self, name=None, type=None, title=None):
147165 {'name' :name , 'type' :type , 'title' :title }
148166 )
149167
150- def add_input_statement (self , name = None , title = None ):
168+ def add_input_statement (self , name = None , title = None , callback = None ):
151169 if name is None : raise TypeError
152170 if title is None : title = name
153171 self .input_statements .append (
154- {'name' :name , 'title' :title }
172+ {'name' :name , 'title' :title , 'callback' : callback }
155173 )
156174
157175
158176 def bind_block (self ):
177+ global _blockly_instances_uid
178+
159179 block_name = self .name
160180 stack_input = self .stack_input
161181 stack_output = self .stack_output
@@ -168,9 +188,23 @@ def bind_block(self):
168188 color = self .color
169189 input_values = self .input_values .js_object
170190 input_statements = self .input_statements .js_object
191+ external_function = self .external_function
192+ external_javascript_function = self .external_javascript_function
193+ is_statement = self .is_statement
171194
172195 with javascript :
173196 def init ():
197+ block_uid = _blockly_instances_uid
198+ _blockly_instances_uid += 1
199+ BlocklyBlockInstances [ block_uid ] = this
200+ this .uid = block_uid ## note that blockly has its own id called: this.id
201+
202+ this .__input_values = input_values
203+ this .__input_statements = input_statements
204+ this .__external_function = external_function
205+ this .__external_javascript_function = external_javascript_function
206+ this .__is_statement = is_statement
207+
174208 if color :
175209 this .setColour ( color )
176210
@@ -206,61 +240,92 @@ def init():
206240
207241 def bind_generator (self ):
208242 block_name = self .name
209- external_function = self .external_function
210- is_statement = self .is_statement
211- input_values = self .input_values .js_object
212- input_statements = self .input_statements .js_object
243+
244+ ## this is not safe with recursive functions? or this was due to the bad local scope bug below? (see input=null)
245+ #external_function = self.external_function
246+ #external_javascript_function = self.external_javascript_function
247+ #is_statement = self.is_statement
248+ #input_values = self.input_values.js_object
249+ #input_statements = self.input_statements.js_object
213250
214251 with javascript :
215252 def generator (block ):
216- code = ''
253+ input_values = block .__input_values
254+ input_statements = block .__input_statements
255+ external_function = block .__external_function
256+ external_javascript_function = block .__external_javascript_function
257+ is_statement = block .__is_statement
217258
259+ code = ''
260+ input = null ## TODO fix local scope generator in python_to_pythonjs.py - need to traverse whileloops - the bug pops up here because this is recursive?
218261 args = []
219262
220263 i = 0
221264 while i < input_values .length :
222265 input = input_values [i ][...]
223- a = Blockly .Python .valueToCode (block , input ['name' ], Blockly .Python .ORDER_NONE )
266+ if external_javascript_function :
267+ a = Blockly .JavaScript .valueToCode (block , input ['name' ], Blockly .JavaScript .ORDER_NONE )
268+ else :
269+ a = Blockly .Python .valueToCode (block , input ['name' ], Blockly .Python .ORDER_NONE )
224270 if a is not null : ## blockly API not correct? is says this will return null when nothing is connected.
225271 args .push ( a )
226272 i += 1
227- i = 0
228- while i < input_statements .length :
229- input = input_statements [i ][...]
230- a = Blockly .Python .statementToCode (block , input ['name' ])
231- if a != '' : ## blockly API would be better not returning an empty string here, in case we wanted to set an empty string
232- args .push ( a )
233- i += 1
273+
274+ ## input statements are used for dynamic updates
275+ if block .pythonjs_object :
276+ print 'dynamic blockly js-------'
277+ wrapper = block .pythonjs_object [...]
278+ print 'dynamic wrapper:' , wrapper
279+ i = 0
280+ while i < input_statements .length :
281+ input = input_statements [i ][...]
282+ #if Object.hasOwnProperty(wrapper, input['name']): ## this fails on THREE.Mesh.position, why?
283+ attr = wrapper [ input ['name' ] ]
284+ if attr :
285+ print 'dynamic wrapper has attr:' , input ['name' ]
286+ js = Blockly .JavaScript .statementToCode (block , input ['name' ])
287+ if input ['callback' ]:
288+ print 'DYNAMIC--calling:' , js
289+ input ['callback' ]( attr , eval (js ) )
290+ else :
291+ print 'WARN - input is missing callback' , input
292+ i += 1
234293
235294 if external_function :
236295 code += external_function + '(' + ',' .join (args ) + ')'
296+ elif external_javascript_function :
297+ ## TODO what about pure javascript functions?
298+ if is_statement and block .parentBlock_ : ## TODO request Blockly API change: "parentBlock_" to "parentBlock"
299+ code += external_javascript_function + '( [' + ',' .join (args ) + '] )' ## calling from js a pyjs function
300+
237301 else : ## this should be a simple series of statements?
238302 for a in args :
239303 code += a + ';'
240304
241305 if is_statement :
242- return code + ';' ## statements can directly return
306+ return code + NEW_LINE ## statements can directly return
243307 else :
308+ code = '__blockinstance( ' + code + ' ,' + block .uid + ')'
244309 return [ code , Blockly .Python .ORDER_NONE ] ## return Array
245310
246- print 'bindings block generator:' , block_name
247311 Blockly .Python [ block_name ] = generator
312+ Blockly .JavaScript [ block_name ] = generator
248313
249314
250315
251316class StatementBlock ( BlocklyBlock ):
252317 '''
253318 A statement-block has a previous and/or next statement notch: stack_input and/or stack_output
254319 '''
255- def __init__ (self , name , title = None , stack_input = False , stack_output = False , color = 170 , category = None ):
256- self .setup ( name , title = title , color = color , category = category )
320+ def __init__ (self , block_name = None , title = None , stack_input = False , stack_output = False , color = 170 , category = None ):
321+ self .setup ( block_name = block_name , title = title , color = color , category = category )
257322 self .make_statement ( stack_input = stack_input , stack_output = stack_output )
258323
259324
260325class ValueBlock ( BlocklyBlock ):
261- def __init__ (self , name , title = None , output = '*' , color = 100 , category = None ):
326+ def __init__ (self , block_name = None , title = None , output = '*' , color = 100 , category = None ):
262327 if output is None : raise TypeError
263- self .setup ( name , title = title , color = color , category = category )
328+ self .setup ( block_name = block_name , title = title , color = color , category = category )
264329 self .set_output ( output )
265330
266331
0 commit comments