Third party blocks

599 views
Skip to first unread message

Bart Butenaers

unread,
Jul 7, 2018, 4:20:36 AM7/7/18
to Blockly
Hi folks,

I have been looking for some extra blocks, and this is what I have found so far:
Two questions about this:
  • Am I correct that there is no central repository/search where I can find all available third-party blocks?
  • Could anybody add some extra links here to other useful blocks?
Thanks a lot !!
Bart Butenaers

Blake

unread,
Jul 7, 2018, 7:09:13 PM7/7/18
to Blockly
I've written some Java blocks here[1] and here[2]

[1] is also an example of integrating Blockly and Angular 1.5


ArduBlockly has blocks for Arduino https://github.com/carlosperate/ardublockly

There was a project to make blocks for Golang but not sure how far that went https://groups.google.com/forum/#!searchin/blockly/golang%7Csort:date/blockly/_y6rK_NhtNs/XyuKLLnkCQAJ

HTH
Blake

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

a...@anm.me

unread,
Jul 9, 2018, 12:53:19 PM7/9/18
to Blockly
Thanks for the list, Bart.

FWIW, the delay/wait block is part of our standard demos: https://blockly-demo.appspot.com/static/demos/interpreter/async-execution.html (src)

Bart Butenaers

unread,
Jul 9, 2018, 4:16:16 PM7/9/18
to blo...@googlegroups.com
Hi Anm,

Thanks!  
Well I added the delay/wait block to the list, because - if I'm not mistaken - the standard Wait block can only be used in combination with the JS interpreter.  And I'm currently implementing a Blockly addon for the Node-Red IOT framework.  And Node-Red has it's own sandbox mechanism (to run the generated Javascript code), so I need a block based on pure Javascript (i.e. setInterval).

Bart

Virusvrij. www.avast.com

Andrew n marshall

unread,
Jul 9, 2018, 4:37:39 PM7/9/18
to blo...@googlegroups.com
setInterval() requires a first order function, and there is no equivalent in Blockly. Not to say that it is impossible. You just need to wrap the waited code in the fully generated function. To insure it plays will with other generator functions, you'll probably need to structure your setInterval block with a statement input, much like a repeat block.

--

Bart Butenaers

unread,
Jul 9, 2018, 4:49:01 PM7/9/18
to blo...@googlegroups.com
Hi Andrew,

I assume you mean something like this?  
Seems that Mike was able to generate the code that I need.  I have send him a mail if he can share the code of his block ...

Bart

Virusvrij. www.avast.com

Andrew n marshall

unread,
Jul 9, 2018, 5:58:37 PM7/9/18
to blo...@googlegroups.com
Yes, that is a perfect example of what I was thinking. And the same can be done with setTimeout() for a one-time delay.

Virusvrij. www.avast.com

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.

a...@anm.me

unread,
Jul 9, 2018, 7:33:18 PM7/9/18
to Blockly

The more I think about this, the more edge cases I realize you have to deal with. Imagine a setTimeout() inside a loop:
  for(var i = 0; i < 3; ++i) {
    setTimeout(function() {
          // User code goes here.
        }, 1000);
  }

The above code does not do three things with one second intervals. It does three things rapidly after a one second delay. The setTimeout() approach requires the end user to understand this distinction and probably put all of the delayed code in a function that can be recursed with the delay:

  // User defined function, defined with blocks.
  function doSomething() {
    ... // Body of the loop
 
    if (count < 3) {
      setTimeout(function() {
         doSomething();  // Recursion
      }, 1000);
    }
  }


setInterval() gets close, but only if you know when to call clearInterval().  The block and the generator probably need to capture the count or other condition in order to generate the correct code:

  // TODO: Ensure unique variables.
  var intervalBody12345 = function() {
    ... // Body of the loop
  };
  var count12345 = 0;  // Possibly introduced by generator.
  var intervalId12345 = setInterval(function() {
        if (count12345 < 3) { // Or other condition to test.
          intervalBody12345();
        } else {
          clearInterval(intervalId12345);
        }
      }, 1000);


Here are possible blocks:


a...@anm.me

unread,
Jul 9, 2018, 7:35:55 PM7/9/18
to Blockly

Bart Butenaers

unread,
Jul 11, 2018, 5:22:12 PM7/11/18
to Blockly
Hi Andrew,

Awesome good support!  Thanks a lot !!!

