This is a read-only archive of the old Scratch 1.x Forums.
Try searching the current Scratch discussion forums.

#1 2012-06-13 13:39:21

Magnie
Scratcher
Registered: 2007-12-12
Posts: 1000+

Python Block Extensions

So I had mentioned that I would make a program that allows you to run things separately from Scratch. As an example, you can get the system time with this.

The Python code:

Code:

#! /usr/env/python
# Block Extensions
# By: Magnie
# GPLv3

instruct = """
Instructions:

1. Start up Scratch and enable Remote Sensor Connections

2. Run be.py (with Python)

3. In Scratch, create a variable named 'be' then set it to 'time'
   then run it.

4. Drag out a sensor value block from the sensing section. Click the
   drop-down menu and select 'be-result'. Click the block once, and
   it should display the time (Hour:Minute:Second).

You can do 3 with the broadcast 'be: time' and get the same results
for number 4.
"""

from array import array

import socket
import time


class ControlBase(object):
    """
    Deals with all the new messages.
    """
    
    def __init__(self, host="127.0.0.1", port=42001):
        self.ext = Extensions()
        self.link = Link(host, port, ext=self)
        
        self.link.run()
    
    def new_message(self, data):
        for msg in data['sensor-update']:
            self.execute(msg, data['sensor-update'][msg])
        
        for msg in data['broadcast']:
            if 'be:' in msg:
                self.execute('be', msg[4:])
    
    def execute(self, name, value):
        if name != 'be':
            return
        
        message = value
        message = message.split(' ')
        block = message[0]
            
        if len(message) > 0:
            args = message[1:]
            
        else:
            args = None
            
        result = self.ext.run_block(block, args)
            
        self.link.send(result)


class Extensions(object):
    """
    The "holder" for the blocks and their functions.
    To create a block, you must do the following:
    
    a) Code a function. 'time' in this example:
       
       def time(self, format_='%H:%M:%S'):
           return time.strftime(format_)
    
    
    b) Create an if statement in the run_block function that runs
       your function and returns the result.
       
       elif block == 'time':
           args = (args or ['%H:%M:%S'])
           return self.time(args[0])
    
    
    c) Add the block "command" to the list of blocks in self.blocks
       ('time' in this example)
    """
    
    def __init__(self):
        pass
    
    def run_block(self, block, args):
        if block == 'echo':
            return 'echo'
        
        elif block == 'time':
            args = (args or ['%H:%M:%S'])
            return self.time(args[0])
        
        elif block == 'help':
            return self.instruct()
        
        return '!No such block?'
        
    def time(self, format_='%H:%M:%S'):
        return time.strftime(format_)
    
    def instruct(self):
        return instruct


class Link(object):
    """
    The connection between Scratch and the extension.
    """
    
    def __init__(self, host="127.0.0.1", port=42001, buffer_=1024, ext=''):
        location = (host, port)
        self.buffer_ = buffer_
        self.ext = ext
        
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect(location)
        
        self.running = 0
    
    def run(self):
        self.running = 1
        while self.running:
            try:
                data = self.sock.recv(self.buffer_)
                data = self.parse_message(data)
                self.ext.new_message(data)
            
            except Exception, e:
                print str(e)
                self.running = 0
                continue
            
    def parse_message(self, message):
        # This pareses the message to make it easier to handle.
        if not message:
            return None
        
        sensors = {}
        broadcasts = []

        commands = []
        i = 0
        while True:
            # Figure out the length and each separate message.
            # Kind of messy and unreadable..
            length = message[i:i + 4]
            length = array("I", length)[0]
            command = message[i + 4:i + length + 4]
            commands.append(command)
            if (i + length + 4) < len(message):
                i = (i+4) + length
            else:
                del command
                break
        
        for command in commands:
            # See if the command is a broadcast or sensor-update.
            if command[0] == 'b':
                command = command.split('"')
                command.pop(0)
                command = '"'.join(command[0:])
                broadcasts.append(command[:-1])
        
            elif command[0] == 's':
                command = command[15:]
                command = command.split('" "', 1)
                name = command[0][0:]
                value = '" "'.join(command[1:])[:-2]
                sensors[name] = value
        
        # Return in programmer-friendly format.
        return dict([('sensor-update', sensors),
                     ('broadcast', broadcasts)])
    
    def give_length(self, cmd):
        # This defines the length of the message.
        n = len(cmd)
        a = array('c')
        a.append(chr((n >> 24) & 0xFF))
        a.append(chr((n >> 16) & 0xFF))
        a.append(chr((n >>  8) & 0xFF))
        a.append(chr(n & 0xFF))
        return (a.tostring() + cmd)
    
    def send(self, message):
        message = 'sensor-update "be-result" "{0}"'.format(message)
        message = self.give_length(message)
        self.sock.send(message)

if __name__ == '__main__':
    # Wouldn't want to ruin someone's program, now would we?
    base = ControlBase()

The instructions are at the top of the code (I don't think you could really miss it). This can be used to run functions that would normally be too complicated for Scratch.

Any suggestions, thoughts?

Offline

 

#2 2012-06-13 13:45:34

chanmanpartyman
Scratcher
Registered: 2011-05-30
Posts: 500+

Re: Python Block Extensions

This is really cool!
EDIT: I remember making a project involving the Scratch OSC Bridge and it allowed me to control the project with my iOS device. The only reason I bring that up is because actions in this are being done remotely also.

Last edited by chanmanpartyman (2012-06-13 14:35:27)

Offline

 

#3 2012-06-13 14:15:52

roijac
Scratcher
Registered: 2010-01-19
Posts: 1000+

Re: Python Block Extensions

nice!
maybe integrate it into m30w?  smile

Offline

 

Board footer