I have created following block with count, based on your explanation:

                Blockly.Blocks['setintervalwithcount'] = {
                  init
: function() {
                   
this.appendDummyInput()
                       
.appendField("Repeat ")
                       
.appendField(new Blockly.FieldNumber(0, 0, Infinity, 1), "COUNT")
                       
.appendField("times every")
                       
.appendField(new Blockly.FieldNumber(0, 0, Infinity, 0.01), "INTERVAL")
                       
.appendField("seconds");
                   
this.appendStatementInput("STATEMENTS")
                       
.setCheck(null);
                   
this.setPreviousStatement(true, null);
                   
this.setNextStatement(true, null);
                   
this.setColour(230);
                 
this.setTooltip("");
                 
this.setHelpUrl("");
                 
}
               
};
               
               
Blockly.JavaScript['setintervalwithcount'] = function(block) {
                 
var count = block.getFieldValue('COUNT');
                 
var interval = block.getFieldValue('INTERVAL') * 1000; // milliseconds
                 
var statements = Blockly.JavaScript.statementToCode(block, 'STATEMENTS');
                 
                 
// Generate unique variable names
                 
var i = 'i_' + Math.random().toString(36).substr(2, 16);
                 
var timerId = 'timerId_' + Math.random().toString(36).substr(2, 16);
                 
                 
var code = `var ${i} = 0;\nvar ${timerId} = setInterval(function() {\n ${statements}\n\tif(++${i} === ${count}) {\n\t\tclearInterval(${timerId});\n\t}\n}, ${interval});\n`;
                 
return code;
               
};

This is the generated Javascript coding (for count=4x and interval=5 sec):

var i_sx34cu3paf = 0;
var timerId_0kvwehj324g = setInterval(function() {
   
//...
 
if(++i_sx34cu3paf === 4) {
 clearInterval
(timerId_0kvwehj324g);
 
}
}, 5000);

And that seems to be working fine...

Wouldn't have thought of the unique variable names myself.  Only disadvantage is that the variable names are not very self explaining, which is a pitty since novice users will have a look at the generated code to learn from ...  But that is only a minor issue!

Bart

Andrew n marshall

unread,
Jul 14, 2018, 11:52:20 AM7/14/18
to blo...@googlegroups.com
One more thought...

You can probably cleanup the code further by having your generator export a function at the top of the file (or including the function in the runtime's built-in library):

function repeatForCount(count, intervalMillis, callbackFn) {
  var i = 0;
  var timerId = setInterval(function() {
    callbackFn();
    if(++i === count) {
      clearInterval(timerId);
    }
  }, intervalMillis);
}

This would allow your in-place code to look like:

  repeatForCount(4, 5000, function() {
    // ...
  });

By moving the count variables into the scope of another function, you shouldn't have to worry about variable conflicts anymore.

See math_random_int for an example of a generator that injects a function.

--

Bart Butenaers

unread,
Jul 14, 2018, 5:04:02 PM7/14/18
to Blockly
Andrew,

Yep, nice catch !

I have still another issue with it, for which I would appreciate to have your opinion.


Following sequence will be logged:
  • At 0 seconds: log line 2
  • At 4 seconds: log line 1
  • At 8 seconds: log line 1
  • At 12 seconds: log line 1
Which is normal since the 'Repeat' block will only start the timer, and then the next statement will be executed immediately (i.e. log line 2).

However I 'assume' that users (without programming background) might think - incorrectly - that it will do this:
  • At 4 seconds: log line 1
  • At 8 seconds: log line 1
  • At 12 seconds: log line 1
  • At 12 seconds: log line 2
So they might think that it first repeats the first log line, and then continues immediately with the second log line.  
What do you think about this?

Had been thinking about adding a second statement input to the block:
                    this.appendDummyInput()
                       
.appendField("Afterwards do ")
                   
this.appendStatementInput("AFTER_LOOP")
                       
.setCheck(null);

Which would result in something like this:

Then the users can add statements that should be executed AFTER the repeat loop.

Of cours the second statement input should be optional, so the user can hide it somehow (??) if there is nothing to do afterwards.

Do you think this is more self-explaining ?  Or do you have a better idea to solve this?


Thanks for all your time !!!
Bart

Andrew n marshall

unread,
Jul 14, 2018, 5:26:22 PM7/14/18
to blo...@googlegroups.com
You can add support for afterward, but it is not a replacement for the the requisite understanding of asynchronous calls. JS-interpreter can perform the way you think your users might interpret it, but you said that wasn't an option. Attempting to solve the case with the "afterward" clause will just lead to further edge cases (e.g. what happens when the interval block is put inside a function or another loop?).

You may want to expose the count variable to your users code (similar to the index passed into the forEach() callback), so users can implement their own afterwards equivalent without the additional statement input.



--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

Bart Butenaers

unread,
Jul 22, 2018, 7:02:04 PM7/22/18
to Blockly
Hi Andrew,

My basic Blockly program works fine now, so finally I have time for the timers ;-)
The count variable is indeed a good suggestion!

Your quote "what happens when the interval block is put inside a function or another loop" is indeed an issue.
But will my "afterward" clause cause such issues?  
Seems to me that I will always run into troubles when the delay block is placed inside a loop block (even without "afterwards" clause ???):

for (i = 1; i <= 10; i++) {

   
var i_sx34cu3paf = 0;
   
var timerId_0kvwehj324g = setInterval(function() {
         
//...
         
if(++i_sx34cu3paf === 4) {
               clearInterval
(timerId_0kvwehj324g);
         
}
   
}, 5000);
}

This code has a series of disadvantages:
  1. The program will start immediately 10 timers in parallel, which is probably not what a user will expect?
  2. And timerId_0kvwehj324g will be overwritten, and at the end it will refer to the last timer.  So we will try to stop the last timer 10 times, and the first 9 timers will keep running infinite...
  3. Moreover the dynamically generated timer names have another disadvantage: sometimes it is required that the timer is stopped earlier (e.g. in my Node-Red case when a new input message arrives and triggers the code generated by Blockly).  So I need something like this:

But then the user needs to be able to select the timer variable name also in the delay block.  And then I'm drifting away from your original idea, which was much more self-explaining ;-(

Does anybody have a clue how I could workaround those issues, and keep the delay block understandable for our users?

Bart

Bart Butenaers

unread,
Jul 23, 2018, 5:42:45 AM7/23/18
to Blockly
To be able to have a 'stop timer' block, I need to be able to specify the timerId name somewhere.  For example ('timer1'):

The first line 'run timer ...' seems acceptable to me, however when adding the second line 'count with ...' it could become confusing.

Any suggestions for this, based on your Blockly user experience ?


And the second issue (overwriting the timerId variables) might be solved by using your trick with the 'repeatForCount' function.
But you say "having your generator export a function at the top of the file".    Not sure how that could be done (and avoiding generating with duplicate names).  Something like this??

Thanks!

Andrew n marshall

unread,
Jul 23, 2018, 1:32:43 PM7/23/18
to blo...@googlegroups.com
I'm sorry, I don't have suggestions for simplifying/clarifying the timer block.

With respect to providing a function, use Blockly.JavaScript.provideFunction_(..). Again, look at the example in math_random_int.

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.

Bart Butenaers

unread,
Jul 29, 2018, 5:02:58 AM7/29/18
to Blockly
Hi everybody,

A few users are already testing my Blockly integration for Node-Red, and they are very enthousiastic about Blockly.  And the more I use it, the more I love it ...

There is one thing at the moment that I cannot figure out myself.
When users add two timers (see above for Andrew's proposal) behind each other:

Then they (incorrectly) think that second timer will only start after the first timer has finished (i.e. after 8 seconds).
However the second timer is executed immediately after the start of the first timer.
Seems the novice users are not aware of timers running in parallel: they just see blocks AFTER each other ...

Aren't there any other solutions for this?
  • Could I perhaps alter the behaviour of the nextStatement, to make sure it executes after the timer? 
  • I could remove the symbol, but then they can keep adding statements after it:

  • Other ideas ??? 

Hasan Shingieti

unread,
Jul 30, 2018, 7:49:29 AM7/30/18
to blo...@googlegroups.com
Hi Bart,

If the order of execution matters, you might consider creating an asynchronous custom block. From a visual perspective, perhaps you could add an icon to the block that indicates that it will run in parallel, so they know those blocks will execute at the same time. Other than that, tooltips, help text, and mentoring would be the way to go to communicate these subtleties.

Thanks,
Hasan

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

Mark Friedman

unread,
Aug 2, 2018, 6:07:22 PM8/2/18
to blo...@googlegroups.com
Bart,

  We went through a similar process as you with respect to asynchronous blocks (like your timer).  Our first approach was to do something like your "afterward" approach.  It kind of works, and for the most part our users figured out how to nest those "afterward" clauses to get sequential behavior.  However, as Andrew mentioned that approach still gets problematic when you nest the blocks inside of loops.  Then you have to teach your users to re-write their loops as recursive function calls.  Ugh.

  One approach to the above (which our developer Paul Medlock-Walton devised) is to convert the generators of our asynchronous blocks to have them create promises (which many of the functions called by our asynchronous block implementations already supported).  Then we used used the new "await" syntax of JavaScript to force sequential behavior. 

  For example, the generated code for a "wait for N seconds" block might look like the following:

await new Promise((resolve, reject) => setTimeout(resolve, 3000)); 

  To support the above, you also need all top-level generated code (in our case, function definitions and event handlers) to get generated as an async function.  Then all blocks generating function calls get "await"ed.  And, as mentioned above, any blocks that generate asynchronous code (i.e. promises) also get "await"ed.  Once you've done all that then you can do ordinary loops around your asynchronous blocks. 

  Note that there are likely some performance implications to doing the above. However, in our case, ease of understanding on the part of our users, who are generally novice programmers (and who now can generally avoid thinking about asynchrony), was more important.  You situation might be different, however.

-Mark

To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.

Bart Butenaers

unread,
Aug 4, 2018, 1:14:36 PM8/4/18
to blo...@googlegroups.com

Hello Hassan, Mark,

Sorry for the late reply, but I'm currently in a computer-free period.  I have moved timers to the next version of my blockly integration for Node-red, so I have some extra time to discuss all your options on the Node-red forum.  Wil get back here in a couple of weeks.

Thanks for all proposals.  Couldn't have managed it without...

Bart
Reply all
Reply to author
Forward
0 new messages