diff options
Diffstat (limited to 'TurtleArt')
-rw-r--r-- | TurtleArt/RtfParser.py | 150 | ||||
-rw-r--r-- | TurtleArt/audiograb.py | 622 | ||||
-rw-r--r-- | TurtleArt/rfidutils.py | 123 | ||||
-rw-r--r-- | TurtleArt/ringbuffer.py | 108 | ||||
-rw-r--r-- | TurtleArt/sprites.py | 5 | ||||
-rw-r--r-- | TurtleArt/tabasics.py | 1270 | ||||
-rw-r--r-- | TurtleArt/tablock.py | 255 | ||||
-rw-r--r-- | TurtleArt/tacamera.py | 42 | ||||
-rw-r--r-- | TurtleArt/tacanvas.py | 362 | ||||
-rw-r--r-- | TurtleArt/tacollaboration.py | 379 | ||||
-rw-r--r-- | TurtleArt/taconstants.py | 830 | ||||
-rw-r--r-- | TurtleArt/taexporthtml.py | 160 | ||||
-rw-r--r-- | TurtleArt/taexportlogo.py | 560 | ||||
-rw-r--r-- | TurtleArt/tagettext.py | 24 | ||||
-rw-r--r-- | TurtleArt/tajail.py | 13 | ||||
-rw-r--r-- | TurtleArt/talogo.py | 1414 | ||||
-rw-r--r-- | TurtleArt/tapalette.py | 299 | ||||
-rwxr-xr-x | TurtleArt/tasprite_factory.py | 20 | ||||
-rw-r--r-- | TurtleArt/taturtle.py | 54 | ||||
-rw-r--r-- | TurtleArt/tautils.py | 154 | ||||
-rw-r--r-- | TurtleArt/tawindow.py | 1140 | ||||
-rw-r--r-- | TurtleArt/v4l2.py | 1914 |
22 files changed, 3464 insertions, 6434 deletions
diff --git a/TurtleArt/RtfParser.py b/TurtleArt/RtfParser.py deleted file mode 100644 index 9a141a4..0000000 --- a/TurtleArt/RtfParser.py +++ /dev/null @@ -1,150 +0,0 @@ -#Copyright (c) 2010, Loic Fejoz -#Copyright (c) 2010, Walter Bender - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - - -import sys - - -class RtfException(Exception): - pass - -plaintext = 1 -control = 2 -argument = 3 -backslash = 4 -escapedChar = 5 - - -class RtfParser(object): - - def __init__(self, unicode=False): - self.state = plaintext - self.arg = '' - self.token = '' - self.unicode = unicode - self.par = False - self.output = '' - - def getChar(self, code): - """ called when an escaped char is found """ - return chr(code) - - def getNonBreakingSpace(self): - return ' ' - - def pushState(self): - pass - - def popState(self): - pass - - def putChar(self): - pass - - def doControl(self, token, arg): - pass - - def feed(self, txt): - for c in txt: - self.feedChar(c) - - def feedChar(self, char): - if self.state == plaintext: # this is just normal user content - if char == '\\': - self.state = backslash - elif char == '{': - self.pushState() - elif char == '}': - self.popState() - else: - self.putChar(char) - elif self.state == backslash: # a command or escape - if char == '\\' or char == '{' or char == '}': - self.putChar(char) - self.state = plaintext - else: - if char.isalpha() or char in ('*', '-', '|'): - self.state = control - self.token = char - elif char == "'": - self.state = escapedChar - self.escapedChar = '' - elif char in ['\\', '{', '}']: - self.putChar(char) - self.state = plaintext - elif char == "~": # non breaking space - self.putChar(self.getNonBreakingSpace()) - self.state = plaintext - else: - raise RtfException(('unexpected %s after \\' % char)) - elif self.state == escapedChar: - self.escapedChar = self.escapedChar + char - if len(self.escapedChar) == 2: - char = self.getChar(int(self.escapedChar, 16)) - self.putChar(char) - self.state = plaintext - elif self.state == control: # collecting the command token - if char.isalpha(): - self.token = self.token + char - elif char.isdigit() or char == '-': - self.state = argument - self.arg = char - else: - self.doControl(self.token, self.arg) - self.state = plaintext - if char == '\\': - self.state = backslash - elif char == '{': - self.pushState() - elif char == '}': - self.popState() - else: - if not char.isspace(): - self.putChar(char) - elif self.state == argument: # collecting the optional argument - if char.isdigit(): - self.arg = self.arg + char - else: - self.state = plaintext - self.doControl(self.token, self.arg) - if char == '\\': - self.state = backslash - elif char == '{': - self.pushState() - elif char == '}': - self.popState() - else: - if not char.isspace(): - self.putChar(char) - - -class RtfTextOnly(RtfParser): - - def __init__(self): - RtfParser.__init__(self) - self.level = 0 - - def pushState(self): - self.level = self.level + 1 - - def popState(self): - self.level = self.level - 1 - - def putChar(self, ch): - if self.par: - self.output += ch - - def doControl(self, token, arg): - if token[0:3] == 'par': - self.par = True - pass diff --git a/TurtleArt/audiograb.py b/TurtleArt/audiograb.py deleted file mode 100644 index 3ecdc11..0000000 --- a/TurtleArt/audiograb.py +++ /dev/null @@ -1,622 +0,0 @@ -#! /usr/bin/python -# -# Author: Arjun Sarwal arjun@laptop.org -# Copyright (C) 2007, Arjun Sarwal -# Copyright (C) 2009,10 Walter Bender -# Copyright (C) 2009, Benjamin Berg, Sebastian Berg -# Copyright (C) 2009, Sayamindu Dasgupta -# Copyright (C) 2010, Sascha Silbe -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import pygst -import gst -import gst.interfaces -from numpy import fromstring -import os -import subprocess -from string import find -from threading import Timer - -# Initial device settings -RATE = 48000 -MIC_BOOST = True -DC_MODE_ENABLE = False -CAPTURE_GAIN = 50 -BIAS = True - -# Setting on quit -QUIT_MIC_BOOST = False -QUIT_DC_MODE_ENABLE = False -QUIT_CAPTURE_GAIN = 100 -QUIT_BIAS = True - -import logging - -_logger = logging.getLogger('TurtleArt') -_logger.setLevel(logging.DEBUG) -logging.basicConfig() - -from taconstants import SENSOR_AC_NO_BIAS, SENSOR_AC_BIAS, SENSOR_DC_NO_BIAS, \ - SENSOR_DC_BIAS, XO1 - - -class AudioGrab: - """ The interface between measure and the audio device """ - - def __init__(self, callable1, activity): - """ Initialize the class: callable1 is a data buffer; - activity is the parent class""" - - self.callable1 = callable1 - self.activity = activity - self.sensor = None - - self.temp_buffer = [0] - self.picture_buffer = [] # place to hold screen grabs - - self.draw_graph_status = False - self.screenshot = True - - self.rate = 48000 - self.final_count = 0 - self.count_temp = 0 - self.entry_count = 0 - - self.counter_buffer = 0 - - self._dc_control = None - self._mic_bias_control = None - self._capture_control = None - self._mic_boost_control = None - self._hardwired = False # Query controls or use hardwired names - - # Set up gst pipeline - self.pipeline = gst.Pipeline("pipeline") - self.alsasrc = gst.element_factory_make("alsasrc", "alsa-source") - self.pipeline.add(self.alsasrc) - self.caps1 = gst.element_factory_make("capsfilter", "caps1") - self.pipeline.add(self.caps1) - caps_str = "audio/x-raw-int,rate=%d,channels=1,depth=16" % (RATE) - self.caps1.set_property("caps", gst.caps_from_string(caps_str)) - self.fakesink = gst.element_factory_make("fakesink", "fsink") - self.pipeline.add(self.fakesink) - self.fakesink.connect("handoff", self.on_buffer) - self.fakesink.set_property("signal-handoffs", True) - gst.element_link_many(self.alsasrc, self.caps1, self.fakesink) - - self.dont_queue_the_buffer = False - - self._mixer = gst.element_factory_make('alsamixer') - rc = self._mixer.set_state(gst.STATE_PAUSED) - assert rc == gst.STATE_CHANGE_SUCCESS - - # Query the available controls - try: # F11+ - _logger.debug('controls: %r', [t.props.untranslated_label \ - for t in self._mixer.list_tracks()]) - self._dc_control = self._find_control(['dc mode']) - self._mic_bias_control = self._find_control(['mic bias', - 'dc input bias', - 'v_refout']) - self._mic_boost_control = self._find_control(['mic boost', - 'mic boost (+20db)', - 'internal mic boost', - 'analog mic boost']) - self._mic_gain_control = self._find_control(['mic']) - self._capture_control = self._find_control(['capture']) - self._master_control = self._find_control(['master']) - except AttributeError: # F9- (no untranslated_label attribute) - self._hardwired = True - - # Variables for saving and resuming state of sound device - self.master = self.get_master() - self.bias = BIAS - self.dcmode = DC_MODE_ENABLE - self.capture_gain = CAPTURE_GAIN - self.mic_boost = MIC_BOOST - self.mic = self.get_mic_gain() - - # Timer for interval sampling and switch to indicate when to capture - self.capture_timer = None - self.capture_interval_sample = False - - def set_handoff_signal(self, handoff_state): - """Sets whether the handoff signal would generate an interrupt or not""" - self.fakesink.set_property("signal-handoffs", handoff_state) - - def _new_buffer(self, buf): - """ Use a new buffer """ - if not self.dont_queue_the_buffer: - self.temp_buffer = buf - self.callable1(buf) - else: - pass - - def on_buffer(self, element, buffer, pad): - """The function that is called whenever new data is available - This is the signal handler for the handoff signal""" - if buffer is None: - _logger.debug('audiograb buffer is None') - return False - - temp_buffer = fromstring(buffer, 'int16') - if not self.dont_queue_the_buffer: - self._new_buffer(temp_buffer) - return False - - def set_sensor(self, sensor): - """Keep a reference to the sensot toolbar for logging""" - self.sensor = sensor - - def start_sound_device(self): - """Start or Restart grabbing data from the audio capture""" - gst.event_new_flush_start() - self.pipeline.set_state(gst.STATE_PLAYING) - - def stop_sound_device(self): - """Stop grabbing data from capture device""" - gst.event_new_flush_stop() - self.pipeline.set_state(gst.STATE_NULL) - - def set_sampling_rate(self, sr): - """Sets the sampling rate of the capture device - Sampling rate must be given as an integer for example 16000 for - setting 16Khz sampling rate - The sampling rate would be set in the device to the nearest available""" - self.pause_grabbing() - caps_str = "audio/x-raw-int,rate=%d,channels=1,depth=16" % (sr, ) - self.caps1.set_property("caps", gst.caps_from_string(caps_str)) - self.resume_grabbing() - - def get_sampling_rate(self): - """Gets the sampling rate of the capture device""" - return int(self.caps1.get_property("caps")[0]['rate']) - - def set_callable1(self, callable1): - """Sets the callable to the drawing function for giving the - data at the end of idle-add""" - self.callable1 = callable1 - - def start_grabbing(self): - """Called right at the start of the Activity""" - self.start_sound_device() - - def pause_grabbing(self): - """When Activity goes into background""" - self.save_state() - self.stop_sound_device() - - def resume_grabbing(self): - """When Activity becomes active after going to background""" - self.start_sound_device() - self.resume_state() - - def stop_grabbing(self): - """Not used ???""" - self.stop_sound_device() - self.set_handoff_signal(False) - - def _find_control(self, prefixes): - """Try to find a mixer control matching one of the prefixes. - - The control with the best match (smallest difference in length - between label and prefix) will be returned. If no match is found, - None is returned. - """ - def best_prefix(label, prefixes): - matches =\ - [len(label) - len(p) for p in prefixes if label.startswith(p)] - if not matches: - return None - - matches.sort() - return matches[0] - - controls = [] - for track in self._mixer.list_tracks(): - label = track.props.untranslated_label.lower() - diff = best_prefix(label, prefixes) - if diff is not None: - controls.append((track, diff)) - - controls.sort(key=lambda e: e[1]) - if controls: - _logger.debug("found control: %s" %\ - (str(controls[0][0].props.untranslated_label))) - return controls[0][0] - - return None - - def save_state(self): - """Saves the state of all audio controls""" - self.master = self.get_master() - self.bias = self.get_bias() - self.dcmode = self.get_dc_mode() - self.capture_gain = self.get_capture_gain() - self.mic_boost = self.get_mic_boost() - - def resume_state(self): - """Put back all audio control settings from the saved state""" - self.set_master(self.master) - self.set_bias(self.bias) - self.set_dc_mode(self.dcmode) - self.set_capture_gain(self.capture_gain) - self.set_mic_boost(self.mic_boost) - - def _get_mute(self, control, name, default): - """Get mute status of a control""" - if not control: - return default - - value = bool(control.flags & gst.interfaces.MIXER_TRACK_MUTE) - _logger.debug('Getting %s (%s) mute status: %r', name, - control.props.untranslated_label, value) - return value - - def _set_mute(self, control, name, value): - """Mute a control""" - if not control: - return - - self._mixer.set_mute(control, value) - _logger.debug('Set mute for %s (%s) to %r', name, - control.props.untranslated_label, value) - - def _get_volume(self, control, name): - """Get volume of a control and convert to a scale of 0-100""" - if not control: - return 100 - - try: # sometimes get_volume does not return a tuple - hw_volume = self._mixer.get_volume(control)[0] - except IndexError: - return 100 - - min_vol = control.min_volume - max_vol = control.max_volume - percent = (hw_volume - min_vol)*100//(max_vol - min_vol) - return percent - - def _set_volume(self, control, name, value): - """Sets the level of a control on a scale of 0-100""" - if not control: - return - - # convert value to scale of control - min_vol = control.min_volume - max_vol = control.max_volume - if min_vol != max_vol: - hw_volume = value*(max_vol - min_vol)//100 + min_vol - self._mixer.set_volume(control, (hw_volume,)*control.num_channels) - - def amixer_set(self, control, state): - """ Direct call to amixer for old systems. """ - if state: - os.system("amixer set '%s' unmute" % (control)) - else: - os.system("amixer set '%s' mute" % (control)) - - def mute_master(self): - """Mutes the Master Control""" - if not self._hardwired and self.activity.hw != XO1: - self._set_mute(self._master_control, 'Master', True) - else: - self.amixer_set('Master', False) - - def unmute_master(self): - """Unmutes the Master Control""" - if not self._hardwired and self.activity.hw != XO1: - self._set_mute(self._master_control, 'Master', True) - else: - self.amixer_set('Master', True) - - def set_master(self, master_val): - """Sets the Master gain slider settings - master_val must be given as an integer between 0 and 100 indicating the - percentage of the slider to be set""" - if not self._hardwired: - self._set_volume(self._master_control, 'Master', master_val) - else: - os.system("amixer set Master " + str(master_val) + "%") - - def get_master(self): - """Gets the Master gain slider settings. The value returned is an - integer between 0-100 and is an indicative of the percentage 0 - 100%""" - if not self._hardwired: - return self._get_volume(self._master_control, 'master') - else: - p = str(subprocess.Popen(["amixer", "get", "Master"], - stdout=subprocess.PIPE).communicate()[0]) - p = p[find(p, "Front Left:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "%]")] - return int(p) - - def set_bias(self, bias_state=False): - """Enables / disables bias voltage.""" - if not self._hardwired and self.activity.hw != XO1: - if self._mic_bias_control is None: - return - # if not isinstance(self._mic_bias_control, - # gst.interfaces.MixerOptions): - if self._mic_bias_control not in self._mixer.list_tracks(): - return self._set_mute(self._mic_bias_control, 'Mic Bias', - not bias_state) - - # We assume that values are sorted from lowest (=off) to highest. - # Since they are mixed strings ("Off", "50%", etc.), we cannot - # easily ensure this by sorting with the default sort order. - try: - if bias_state: - # self._mixer.set_option(self._mic_bias_control, values[-1]) - self._mixer.set_volume(self._mic_bias_control, - self._mic_bias_control.max_volume) - else: - self._mixer.set_volume(self._mic_bias_control, - self._mic_bias_control.min_volume) - # self._mixer.set_option(self._mic_bias_control, values[0]) - except TypeError: - self._set_mute(self._mic_bias_control, 'Mic Bias', - not bias_state) - elif self._hardwired: - self.amixer_set('V_REFOUT Enable', bias_state) - else: - self.amixer_set('MIC Bias Enable', bias_state) - - def get_bias(self): - """Check whether bias voltage is enabled.""" - if not self._hardwired: - if self._mic_bias_control is None: - return False - if self._mic_bias_control not in self._mixer.list_tracks(): - return not self._get_mute(self._mic_bias_control, 'Mic Bias', - False) - current = self._mixer.get_volume(self._mic_bias_control) - # same ordering assertion as in set_bias() applies - # if current == values[0]: - if current == self._mic_bias_control.min_volume: - return False - return True - else: - p = str(subprocess.Popen(["amixer", "get", "'V_REFOUT Enable'"], - stdout=subprocess.PIPE).communicate()[0]) - p = p[find(p, "Mono:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "]")] - if p == "on": - return True - return False - - def set_dc_mode(self, dc_mode=False): - """Sets the DC Mode Enable control - pass False to mute and True to unmute""" - if not self._hardwired and self.activity.hw != XO1: - if self._dc_control is not None: - self._set_mute(self._dc_control, 'DC mode', not dc_mode) - else: - self.amixer_set('DC Mode Enable', dc_mode) - - def get_dc_mode(self): - """Returns the setting of DC Mode Enable control - i .e. True: Unmuted and False: Muted""" - if not self._hardwired: - if self._dc_control is not None: - return not self._get_mute(self._dc_control, 'DC mode', False) - else: - return False - else: - p = str(subprocess.Popen(["amixer", "get", "'DC Mode Enable'"], - stdout=subprocess.PIPE).communicate()[0]) - p = p[find(p, "Mono:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "]")] - if p == "on": - return True - else: - return False - - def set_mic_boost(self, mic_boost=False): - """Set Mic Boost. - True = +20dB, False = 0dB""" - if not self._hardwired: - if self._mic_boost_control is None: - return - if self._mic_boost_control not in self._mixer.list_tracks(): - return self._set_mute(self._mic_boost_control, 'Mic Boost', - mic_boost) - value = self._mixer.get_volume(self._mic_boost_control) - try: - if mic_boost: - self._mixer.set_volume(self._mic_boost_control, - self._mic_boost_control.max_volume) - else: - self._mixer.set_volume(self._mic_boost_control, - self._mic_boost_control.min_volume) - except TypeError: - return self._set_mute(self._mic_boost_control, 'Mic Boost', - not mic_boost) - else: - self.amixer_set('Mic Boost (+20dB)', mic_boost) - - def get_mic_boost(self): - """Return Mic Boost setting. - True = +20dB, False = 0dB""" - if not self._hardwired: - if self._mic_boost_control is None: - return False - if self._mic_boost_control not in self._mixer.list_tracks(): - return self._get_mute(self._mic_boost_control, 'Mic Boost', - False) - current = self._mixer.get_volume(self._mic_boost_control) - _logger.debug('current: %s' % (str(current))) - if current != self._mic_boost_control.min_volume: - return True - return False - else: - p = str(subprocess.Popen(["amixer", "get", "'Mic Boost (+20dB)'"], - stdout=subprocess.PIPE).communicate()[0]) - p = p[find(p, "Mono:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "]")] - if p == "on": - return True - else: - return False - - def set_capture_gain(self, capture_val): - """Sets the Capture gain slider settings - capture_val must be given as an integer between 0 and 100 indicating the - percentage of the slider to be set""" - if not self._hardwired and self.activity.hw != XO1: - if self._capture_control is not None: - self._set_volume(self._capture_control, 'Capture', capture_val) - else: - os.system("amixer set Capture " + str(capture_val) + "%") - - def get_capture_gain(self): - """Gets the Capture gain slider settings. The value returned is an - integer between 0-100 and is an indicative of the percentage 0 - 100%""" - if not self._hardwired: - if self._capture_control is not None: - return self._get_volume(self._capture_control, 'Capture') - else: - return 0 - else: - p = str(subprocess.Popen(["amixer", "get", "Capture"], - stdout=subprocess.PIPE).communicate()[0]) - p = p[find(p, "Front Left:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "%]")] - return int(p) - - def set_mic_gain(self, mic_val): - """Sets the MIC gain slider settings - mic_val must be given as an integer between 0 and 100 indicating the - percentage of the slider to be set""" - if not self._hardwired and self.activity.hw != XO1: - self._set_volume(self._mic_gain_control, 'Mic', mic_val) - else: - os.system("amixer set Mic " + str(mic_val) + "%") - - def get_mic_gain(self): - """Gets the MIC gain slider settings. The value returned is an - integer between 0-100 and is an indicative of the percentage 0 - 100%""" - if not self._hardwired: - return self._get_volume(self._mic_gain_control, 'Mic') - else: - p = str(subprocess.Popen(["amixer", "get", "Mic"], - stdout=subprocess.PIPE).communicate()[0]) - try: - p = p[find(p, "Mono:"):] - p = p[find(p, "[")+1:] - p = p[:find(p, "%]")] - return int(p) - except: - return(0) - - def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS): - """Set the type of sensor you want to use. Set sensor_type according - to the following - SENSOR_AC_NO_BIAS - AC coupling with Bias Off --> Very rarely used. - Use when connecting a dynamic microphone externally - SENSOR_AC_BIAS - AC coupling with Bias On --> The default settings. - The internal MIC uses these - SENSOR_DC_NO_BIAS - DC coupling with Bias Off --> measuring voltage - output sensor. For example LM35 which gives output proportional - to temperature - SENSOR_DC_BIAS - DC coupling with Bias On --> measuing resistance. - """ - PARAMETERS = { - SENSOR_AC_NO_BIAS: (False, False, 50, True), - SENSOR_AC_BIAS: (False, True, 40, True), - SENSOR_DC_NO_BIAS: (True, False, 0, False), - SENSOR_DC_BIAS: (True, True, 0, False) - } - mode, bias, gain, boost = PARAMETERS[sensor_type] - _logger.debug("====================================") - _logger.debug("Set Sensor Type to %s" % (str(sensor_type))) - self._set_sensor_type(mode, bias, gain, boost) - _logger.debug("====================================") - - def _set_sensor_type(self, mode=None, bias=None, gain=None, boost=None): - """Helper to modify (some) of the sensor settings.""" - if mode is not None: - self.set_dc_mode(mode) - if self._dc_control is not None: - os.system("amixer get '%s'" %\ - (self._dc_control.props.untranslated_label)) - if bias is not None: - self.set_bias(bias) - if self._mic_bias_control is not None: - os.system("amixer get '%s'" %\ - (self._mic_bias_control.props.untranslated_label)) - if gain is not None: - self.set_capture_gain(gain) - if self._capture_control is not None: - os.system("amixer get '%s'" %\ - (self._capture_control.props.untranslated_label)) - if boost is not None: - self.set_mic_boost(boost) - if self._mic_boost_control is not None: - os.system("amixer get '%s'" %\ - (self._mic_boost_control.props.untranslated_label)) - - def on_activity_quit(self): - """When Activity quits""" - self.set_mic_boost(QUIT_MIC_BOOST) - self.set_dc_mode(QUIT_DC_MODE_ENABLE) - self.set_capture_gain(QUIT_CAPTURE_GAIN) - self.set_bias(QUIT_BIAS) - self.stop_sound_device() - - -class AudioGrab_XO1(AudioGrab): - """ Use default parameters for OLPC XO 1.0 laptop """ - pass - - -class AudioGrab_XO15(AudioGrab): - """ Override parameters for OLPC XO 1.5 laptop """ - def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS): - """Helper to modify (some) of the sensor settings.""" - PARAMETERS = { - SENSOR_AC_NO_BIAS: (False, False, 80, True), - SENSOR_AC_BIAS: (False, True, 80, True), - SENSOR_DC_NO_BIAS: (True, False, 80, False), - SENSOR_DC_BIAS: (True, True, 90, False) - } - _logger.debug("====================================") - _logger.debug("Set Sensor Type to %s" % (str(sensor_type))) - mode, bias, gain, boost = PARAMETERS[sensor_type] - self._set_sensor_type(mode, bias, gain, boost) - _logger.debug("====================================") - - -class AudioGrab_Unknown(AudioGrab): - """ Override parameters for generic hardware """ - def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS): - """Helper to modify (some) of the sensor settings.""" - PARAMETERS = { - SENSOR_AC_NO_BIAS: (None, False, 50, True), - SENSOR_AC_BIAS: (None, True, 40, True), - SENSOR_DC_NO_BIAS: (True, False, 80, False), - SENSOR_DC_BIAS: (True, True, 90, False) - } - _logger.debug("====================================") - _logger.debug("Set Sensor Type to %s" % (str(sensor_type))) - mode, bias, gain, boost = PARAMETERS[sensor_type] - self._set_sensor_type(mode, bias, gain, boost) - _logger.debug("====================================") diff --git a/TurtleArt/rfidutils.py b/TurtleArt/rfidutils.py deleted file mode 100644 index f2c74b4..0000000 --- a/TurtleArt/rfidutils.py +++ /dev/null @@ -1,123 +0,0 @@ -# utils.py - Helper functions for tis2000.py -# Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os -import logging - -def find_device(): - """ - Search for devices. - Return a device instance or None. - """ - device = None - for i in os.listdir(os.path.join('.', 'devices')): - if not os.path.isdir(os.path.join('.', 'devices', i)): - try: - _tempmod = __import__('devices.%s'%i.split('.')[0], globals(), - locals(), ['RFIDReader'], -1) - devtemp = _tempmod.RFIDReader() - if devtemp.get_present() == True: - device = devtemp - except Exception, e: - # logging.error("FIND_DEVICE: %s: %s"%(i, e)) - pass - if device is None: - logging.debug("No RFID device found") - return device - - -def strhex2bin(strhex): - """ - Convert a string representing an hex value into a - string representing the same value in binary format. - """ - dic = { '0':"0000", - '1':"0001", - '2':"0010", - '3':"0011", - '4':"0100", - '5':"0101", - '6':"0110", - '7':"0111", - '8':"1000", - '9':"1001", - 'A':"1010", - 'B':"1011", - 'C':"1100", - 'D':"1101", - 'E':"1110", - 'F':"1111" - } - binstr = "" - for i in strhex: - binstr = binstr + dic[i.upper()] - return binstr - -def strbin2dec(strbin): - """ - Convert a string representing a binary value into a - string representing the same value in decimal format. - """ - strdec = "0" - for i in range(1, strbin.__len__()+1): - strdec = str(int(strdec)+int(strbin[-i])*int(pow(2, i-1))) - return strdec - -def dec2bin(ndec): - """ - Convert a decimal number into a string representing - the same value in binary format. - """ - if ndec < 1: - return "0" - binary = [] - while ndec != 0: - binary.append(ndec%2) - ndec = ndec/2 - strbin = "" - binary.reverse() - for i in binary: - strbin = strbin+str(i) - return strbin - -def bin2hex(strbin): - """ - Convert a string representing a binary number into a string - representing the same value in hexadecimal format. - """ - dic = { "0000":"0", - "0001":"1", - "0010":"2", - "0011":"3", - "0100":"4", - "0101":"5", - "0110":"6", - "0111":"7", - "1000":"8", - "1001":"9", - "1010":"A", - "1011":"B", - "1100":"C", - "1101":"D", - "1110":"E", - "1111":"F" - } - while strbin.__len__()%4 != 0: - strbin = '0' + strbin - strh = "" - for i in range(0, strbin.__len__()/4): - strh = strh + dic[str(strbin[i*4:i*4+4])] - return strh diff --git a/TurtleArt/ringbuffer.py b/TurtleArt/ringbuffer.py deleted file mode 100644 index 2afb5c9..0000000 --- a/TurtleArt/ringbuffer.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (C) 2009, Benjamin Berg, Sebastian Berg -# Copyright (C) 2010, Walter Bender -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import numpy as np - - -class RingBuffer1d(object): - """This class implements an array being written in as a ring and that can - be read from continuously ending with the newest data or starting with the - oldest. It returns a numpy array copy of the data; - """ - - def __init__(self, length, dtype=None): - """Initialize the 1 dimensional ring buffer with the given lengths. - The initial values are all 0s - """ - self.offset = 0 - - self._data = np.zeros(length, dtype=dtype) - - self.stored = 0 - - def fill(self, number): - self._data.fill(number) - self.offset = 0 - - def append(self, data): - """Append to the ring buffer (and overwrite old data). If len(data) - is greater then the ring buffers length, the newest data takes - precedence. - """ - data = np.asarray(data) - - if len(self._data) == 0: - return - - if len(data) >= len(self._data): - self._data[:] = data[-len(self._data):] - self.offset = 0 - self.stored = len(self._data) - - elif len(self._data) - self.offset >= len(data): - self._data[self.offset: self.offset + len(data)] = data - self.offset = self.offset + len(data) - self.stored += len(data) - else: - self._data[self.offset:] = data[:len(self._data) - self.offset] - self._data[:len(data) - (len(self._data) - self.offset)] = \ - data[-len(data) + (len(self._data) - self.offset):] - self.offset = len(data) - (len(self._data) - self.offset) - self.stored += len(data) - - if len(self._data) <= self.stored: - self.read = self._read - - def read(self, number=None, step=1): - """Read the ring Buffer. Number can be positive or negative. - Positive values will give the latest information, negative values will - give the newest added information from the buffer. (in normal order) - - Before the buffer is filled once: This returns just None - """ - return np.array([]) - - def _read(self, number=None, step=1): - """Read the ring Buffer. Number can be positive or negative. - Positive values will give the latest information, negative values will - give the newest added information from the buffer. (in normal order) - """ - if number == None: - number = len(self._data) // step - - number *= step - assert abs(number) <= len(self._data), \ - 'Number to read*step must be smaller then length' - - if number < 0: - if abs(number) <= self.offset: - return self._data[self.offset + number:self.offset:step] - - spam = (self.offset - 1) % step - - return np.concatenate( - (self._data[step - spam - 1 + self.offset + number::step], - self._data[spam:self.offset:step])) - - if number - (len(self._data) - self.offset) > 0: - spam = ((self.offset + number) - self.offset - 1) % step - return np.concatenate( - (self._data[self.offset:self.offset + number:step], - self._data[spam:number - - (len(self._data) - self.offset):step])) - - return self._data[self.offset:self.offset + number:step].copy() diff --git a/TurtleArt/sprites.py b/TurtleArt/sprites.py index fdcd72f..c633397 100644 --- a/TurtleArt/sprites.py +++ b/TurtleArt/sprites.py @@ -426,12 +426,15 @@ class Sprite: b = ord(array[offset + 2]) return(r, g, b, a) except IndexError: + """ print "Index Error: %d %d (%d, %d) (w: %d, h: %d) (%dx%d)"\ % (len(array), offset, x, y, self.images[i].get_width(), self.images[i].get_height(), self.rect.width, self.rect.height) - return(-1, -1, -1, -1) + """ + pass + return(-1, -1, -1, -1) else: w, h = self.images[i].get_size() if x < 0 or x > (w - 1) or y < 0 or y > (h - 1): diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py new file mode 100644 index 0000000..bfed2d7 --- /dev/null +++ b/TurtleArt/tabasics.py @@ -0,0 +1,1270 @@ +# -*- coding: utf-8 -*- +#Copyright (c) 2011, Walter Bender + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +""" +This file contains the constants that by-in-large determine the +behavior of Turtle Art. Notably, the block palettes are defined +below. If you want to add a new block to Turtle Art, you could +simply add a block of code to this file or to turtle_block_plugin.py, +which contains additional blocks. (Even better, write your own plugin!!) + + +Adding a new palette is simply a matter of: + palette = make_palette('mypalette', # the name of your palette + colors=["#00FF00", "#00A000"], + help_string=_('Palette of my custom commands')) + +For example, if we want to add a new turtle command, 'uturn', we'd use the +add_block method in the Palette class. + palette.add_block('uturn', # the name of your block + style='basic-style', # the block style + label=_('u turn'), # the label for the block + prim_name='uturn', # code reference (see below) + help_string=_('turns the turtle 180 degrees')) + + # Next, you need to define what your block will do: + # def_prim takes 3 arguments: the primitive name, the number of + # arguments -- 0 in this case -- and the function to call -- in this + # case, the canvas.seth function to set the heading. + self.tw.lc.def_prim('uturn', 0, + lambda self: self.tw.canvas.seth(self.tw.canvas.heading + 180)) + +That's it. When you next run Turtle Art, you will have a 'uturn' block +on the 'mypalette' palette. + +You will have to create icons for the palette-selector buttons. These +are kept in the icons subdirectory. You need two icons: +mypaletteoff.svg and mypaletteon.svg, where 'mypalette' is the same +string as the entry you used in instantiating the Palette class. Note +that the icons should be the same size (55x55) as the others. (This is +the default icon size for Sugar toolbars.) +""" + +from time import time +from math import sqrt +from random import uniform + +from gettext import gettext as _ + +from tapalette import make_palette, define_logo_function +from talogo import primitive_dictionary, logoerror +from tautils import convert, chr_to_ord, round_int, strtype +from taconstants import BLACK, WHITE, CONSTANTS + +def _num_type(x): + """ Is x a number type? """ + if type(x) == int: + return True + if type(x) == float: + return True + if type(x) == ord: + return True + return False + + +def _millisecond(): + """ Current time in milliseconds """ + return time() * 1000 + + +class Palettes(): + """ a class for creating the palettes of blocks """ + + def __init__(self, parent): + self.tw = parent + + self._turtle_palette() + + self._pen_palette() + + self._color_palette() + + self._numbers_palette() + + self._flow_palette() + + self._blocks_palette() + + self._trash_palette() + + # Palette definitions + + def _turtle_palette(self): + """ The basic Turtle Art turtle palette """ + + palette = make_palette('turtle', + colors=["#00FF00", "#00A000"], + help_string=_('Palette of turtle commands')) + + primitive_dictionary['move'] = self._prim_move + palette.add_block('forward', + style='basic-style-1arg', + label=_('forward'), + prim_name='forward', + default=100, + logo_command='forward', + help_string=_('moves turtle forward')) + self.tw.lc.def_prim('forward', 1, + lambda self, x: primitive_dictionary['move']( + self.tw.canvas.forward, x)) + + palette.add_block('back', + style='basic-style-1arg', + label=_('back'), + prim_name='back', + default=100, + logo_command='back', + help_string=_('moves turtle backward')) + self.tw.lc.def_prim('back', 1, + lambda self, x: primitive_dictionary['move']( + self.tw.canvas.forward, -x)) + + primitive_dictionary['clean'] = self.tw.lc.prim_clear + palette.add_block('clean', + style='basic-style-extended-vertical', + label=_('clean'), + prim_name='clean', + logo_command='clean', + help_string=_('clears the screen and reset the \ +turtle')) + self.tw.lc.def_prim('clean', 0, + lambda self: primitive_dictionary['clean']()) + + primitive_dictionary['right'] = self._prim_right + palette.add_block('left', + style='basic-style-1arg', + label=_('left'), + prim_name='left', + default=90, + logo_command='left', + help_string=_('turns turtle counterclockwise (angle \ +in degrees)')) + self.tw.lc.def_prim('left', 1, + lambda self, x: primitive_dictionary['right'](-x)) + + palette.add_block('right', + style='basic-style-1arg', + label=_('right'), + prim_name='right', + default=90, + logo_command='right', + help_string=_('turns turtle clockwise (angle in \ +degrees)')) + self.tw.lc.def_prim('right', 1, + lambda self, x: primitive_dictionary['right'](x)) + + primitive_dictionary['arc'] = self._prim_arc + palette.add_block('arc', + style='basic-style-2arg', + label=[_('arc'), _('angle'), _('radius')], + prim_name='arc', + default=[90, 100], + logo_command='taarc', + help_string=_('moves turtle along an arc')) + self.tw.lc.def_prim('arc', 2, + lambda self, x, y: primitive_dictionary['arc']( + self.tw.canvas.arc, x, y)) + define_logo_function('taarc', 'to taarc :a :r\rrepeat round :a \ +[right 1 forward (0.0175 * :r)]\rend\r') + + palette.add_block('setxy2', + style='basic-style-2arg', + label=[_('set xy'), _('x'), _('y')], + prim_name='setxy2', + logo_command='tasetxy', + default=[0, 0], + help_string=_('moves turtle to position xcor, ycor; \ +(0, 0) is in the center of the screen.')) + self.tw.lc.def_prim('setxy2', 2, + lambda self, x, y: primitive_dictionary['move']( + self.tw.canvas.setxy, x, y)) + define_logo_function('tasetxy', 'to tasetxy :x :y\rsetxy :x :y\rend\r') + + primitive_dictionary['set'] = self._prim_set + palette.add_block('seth', + style='basic-style-1arg', + label=_('set heading'), + prim_name='seth', + default=0, + logo_command='seth', + help_string=_('sets the heading of the turtle (0 is \ +towards the top of the screen.)')) + self.tw.lc.def_prim('seth', 1, + lambda self, x: primitive_dictionary['set']( + 'heading', self.tw.canvas.seth, x)) + + palette.add_block('xcor', + style='box-style', + label=_('xcor'), + help_string=_('holds current x-coordinate value of \ +the turtle (can be used in place of a number block)'), + value_block=True, + prim_name='xcor', + logo_command='xcor') + self.tw.lc.def_prim( + 'xcor', 0, lambda self: self.tw.canvas.xcor / self.tw.coord_scale) + + palette.add_block('ycor', + style='box-style', + label=_('ycor'), + help_string=_('holds current y-coordinate value of \ +the turtle (can be used in place of a number block)'), + value_block=True, + prim_name='ycor', + logo_command='ycor') + self.tw.lc.def_prim( + 'ycor', 0, lambda self: self.tw.canvas.ycor / self.tw.coord_scale) + + palette.add_block('heading', + style='box-style', + label=_('heading'), + help_string=_('holds current heading value of the \ +turtle (can be used in place of a number block)'), + value_block=True, + prim_name='heading', + logo_command='heading') + self.tw.lc.def_prim( + 'heading', 0, lambda self: self.tw.canvas.heading) + + palette.add_block('turtle-label', + hidden=True, + style='blank-style', + label=['turtle']) + + # Deprecated + palette.add_block('setxy', + hidden=True, + style='basic-style-2arg', + label=[_('set xy'), _('x'), _('y')], + prim_name='setxy', + default=[0, 0], + logo_command='tasetxypenup', + help_string=_('moves turtle to position xcor, ycor; \ +(0, 0) is in the center of the screen.')) + self.tw.lc.def_prim('setxy', 2, + lambda self, x, y: primitive_dictionary['move']( + self.tw.canvas.setxy, x, y, pendown=False)) + define_logo_function('tasetxypenup', 'to tasetxypenup :x :y\rpenup\r\ +setxy :x :y\rpendown\rend\r') + + def _pen_palette(self): + """ The basic Turtle Art pen palette """ + + palette = make_palette('pen', + colors=["#00FFFF", "#00A0A0"], + help_string=_('Palette of pen commands')) + + palette.add_block('penup', + style='basic-style-extended-vertical', + label=_('pen up'), + prim_name='penup', + logo_command='penup', + help_string=_('Turtle will not draw when moved.')) + self.tw.lc.def_prim('penup', 0, + lambda self: self.tw.canvas.setpen(False)) + + palette.add_block('pendown', + style='basic-style-extended-vertical', + label=_('pen down'), + prim_name='pendown', + logo_command='pendown', + help_string=_('Turtle will draw when moved.')) + self.tw.lc.def_prim('pendown', 0, + lambda self: self.tw.canvas.setpen(True)) + + palette.add_block('setpensize', + style='basic-style-1arg', + label=_('set pen size'), + prim_name='setpensize', + default=5, + logo_command='setpensize', + help_string=_('sets size of the line drawn by the \ +turtle')) + self.tw.lc.def_prim('setpensize', 1, + lambda self, x: primitive_dictionary['set']( + 'pensize', self.tw.canvas.setpensize, x)) + define_logo_function('tasetpensize', 'to tasetpensize :a\rsetpensize \ +round :a\rend\r') + + palette.add_block('fillscreen', + style='basic-style-2arg', + label=[_('fill screen'), _('color'), _('shade')], + prim_name='fillscreen', + default=[60, 80], + logo_command='tasetbackground', + help_string=_('fills the background with (color, \ +shade)')) + self.tw.lc.def_prim('fillscreen', 2, + lambda self, x, y: self.tw.canvas.fillscreen(x, y)) + define_logo_function('tasetbackground', 'to tasetbackground :color \ +:shade\rtasetshade :shade\rsetbackground :color\rend\r') + + palette.add_block('pensize', + style='box-style', + label=_('pen size'), + help_string=_('holds current pen size (can be used \ +in place of a number block)'), + value_block=True, + prim_name='pensize', + logo_command='pensize') + self.tw.lc.def_prim('pensize', 0, lambda self: self.tw.canvas.pensize) + define_logo_function('tapensize', 'to tapensize\routput first round \ +pensize\rend\r') + + palette.add_block('startfill', + style='basic-style-extended-vertical', + label=_('start fill'), + prim_name='startfill', + help_string=_('starts filled polygon (used with end \ +fill block)')) + self.tw.lc.def_prim('startfill', 0, + lambda self: self.tw.canvas.start_fill()) + + palette.add_block('stopfill', + style='basic-style-extended-vertical', + label=_('end fill'), + prim_name='stopfill', + help_string=_('completes filled polygon (used with \ +start fill block)')) + self.tw.lc.def_prim('stopfill', 0, + lambda self: self.tw.canvas.stop_fill()) + + def _color_palette(self): + """ The basic Turtle Art color palette """ + + palette = make_palette('colors', + colors=["#00FFFF", "#00A0A0"], + help_string=_('Palette of pen colors')) + + palette.add_block('setcolor', + style='basic-style-1arg', + label=_('set color'), + prim_name='setcolor', + default=0, + logo_command='tasetpencolor', + help_string=_('sets color of the line drawn by the \ +turtle')) + self.tw.lc.def_prim('setcolor', 1, + lambda self, x: primitive_dictionary['set']( + 'color', self.tw.canvas.setcolor, x)) + + palette.add_block('setshade', + style='basic-style-1arg', + label=_('set shade'), + prim_name='setshade', + default=50, + logo_command='tasetshade', + help_string=_('sets shade of the line drawn by the \ +turtle')) + self.tw.lc.def_prim('setshade', 1, + lambda self, x: primitive_dictionary['set']( + 'shade', self.tw.canvas.setshade, x)) + + palette.add_block('setgray', + style='basic-style-1arg', + label=_('set gray'), + prim_name='setgray', + default=100, + help_string=_('sets gray level of the line drawn by \ +the turtle')) + self.tw.lc.def_prim('setgray', 1, + lambda self, x: primitive_dictionary['set']( + 'gray', self.tw.canvas.setgray, x)) + + palette.add_block('color', + style='box-style', + label=_('color'), + help_string=_('holds current pen color (can be used \ +in place of a number block)'), + value_block=True, + prim_name='color', + logo_command='pencolor') + self.tw.lc.def_prim('color', 0, lambda self: self.tw.canvas.color) + + palette.add_block('shade', + style='box-style', + label=_('shade'), + help_string=_('holds current pen shade'), + value_block=True, + prim_name='shade', + logo_command=':shade') + self.tw.lc.def_prim('shade', 0, lambda self: self.tw.canvas.shade) + + palette.add_block('gray', + style='box-style', + label=_('gray'), + help_string=_('holds current gray level (can be used \ +in place of a number block)'), + value_block=True, + prim_name='gray') + self.tw.lc.def_prim('gray', 0, lambda self: self.tw.canvas.gray) + + self._make_constant(palette, 'red', CONSTANTS['red']) + self._make_constant(palette, 'orange', CONSTANTS['orange']) + self._make_constant(palette, 'yellow', CONSTANTS['yellow']) + self._make_constant(palette, 'green', CONSTANTS['green']) + self._make_constant(palette, 'cyan', CONSTANTS['cyan']) + self._make_constant(palette, 'blue', CONSTANTS['blue']) + self._make_constant(palette, 'purple', CONSTANTS['purple']) + self._make_constant(palette, 'white', WHITE) + self._make_constant(palette, 'black', BLACK) + + # deprecated blocks + palette.add_block('settextcolor', + hidden=True, + style='basic-style-1arg', + label=_('set text color'), + prim_name='settextcolor', + default=0, + help_string=_('sets color of text drawn by the \ +turtle')) + self.tw.lc.def_prim('settextcolor', 1, + lambda self, x: self.tw.canvas.settextcolor(x)) + + palette.add_block('settextsize', + hidden=True, + style='basic-style-1arg', + label=_('set text size'), + prim_name='settextsize', + default=0, + help_string=_('sets size of text drawn by the \ +turtle')) + self.tw.lc.def_prim('settextsize', 1, + lambda self, x: self.tw.canvas.settextsize(x)) + + # In order to map Turtle Art colors to the standard UCB Logo palette, + # we need to define a somewhat complex set of functions. + define_logo_function('tacolor', '\ +to tasetpalette :i :r :g :b :myshade \r\ +make "s ((:myshade - 50) / 50) \r\ +ifelse lessp :s 0 [ \r\ +make "s (1 + (:s *0.8)) \r\ +make "r (:r * :s) \r\ +make "g (:g * :s) \r\ +make "b (:b * :s) \r\ +] [ \ +make "s (:s * 0.9) \r\ +make "r (:r + ((99-:r) * :s)) \r\ +make "g (:g + ((99-:g) * :s)) \r\ +make "b (:b + ((99-:b) * :s)) \r\ +] \ +setpalette :i (list :r :g :b) \r\ +end \r\ +\ +to rgb :myi :mycolors :myshade \r\ +make "myr first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +make "myg first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +make "myb first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +tasetpalette :myi :myr :myg :myb :myshade \r\ +output :mycolors \r\ +end \r\ +\ +to processcolor :mycolors :myshade \r\ +if emptyp :mycolors [stop] \r\ +make "i :i + 1 \r\ +processcolor (rgb :i :mycolors :myshade) :myshade \r\ +end \r\ +\ +to tasetshade :shade \r\ +make "myshade modulo :shade 200 \r\ +if greaterp :myshade 99 [make "myshade (199-:myshade)] \r\ +make "i 7 \r\ +make "mycolors :colors \r\ +processcolor :mycolors :myshade \r\ +end \r\ +\ +to tasetpencolor :c \r\ +make "color (modulo (round :c) 100) \r\ +setpencolor :color + 8 \r\ +end \r\ +\ +make "colors [ \ +99 0 0 99 5 0 99 10 0 99 15 0 99 20 0 \ +99 25 0 99 30 0 99 35 0 99 40 0 99 45 0 \ +99 50 0 99 55 0 99 60 0 99 65 0 99 70 0 \ +99 75 0 99 80 0 99 85 0 99 90 0 99 95 0 \ +99 99 0 90 99 0 80 99 0 70 99 0 60 99 0 \ +50 99 0 40 99 0 30 99 0 20 99 0 10 99 0 \ + 0 99 0 0 99 5 0 99 10 0 99 15 0 99 20 \ + 0 99 25 0 99 30 0 99 35 0 99 40 0 99 45 \ + 0 99 50 0 99 55 0 99 60 0 99 65 0 99 70 \ + 0 99 75 0 99 80 0 99 85 0 99 90 0 99 95 \ + 0 99 99 0 95 99 0 90 99 0 85 99 0 80 99 \ + 0 75 99 0 70 99 0 65 99 0 60 99 0 55 99 \ + 0 50 99 0 45 99 0 40 99 0 35 99 0 30 99 \ + 0 25 99 0 20 99 0 15 99 0 10 99 0 5 99 \ + 0 0 99 5 0 99 10 0 99 15 0 99 20 0 99 \ +25 0 99 30 0 99 35 0 99 40 0 99 45 0 99 \ +50 0 99 55 0 99 60 0 99 65 0 99 70 0 99 \ +75 0 99 80 0 99 85 0 99 90 0 99 95 0 99 \ +99 0 99 99 0 90 99 0 80 99 0 70 99 0 60 \ +99 0 50 99 0 40 99 0 30 99 0 20 99 0 10] \r\ +make "shade 50 \r\ +tasetshade :shade \r') + + def _numbers_palette(self): + """ The basic Turtle Art numbers palette """ + + palette = make_palette('numbers', + colors=["#FF00FF", "#A000A0"], + help_string=_('Palette of numeric operators')) + + primitive_dictionary['plus'] = self._prim_plus + palette.add_block('plus2', + style='number-style', + label='+', + special_name=_('plus'), + prim_name='plus', + logo_command='sum', + help_string=_('adds two alphanumeric inputs')) + self.tw.lc.def_prim( + 'plus', 2, lambda self, x, y: primitive_dictionary['plus'](x, y)) + + primitive_dictionary['minus'] = self._prim_minus + palette.add_block('minus2', + style='number-style-porch', + label='–', + special_name=_('minus'), + prim_name='minus', + logo_command='taminus', + help_string=_('subtracts bottom numeric input from \ +top numeric input')) + self.tw.lc.def_prim( + 'minus', 2, lambda self, x, y: primitive_dictionary['minus'](x, y)) + define_logo_function('taminus', 'to taminus :y :x\routput sum :x minus \ +:y\rend\r') + + primitive_dictionary['product'] = self._prim_product + palette.add_block('product2', + style='number-style', + label='×', + special_name=_('multiply'), + prim_name='product', + logo_command='product', + help_string=_('multiplies two numeric inputs')) + self.tw.lc.def_prim( + 'product', 2, + lambda self, x, y: primitive_dictionary['product'](x, y)) + + primitive_dictionary['division'] = self._prim_careful_divide + palette.add_block('division2', + style='number-style-porch', + label='/', + special_name=_('divide'), + prim_name='division', + logo_command='quotient', + help_string=_('divides top numeric input (numerator) \ +by bottom numeric input (denominator)')) + self.tw.lc.def_prim( + 'division', 2, + lambda self, x, y: primitive_dictionary['division'](x, y)) + + primitive_dictionary['id'] = self._prim_identity + palette.add_block('identity2', + style='number-style-1arg', + label='←', + special_name=_('identity'), + prim_name='id', + help_string=_('identity operator used for extending \ +blocks')) + self.tw.lc.def_prim('id', 1, + lambda self, x: primitive_dictionary['id'](x)) + + primitive_dictionary['remainder'] = self._prim_mod + palette.add_block('remainder2', + style='number-style-porch', + label=_('mod'), + special_name=_('mod'), + prim_name='remainder', + logo_command='remainder', + help_string=_('modular (remainder) operator')) + self.tw.lc.def_prim('remainder', 2, + lambda self, x, y: primitive_dictionary['remainder'](x, y)) + + primitive_dictionary['sqrt'] = self._prim_sqrt + palette.add_block('sqrt', + style='number-style-1arg', + label=_('√'), + special_name=_('square root'), + prim_name='sqrt', + logo_command='tasqrt', + help_string=_('calculates square root')) + self.tw.lc.def_prim('sqrt', 1, + lambda self, x: primitive_dictionary['sqrt'](x)) + + primitive_dictionary['random'] = self._prim_random + palette.add_block('random', + style='number-style-block', + label=[_('random'), _('min'), _('max')], + default=[0, 100], + prim_name='random', + logo_command='tarandom', + help_string=_('returns random number between minimum \ +(top) and maximum (bottom) values')) + self.tw.lc.def_prim( + 'random', 2, lambda self, x, y: primitive_dictionary['random']( + x, y)) + define_logo_function('tarandom', 'to tarandom :min :max\r \ +output (random (:max - :min)) + :min\rend\r') + + palette.add_block('number', + style='box-style', + label='100', + default=100, + special_name=_('number'), + help_string=_('used as numeric input in mathematic \ +operators')) + + primitive_dictionary['more'] = self._prim_more + palette.add_block('greater2', + style='compare-porch-style', + label='>', + special_name=_('greater than'), + prim_name='greater?', + logo_command='greater?', + help_string=_('logical greater-than operator')) + self.tw.lc.def_prim( + 'greater?', 2, lambda self, x, y: primitive_dictionary['more'](x, y)) + + primitive_dictionary['less'] = self._prim_less + palette.add_block('less2', + style='compare-porch-style', + label='<', + special_name=_('less than'), + prim_name='less?', + logo_command='less?', + help_string=_('logical less-than operator')) + self.tw.lc.def_prim( + 'less?', 2, lambda self, x, y: primitive_dictionary['less'](x, y)) + + primitive_dictionary['equal'] = self._prim_equal + palette.add_block('equal2', + style='compare-style', + label='=', + special_name=_('equal'), + prim_name='equal?', + logo_command='equal?', + help_string=_('logical equal-to operator')) + self.tw.lc.def_prim( + 'equal?', 2, lambda self, x, y: primitive_dictionary['equal'](x, y)) + + palette.add_block('not', + style='not-style', + label=_('not'), + prim_name='not', + logo_command='not', + help_string=_('logical NOT operator')) + self.tw.lc.def_prim('not', 1, lambda self, x: not x) + + primitive_dictionary['and'] = self._prim_and + palette.add_block('and2', + style='boolean-style', + label=_('and'), + prim_name='and', + logo_command='and', + special_name=_('and'), + help_string=_('logical AND operator')) + self.tw.lc.def_prim( + 'and', 2, lambda self, x, y: primitive_dictionary['and'](x, y)) + + primitive_dictionary['or'] = self._prim_or + palette.add_block('or2', + style='boolean-style', + label=_('or'), + prim_name='or', + logo_command='or', + special_name=_('or'), + help_string=_('logical OR operator')) + self.tw.lc.def_prim( + 'or', 2, lambda self, x, y: primitive_dictionary['or'](x, y)) + + def _flow_palette(self): + """ The basic Turtle Art flow palette """ + + palette = make_palette('flow', + colors=["#FFC000", "#A08000"], + help_string=_('Palette of flow operators')) + + primitive_dictionary['wait'] = self._prim_wait + palette.add_block('wait', + style='basic-style-1arg', + label=_('wait'), + prim_name='wait', + default=1, + logo_command='wait', + help_string=_('pauses program execution a specified \ +number of seconds')) + self.tw.lc.def_prim('wait', 1, primitive_dictionary['wait'], True) + + primitive_dictionary['forever'] = self._prim_forever + palette.add_block('forever', + style='flow-style', + label=_('forever'), + prim_name='forever', + default=[None, 'vspace'], + logo_command='forever', + help_string=_('loops forever')) + self.tw.lc.def_prim('forever', 1, primitive_dictionary['forever'], True) + + primitive_dictionary['repeat'] = self._prim_repeat + palette.add_block('repeat', + style='flow-style-1arg', + label=[' ', _('repeat')], + prim_name='repeat', + default=[4, None, 'vspace'], + logo_command='repeat', + special_name=_('repeat'), + help_string=_('loops specified number of times')) + self.tw.lc.def_prim('repeat', 2, primitive_dictionary['repeat'], True) + + primitive_dictionary['if'] = self._prim_if + palette.add_block('if', + style='flow-style-boolean', + label=[' ', _('if'), _('then')], + prim_name='if', + default=[None, None, 'vspace'], + special_name=_('if then'), + logo_command='if', + help_string=_('if-then operator that uses boolean \ +operators from Numbers palette')) + self.tw.lc.def_prim('if', 2, primitive_dictionary['if'], True) + + primitive_dictionary['ifelse'] = self._prim_ifelse + palette.add_block('ifelse', + style='flow-style-else', + label=[' ', _('if'), _('then else')], + prim_name='ifelse', + default=[None, 'vspace', None, 'vspace'], + logo_command='ifelse', + special_name=_('if then else'), + help_string=_('if-then-else operator that uses \ +boolean operators from Numbers palette')) + self.tw.lc.def_prim('ifelse', 3, primitive_dictionary['ifelse'], True) + + palette.add_block('hspace', + style='flow-style-tail', + label=' ', + prim_name='nop', + special_name=_('horizontal space'), + help_string=_('jogs stack right')) + self.tw.lc.def_prim('nop', 0, lambda self: None) + + palette.add_block('vspace', + style='basic-style-extended-vertical', + label=' ', + prim_name='nop', + special_name=_('vertical space'), + help_string=_('jogs stack down')) + self.tw.lc.def_prim('nop', 0, lambda self: None) + + primitive_dictionary['stopstack'] = self._prim_stopstack + palette.add_block('stopstack', + style='basic-style-tail', + label=_('stop action'), + prim_name='stopstack', + logo_command='stop', + help_string=_('stops current action')) + self.tw.lc.def_prim('stopstack', 0, + lambda self: primitive_dictionary['stopstack']()) + + def _blocks_palette(self): + """ The basic Turtle Art blocks palette """ + + palette = make_palette('blocks', + colors=["#FFFF00", "#A0A000"], + help_string=_('Palette of variable blocks')) + + primitive_dictionary['start'] = self._prim_start + palette.add_block('start', + style='basic-style-head', + label=_('start'), + prim_name='start', + logo_command='to start\r', + help_string=_('connects action to toolbar run \ +buttons')) + self.tw.lc.def_prim('start', 0, + lambda self: primitive_dictionary['start']()) + + primitive_dictionary['setbox'] = self._prim_setbox + palette.add_block('storeinbox1', + style='basic-style-1arg', + label=_('store in box 1'), + prim_name='storeinbox1', + default=100, + logo_command='make "box1', + help_string=_('stores numeric value in Variable 1')) + self.tw.lc.def_prim('storeinbox1', 1, + lambda self, x: primitive_dictionary['setbox']( + 'box1', None, x)) + + palette.add_block('storeinbox2', + style='basic-style-1arg', + label=_('store in box 2'), + prim_name='storeinbox2', + default=100, + logo_command='make "box2', + help_string=_('stores numeric value in Variable 2')) + self.tw.lc.def_prim('storeinbox2', 1, + lambda self, x: primitive_dictionary['setbox']( + 'box2', None, x)) + + palette.add_block('string', + style='box-style', + label=_('text'), + default=_('text'), + special_name=_('text'), + help_string=_('string value')) + + palette.add_block('box1', + style='box-style', + label=_('box 1'), + prim_name='box1', + logo_command=':box1', + help_string=_('Variable 1 (numeric value)'), + value_block=True) + self.tw.lc.def_prim('box1', 0, lambda self: self.tw.lc.boxes['box1']) + + palette.add_block('box2', + style='box-style', + label=_('box 2'), + prim_name='box2', + logo_command=':box2', + help_string=_('Variable 2 (numeric value)'), + value_block=True) + self.tw.lc.def_prim('box2', 0, lambda self: self.tw.lc.boxes['box2']) + + primitive_dictionary['box'] = self._prim_box + palette.add_block('box', + style='number-style-1strarg', + label=_('box'), + prim_name='box', + default=_('my box'), + logo_command='box', + help_string=_('named variable (numeric value)')) + self.tw.lc.def_prim('box', 1, + lambda self, x: primitive_dictionary['box'](x)) + + palette.add_block('storein', + style='basic-style-2arg', + label=[_('store in'), _('box'), _('value')], + prim_name='storeinbox', + logo_command='storeinbox', + default=[_('my box'), 100], + help_string=_('stores numeric value in named \ +variable')) + self.tw.lc.def_prim('storeinbox', 2, + lambda self, x, y: primitive_dictionary['setbox']( + 'box3', x, y)) + + palette.add_block('hat', + style='basic-style-head-1arg', + label=_('action'), + prim_name='nop3', + default=_('stack'), + logo_command='to action', + help_string=_('top of nameable action stack')) + self.tw.lc.def_prim('nop3', 1, lambda self, x: None) + + palette.add_block('hat1', + style='basic-style-head', + label=_('action 1'), + prim_name='nop1', + logo_command='to stack1\r', + help_string=_('top of Action 1 stack')) + self.tw.lc.def_prim('nop1', 0, lambda self: None) + + palette.add_block('hat2', + style='basic-style-head', + label=_('action 2'), + prim_name='nop2', + logo_command='to stack2\r', + help_string=_('top of Action 2 stack')) + self.tw.lc.def_prim('nop2', 0, lambda self: None) + + primitive_dictionary['stack'] = self._prim_stack + palette.add_block('stack', + style='basic-style-1arg', + label=_('action'), + prim_name='stack', + logo_command='action', + default=_('action'), + help_string=_('invokes named action stack')) + self.tw.lc.def_prim('stack', 1, primitive_dictionary['stack'], True) + + primitive_dictionary['stack1'] = self._prim_stack1 + palette.add_block('stack1', + style='basic-style-extended-vertical', + label=_('action 1'), + prim_name='stack1', + logo_command='stack1', + help_string=_('invokes Action 1 stack')) + self.tw.lc.def_prim('stack1', 0, primitive_dictionary['stack1'], True) + + primitive_dictionary['stack2'] = self._prim_stack2 + palette.add_block('stack2', + style='basic-style-extended-vertical', + label=_('action 2'), + prim_name='stack2', + logo_command='stack2', + help_string=_('invokes Action 2 stack')) + self.tw.lc.def_prim('stack2', 0, primitive_dictionary['stack2'], True) + + def _trash_palette(self): + """ The basic Turtle Art turtle palette """ + + palette = make_palette('trash', + colors=["#FFFF00", "#A0A000"], + help_string=_('trash')) + + palette.add_block('empty', + style='basic-style-tail', + label=_('empty trash'), + help_string=_('permanently deletes items in trash')) + + palette.add_block('restoreall', + style='basic-style-head', + label=_('restore all'), + help_string=_('restore all blocks from trash')) + + palette.add_block('trashall', + style='basic-style-tail', + label=_('clear all'), + help_string=_('move all blocks to trash')) + + # Block primitives + + def _prim_and(self, x, y): + """ Logical and """ + return x & y + + def _prim_arc(self, cmd, value1, value2): + """ Turtle draws an arc of degree, radius """ + cmd(float(value1), float(value2)) + self.tw.lc.update_label_value( + 'xcor', self.tw.canvas.xcor / self.tw.coord_scale) + self.tw.lc.update_label_value( + 'ycor', self.tw.canvas.ycor / self.tw.coord_scale) + self.tw.lc.update_label_value('heading', self.tw.canvas.heading) + + def _prim_box(self, x): + """ Retrieve value from named box """ + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + try: + return self.tw.lc.boxes['box3' + str(x)] + except KeyError: + raise logoerror("#emptybox") + + def _prim_forever(self, blklist): + """ Do list forever """ + while True: + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + if self.tw.lc.procstop: + break + self.tw.lc.ireturn() + yield True + + def _prim_if(self, boolean, blklist): + """ If bool, do list """ + if boolean: + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + self.tw.lc.ireturn() + yield True + + def _prim_ifelse(self, boolean, list1, list2): + """ If bool, do list1, else do list2 """ + if boolean: + self.tw.lc.ijmp(self.tw.lc.evline, list1[:]) + yield True + else: + self.tw.lc.ijmp(self.tw.lc.evline, list2[:]) + yield True + + def _prim_move(self, cmd, value1, value2=None, pendown=True): + """ Turtle moves by method specified in value1 """ + if value2 is None: + cmd(value1) + else: + cmd(float(value1), float(value2), pendown=pendown) + self.tw.lc.update_label_value('xcor', + self.tw.canvas.xcor / self.tw.coord_scale) + self.tw.lc.update_label_value('ycor', + self.tw.canvas.ycor / self.tw.coord_scale) + + def _prim_or(self, x, y): + """ Logical or """ + return x | y + + def _prim_repeat(self, num, blklist): + """ Repeat list num times. """ + num = self.tw.lc.int(num) + for i in range(num): + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + if self.tw.lc.procstop: + break + self.tw.lc.ireturn() + yield True + + def _prim_right(self, value): + """ Turtle rotates clockwise """ + self.tw.canvas.right(float(value)) + self.tw.lc.update_label_value('heading', self.tw.canvas.heading) + + def _prim_set(self, name, cmd, value=None): + """ Set a value and update the associated value blocks """ + if value is not None: + cmd(value) + self.tw.lc.update_label_value(name, value) + + def _prim_setbox(self, name, x, val): + """ Define value of named box """ + if x is not None: + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + self.tw.lc.boxes[name + str(x)] = val + return + + self.tw.lc.boxes[name] = val + self.tw.lc.update_label_value(name, val) + + def _prim_stack(self, x): + """ Process a named stack """ + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + if 'stack3' + str(x) not in self.tw.lc.stacks or \ + self.tw.lc.stacks['stack3' + str(x)] is None: + raise logoerror("#nostack") + self.tw.lc.icall(self.tw.lc.evline, + self.tw.lc.stacks['stack3' + str(x)][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_stack1(self): + """ Process Stack 1 """ + if self.tw.lc.stacks['stack1'] is None: + raise logoerror("#nostack") + self.tw.lc.icall(self.tw.lc.evline, + self.tw.lc.stacks['stack1'][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_stack2(self): + """ Process Stack 2 """ + if self.tw.lc.stacks['stack2'] is None: + raise logoerror("#nostack") + self.tw.lc.icall(self.tw.lc.evline, self.tw.lc.stacks['stack2'][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_start(self): + """ Start block: recenter """ + if self.tw.running_sugar: + self.tw.activity.recenter() + + def _prim_stopstack(self): + """ Stop execution of a stack """ + self.tw.lc.procstop = True + + def _prim_wait(self, wait_time): + """ Show the turtle while we wait """ + self.tw.active_turtle.show() + endtime = _millisecond() + wait_time * 1000. + while _millisecond() < endtime: + yield True + self.tw.active_turtle.hide() + self.tw.lc.ireturn() + yield True + + # Math primitivies + + def _prim_careful_divide(self, x, y): + """ Raise error on divide by zero """ + try: + return x / y + except ZeroDivisionError: + raise logoerror("#zerodivide") + except TypeError: + try: + return self._string_to_num(x) / self._string_to_num(y) + except ZeroDivisionError: + raise logoerror("#zerodivide") + except ValueError: + raise logoerror("#syntaxerror") + except TypeError: + raise logoerror("#notanumber") + + def _prim_equal(self, x, y): + """ Numeric and logical equal """ + try: + return float(x) == float(y) + except TypeError: + typex, typey = False, False + if strtype(x): + typex = True + if strtype(y): + typey = True + if typex and typey: + return x == y + try: + return self._string_to_num(x) == self._string_to_num(y) + except ValueError: + raise logoerror("#syntaxerror") + + def _prim_less(self, x, y): + """ Compare numbers and strings """ + try: + return float(x) < float(y) + except ValueError: + typex, typey = False, False + if strtype(x): + typex = True + if strtype(y): + typey = True + if typex and typey: + return x < y + try: + return self._string_to_num(x) < self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_more(self, x, y): + """ Compare numbers and strings """ + return self._prim_less(y, x) + + def _prim_plus(self, x, y): + """ Add numbers, concat strings """ + if _num_type(x) and _num_type(y): + return(x + y) + else: + if _num_type(x): + xx = str(round_int(x)) + else: + xx = str(x) + if _num_type(y): + yy = str(round_int(y)) + else: + yy = str(y) + return(xx + yy) + + def _prim_minus(self, x, y): + """ Numerical subtraction """ + if _num_type(x) and _num_type(y): + return(x - y) + try: + return self._string_to_num(x) - self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_product(self, x, y): + """ Numerical multiplication """ + if _num_type(x) and _num_type(y): + return(x * y) + try: + return self._string_to_num(x) * self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_mod(self, x, y): + """ Numerical mod """ + if _num_type(x) and _num_type(y): + return(x % y) + try: + return self._string_to_num(x) % self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + except ValueError: + raise logoerror("#syntaxerror") + + def _prim_sqrt(self, x): + """ Square root """ + if _num_type(x): + if x < 0: + raise logoerror("#negroot") + return sqrt(x) + try: + return sqrt(self._string_to_num(x)) + except ValueError: + raise logoerror("#negroot") + except TypeError: + raise logoerror("#notanumber") + + def _prim_random(self, x, y): + """ Random integer """ + if _num_type(x) and _num_type(y): + return(int(round(uniform(x, y), 0))) + xx, xflag = chr_to_ord(x) + yy, yflag = chr_to_ord(y) + if xflag and yflag: + return chr(int(round(uniform(xx, yy), 0))) + if not xflag: + xx = self._string_to_num(x) + if not yflag: + yy = self._string_to_num(y) + try: + return(int(round(uniform(xx, yy), 0))) + except TypeError: + raise logoerror("#notanumber") + + def _prim_identity(self, x): + """ Identity function """ + return(x) + + # Utilities + + def _string_to_num(self, x): + """ Try to comvert a string to a number """ + if type(x) is float: + return(x) + if type(x) is int: + return(x) + if type(x) is ord: + return(int(x)) + xx = convert(x.replace(self.tw.decimal_point, '.'), float) + if type(xx) is float: + return xx + else: + xx, xflag = chr_to_ord(x) + if xflag: + return xx + else: + raise logoerror("#syntaxerror") + + def _make_constant(self, palette, block_name, constant): + """ Factory for constant blocks """ + palette.add_block(block_name, style='box-style', + label=_(block_name), prim_name=block_name, + logo_command=block_name) + self.tw.lc.def_prim(block_name, 0, lambda self: constant) diff --git a/TurtleArt/tablock.py b/TurtleArt/tablock.py index 05b95ae..bcca2cd 100644 --- a/TurtleArt/tablock.py +++ b/TurtleArt/tablock.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#Copyright (c) 2010 Walter Bender +#Copyright (c) 2010,11 Walter Bender #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -20,31 +20,21 @@ #THE SOFTWARE. import gtk - from gettext import gettext as _ -from taconstants import EXPANDABLE, EXPANDABLE_BLOCKS, EXPANDABLE_ARGS, \ - PRIMITIVES, OLD_NAMES, BLOCK_SCALE, BLOCK_NAMES, CONTENT_BLOCKS, \ - PALETTES, COLORS, BASIC_STYLE_HEAD, BASIC_STYLE_HEAD_1ARG, \ - BASIC_STYLE_TAIL, BASIC_STYLE, BASIC_STYLE_EXTENDED, BASIC_STYLE_1ARG, \ - BASIC_STYLE_VAR_ARG, BULLET_STYLE, BASIC_STYLE_2ARG, BOX_STYLE, \ - BOX_STYLE_MEDIA, NUMBER_STYLE, NUMBER_STYLE_VAR_ARG, NUMBER_STYLE_BLOCK, \ - NUMBER_STYLE_PORCH, NUMBER_STYLE_1ARG, NUMBER_STYLE_1STRARG, \ - COMPARE_STYLE, BOOLEAN_STYLE, NOT_STYLE, FLOW_STYLE, FLOW_STYLE_TAIL, \ - FLOW_STYLE_1ARG, FLOW_STYLE_BOOLEAN, FLOW_STYLE_WHILE, FLOW_STYLE_ELSE, \ - COLLAPSIBLE_TOP, COLLAPSIBLE_TOP_NO_ARM, COLLAPSIBLE_TOP_NO_LABEL, \ - COLLAPSIBLE_TOP_NO_ARM_NO_LABEL, COLLAPSIBLE_BOTTOM, PORTFOLIO_STYLE_2x2, \ - PORTFOLIO_STYLE_1x1, PORTFOLIO_STYLE_2x1, PORTFOLIO_STYLE_1x2, \ - STANDARD_STROKE_WIDTH, BOX_COLORS, GRADIENT_COLOR, \ - BASIC_STYLE_EXTENDED_VERTICAL, CONSTANTS, INVISIBLE +from taconstants import EXPANDABLE, EXPANDABLE_ARGS, OLD_NAMES, CONSTANTS, \ + STANDARD_STROKE_WIDTH, BLOCK_SCALE, BOX_COLORS, GRADIENT_COLOR +from tapalette import palette_blocks, block_colors, expandable_blocks, \ + content_blocks, block_names, block_primitives, block_styles, \ + special_block_colors from tasprite_factory import SVG, svg_str_to_pixbuf import sprites -import logging -_logger = logging.getLogger('turtleart-activity') +from tautils import debug_output, error_output class Blocks: + """ A class for the list of blocks and everything they share in common """ def __init__(self, font_scale_factor=1, decimal_point='.'): @@ -130,7 +120,9 @@ class Block: """ A class for the individual blocks """ def __init__(self, block_list, sprite_list, name, x, y, type='block', - values=[], scale=BLOCK_SCALE, colors=['#FF0000', '#A00000']): + values=[], scale=BLOCK_SCALE[0], + colors=['#A0A0A0', '#808080']): + self.block_list = block_list self.spr = None self.shapes = [None, None] @@ -150,6 +142,51 @@ class Block: self._font_size = [6.0, 4.5] self._image = None + self.block_methods = { + 'basic-style': self._make_basic_style, + 'blank-style': self._make_blank_style, + 'basic-style-head': self._make_basic_style_head, + 'basic-style-head-1arg': self._make_basic_style_head_1arg, + 'basic-style-tail': self._make_basic_style_tail, + 'basic-style-extended': [self._make_basic_style, 16, 16], + 'basic-style-extended-vertical': [self._make_basic_style, 0, 4], + 'basic-style-1arg': self._make_basic_style_1arg, + 'basic-style-2arg': self._make_basic_style_2arg, + 'basic-style-3arg': self._make_basic_style_3arg, + 'basic-style-var-arg': self._make_basic_style_var_arg, + 'invisible': self._make_invisible_style, + 'bullet-style': self._make_bullet_style, + 'box-style': self._make_box_style, + 'box-style-media': self._make_media_style, + 'number-style': self._make_number_style, + 'number-style-block': self._make_number_style_block, + 'number-style-porch': self._make_number_style_porch, + 'number-style-1arg': self._make_number_style_1arg, + 'number-style-1strarg': self._make_number_style_1strarg, + 'number-style-var-arg': self._make_number_style_var_arg, + 'compare-style': self._make_compare_style, + 'compare-porch-style': self._make_compare_porch_style, + 'boolean-style': self._make_boolean_style, + 'not-style': self._make_not_style, + 'flow-style': self._make_flow_style, + 'flow-style-tail': self._make_flow_style_tail, + 'flow-style-1arg': self._make_flow_style_1arg, + 'flow-style-boolean': self._make_flow_style_boolean, + 'flow-style-while': self._make_flow_style_while, + 'flow-style-else': self._make_flow_style_else, + 'collapsible-top': [self._make_collapsible_style_top, True, True], + 'collapsible-top-no-arm': [self._make_collapsible_style_top, + False, True], + 'collapsible-top-no-label': [self._make_collapsible_style_top, + True, False], + 'collapsible-top-no-arm-no-label': [ + self._make_collapsible_style_top, False, False], + 'collapsible-bottom': self._make_collapsible_style_bottom, + 'portfolio-style-2x2': self._make_portfolio_style_2x2, + 'portfolio-style-1x1': self._make_portfolio_style_1x1, + 'portfolio-style-2x1': self._make_portfolio_style_2x1, + 'portfolio-style-1x2': self._make_portfolio_style_1x2} + if self.name in OLD_NAMES: self.name = OLD_NAMES[self.name] @@ -162,22 +199,38 @@ class Block: # If there is already a block with the same name, reuse it copy_block = None - if self.name not in EXPANDABLE and \ - self.name not in EXPANDABLE_BLOCKS and \ - self.name not in EXPANDABLE_ARGS and \ - self.name not in BOX_STYLE and \ - self.name not in ['sandwichtop', 'sandwichtop_no_label']: + if self.cloneable(): for b in self.block_list.list: if b.scale == self.scale and b.name == self.name: copy_block = b break self._new_block_from_factory(sprite_list, x, y, copy_block) - if name in PRIMITIVES: - self.primitive = PRIMITIVES[self.name] + if name in block_primitives: + self.primitive = block_primitives[self.name] self.block_list.append_to_list(self) + def expandable(self): + """ Can this block be expanded? """ + if self.name in EXPANDABLE: + return True + if self.name in expandable_blocks: + return True + if self.name in EXPANDABLE_ARGS: + return True + return False + + def cloneable(self): + """ Is it safe to clone this block? """ + if self.expandable(): + return False + if self.name in block_styles['box-style']: + return False + if self.name in ['sandwichtop', 'sandwichtop_no_label']: + return False + return True + def highlight(self): """ We may want to highlight a block... """ if self.spr is not None: @@ -310,7 +363,6 @@ class Block: return (self.ex, self.ey) def _new_block_from_factory(self, sprite_list, x, y, copy_block=None): - self.svg = SVG() self.svg.set_scale(self.scale) self.svg.set_innie([False]) @@ -335,10 +387,7 @@ class Block: self.shapes[1] = copy_block.shapes[1] self.docks = copy_block.docks[:] else: - if (self.name in EXPANDABLE or \ - self.name in EXPANDABLE_BLOCKS or \ - self.name in EXPANDABLE_ARGS) and \ - self.type == 'block': + if self.expandable() and self.type == 'block': self.svg.set_show(True) self._make_block(self.svg) @@ -358,11 +407,11 @@ class Block: else: self._set_labels(i, str(v)) elif self.type == 'block' and self.name in CONSTANTS: - self._set_labels(0, BLOCK_NAMES[self.name][0] + ' = ' + \ + self._set_labels(0, block_names[self.name][0] + ' = ' + \ str(CONSTANTS[self.name])) - elif self.name in BLOCK_NAMES: - for i, n in enumerate(BLOCK_NAMES[self.name]): + elif self.name in block_names: + for i, n in enumerate(block_names[self.name]): self._set_labels(i, n) if copy_block is None: @@ -374,18 +423,24 @@ class Block: self.svg.margins[2], self.svg.margins[3]) def _set_label_attributes(self): - if self.name in CONTENT_BLOCKS: + if self.name in content_blocks: n = len(self.values) if n == 0: n = 1 # Force a scale to be set, even if there is no value. else: - if self.name in BLOCK_NAMES: - n = len(BLOCK_NAMES[self.name]) + if self.name in block_names: + n = len(block_names[self.name]) else: - _logger.debug('WARNING: unknown block name %s' % (self.name)) + debug_output('WARNING: unknown block name %s' % (self.name)) n = 0 for i in range(n): - if i == 1: # top + if self.name in block_styles['compare-porch-style']: + self.spr.set_label_attributes(int(self._font_size[0] + 0.5), + True, 'center', 'bottom', i) + elif self.name in block_styles['number-style-porch']: + self.spr.set_label_attributes(int(self._font_size[0] + 0.5), + True, 'right', 'bottom', i) + elif i == 1: # top self.spr.set_label_attributes(int(self._font_size[1] + 0.5), True, 'right', 'top', i) elif i == 2: # bottom @@ -405,92 +460,26 @@ class Block: self._bottom = 0 self.svg.set_stroke_width(STANDARD_STROKE_WIDTH) self.svg.clear_docks() - if self.name in BASIC_STYLE: - self._make_basic_style(svg) - elif self.name in BASIC_STYLE_HEAD: - self._make_basic_style_head(svg) - elif self.name in BASIC_STYLE_EXTENDED: - self._make_basic_style(svg, 16, 16) - elif self.name in BASIC_STYLE_EXTENDED_VERTICAL: - self._make_basic_style(svg, 0, 4) - elif self.name in BASIC_STYLE_HEAD_1ARG: - self._make_basic_style_head_1arg(svg) - elif self.name in BASIC_STYLE_TAIL: - self._make_basic_style_tail(svg) - elif self.name in BASIC_STYLE_1ARG: - self._make_basic_style_1arg(svg) - elif self.name in BASIC_STYLE_2ARG: - self._make_basic_style_2arg(svg) - elif self.name in BASIC_STYLE_VAR_ARG: - self._make_basic_style_var_arg(svg) - elif self.name in BULLET_STYLE: - self._make_bullet_style(svg) - elif self.name in BOX_STYLE: - self._make_box_style(svg) - elif self.name in BOX_STYLE_MEDIA: - self._make_media_style(svg) - elif self.name in NUMBER_STYLE: - self._make_number_style(svg) - elif self.name in NUMBER_STYLE_BLOCK: - self._make_number_style_block(svg) - elif self.name in NUMBER_STYLE_VAR_ARG: - self._make_number_style_var_arg(svg) - elif self.name in NUMBER_STYLE_1ARG: - self._make_number_style_1arg(svg) - elif self.name in NUMBER_STYLE_1STRARG: - self._make_number_style_1strarg(svg) - elif self.name in NUMBER_STYLE_PORCH: - self._make_number_style_porch(svg) - elif self.name in COMPARE_STYLE: - self._make_compare_style(svg) - elif self.name in BOOLEAN_STYLE: - self._make_boolean_style(svg) - elif self.name in NOT_STYLE: - self._make_not_style(svg) - elif self.name in FLOW_STYLE: - self._make_flow_style(svg) - elif self.name in FLOW_STYLE_TAIL: - self._make_flow_style_tail(svg) - elif self.name in FLOW_STYLE_1ARG: - self._make_flow_style_1arg(svg) - elif self.name in FLOW_STYLE_BOOLEAN: - self._make_flow_style_boolean(svg) - elif self.name in FLOW_STYLE_WHILE: - self._make_flow_style_while(svg) - elif self.name in FLOW_STYLE_ELSE: - self._make_flow_style_else(svg) - elif self.name in COLLAPSIBLE_TOP: - self._make_collapsible_style_top(svg, arm=True, label=True) - elif self.name in COLLAPSIBLE_TOP_NO_ARM: - self._make_collapsible_style_top(svg, arm=False, label=True) - elif self.name in COLLAPSIBLE_TOP_NO_LABEL: - self._make_collapsible_style_top(svg, arm=True, label=False) - elif self.name in COLLAPSIBLE_TOP_NO_ARM_NO_LABEL: - self._make_collapsible_style_top(svg, arm=False, label=False) - elif self.name in COLLAPSIBLE_BOTTOM: - self._make_collapsible_style_bottom(svg) - elif self.name in INVISIBLE: - self._make_invisible_style(svg) - elif self.name in PORTFOLIO_STYLE_2x2: - self._make_portfolio_style_2x2(svg) - elif self.name in PORTFOLIO_STYLE_2x1: - self._make_portfolio_style_2x1(svg) - elif self.name in PORTFOLIO_STYLE_1x1: - self._make_portfolio_style_1x1(svg) - elif self.name in PORTFOLIO_STYLE_1x2: - self._make_portfolio_style_1x2(svg) - else: - self._make_basic_style(svg) - _logger.debug("WARNING: I don't know how to create a %s block" % \ - (self.name)) + for k in block_styles.keys(): + if self.name in block_styles[k]: + if type(self.block_methods[k]) == type([]): + self.block_methods[k][0](svg, self.block_methods[k][1], + self.block_methods[k][2]) + else: + self.block_methods[k](svg) + return + error_output('block type not found %s' % (self.name)) + self.block_methods['basic-style'](svg) def _set_colors(self, svg): if self.name in BOX_COLORS: self.colors = BOX_COLORS[self.name] + elif self.name in special_block_colors: + self.colors = special_block_colors[self.name] else: - for p in range(len(PALETTES)): - if self.name in PALETTES[p]: - self.colors = COLORS[p] + for p in range(len(palette_blocks)): + if self.name in palette_blocks[p]: + self.colors = block_colors[p] self.svg.set_colors(self.colors) def _make_basic_style(self, svg, extend_x=0, extend_y=0): @@ -500,6 +489,13 @@ class Block: self.svg.docks[0][1]], ['flow', False, self.svg.docks[1][0], self.svg.docks[1][1]]] + def _make_blank_style(self, svg, extend_x=0, extend_y=0): + self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y) + self.svg.set_slot(False) + self.svg.set_tab(False) + self._make_block_graphics(svg, self.svg.basic_block) + self.docks = [] + def _make_basic_style_head(self, svg): self.svg.expand(10 + self.dx + self.ex, self.ey) self.svg.set_slot(False) @@ -553,6 +549,21 @@ class Block: ['flow', False, self.svg.docks[3][0], self.svg.docks[3][1]]] + def _make_basic_style_3arg(self, svg): + self.svg.expand(10 + self.dx + self.ex, self.ey) + self.svg.set_innie([True, True, True]) + self._make_block_graphics(svg, self.svg.basic_block) + self.docks = [['flow', True, self.svg.docks[0][0], + self.svg.docks[0][1]], + ['number', False, self.svg.docks[1][0], + self.svg.docks[1][1]], + ['number', False, self.svg.docks[2][0], + self.svg.docks[2][1]], + ['number', False, self.svg.docks[3][0], + self.svg.docks[3][1]], + ['flow', False, self.svg.docks[4][0], + self.svg.docks[4][1]]] + def _make_basic_style_var_arg(self, svg): self.svg.expand(10 + self.dx + self.ex, self.ey) innie = [True] @@ -706,6 +717,10 @@ class Block: self.svg.docks[2][1]], ['unavailable', False, 0, 0, ')']] + def _make_compare_porch_style(self, svg): + self.svg.set_porch(True) + self._make_compare_style(svg) + def _make_boolean_style(self, svg): self.svg.expand(10 + self.dx + self.ex, self.ey) self._make_block_graphics(svg, self.svg.boolean_and_or) diff --git a/TurtleArt/tacamera.py b/TurtleArt/tacamera.py deleted file mode 100644 index 2177288..0000000 --- a/TurtleArt/tacamera.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -#Copyright (c) 2010, Walter Bender -#Copyright (c) 2010, Tony Forster - -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: - -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. - -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. - -import gst, time - -GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc'] - -class Camera(): - """ A class for representing the video camera """ - - def __init__(self, imagepath): - GST_PIPE.append('filesink location=%s' % imagepath) - self.pipe = gst.parse_launch('!'.join(GST_PIPE)) - self.bus = self.pipe.get_bus() - - def save_camera_input_to_file(self): - """ Grab a frame from the camera """ - self.pipe.set_state(gst.STATE_PLAYING) - self.bus.poll(gst.MESSAGE_EOS, -1) - - def stop_camera_input(self): - self.pipe.set_state(gst.STATE_NULL) - diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py index d4395a2..bf866fb 100644 --- a/TurtleArt/tacanvas.py +++ b/TurtleArt/tacanvas.py @@ -1,5 +1,5 @@ #Copyright (c) 2007-8, Playful Invention Company. -#Copyright (c) 2008-10, Walter Bender +#Copyright (c) 2008-11, Walter Bender #Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/> #Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,15 +24,15 @@ import gtk from math import sin, cos, pi import pango import cairo +import base64 +from gettext import gettext as _ from sprites import Sprite from tasprite_factory import SVG -from tautils import image_to_base64, data_to_string, round_int +from tautils import image_to_base64, get_path, data_to_string, round_int, \ + debug_output from taconstants import CANVAS_LAYER, BLACK, WHITE -import logging -_logger = logging.getLogger('turtleart-activity') - def wrap100(n): """ A variant on mod... 101 -> 99; 199 -> 1 """ @@ -43,6 +43,23 @@ def wrap100(n): return n +def calc_poly_bounds(poly_points): + """ Calculate the minx, miny, width, height of polygon """ + minx = poly_points[0][0] + miny = poly_points[0][1] + maxx, maxy = minx, miny + for p in poly_points: + if p[0] < minx: + minx = p[0] + elif p[0] > maxx: + maxx = p[0] + if p[1] < miny: + miny = p[1] + elif p[1] > maxy: + maxy = p[1] + return(minx, miny, maxx - minx, maxy - miny) + + def calc_shade(c, s, invert=False): """ Convert a color to the current shade (lightness/darkness). """ # Assumes 16 bit input values @@ -119,8 +136,8 @@ class TurtleGraphics: self.fgcolor = self.cm.alloc_color('red') self.bgrgb = [255, 248, 222] self.bgcolor = self.cm.alloc_color('#fff8de') - self.textsize = 48 # depreciated - self.textcolor = self.cm.alloc_color('blue') + self.textsize = 48 # deprecated + self.textcolor = self.cm.alloc_color('red') # deprecated self.tw.active_turtle.show() self.shade = 0 self.pendown = False @@ -128,7 +145,6 @@ class TurtleGraphics: self.ycor = 0 self.heading = 0 self.pensize = 5 - self.tcolor = 0 self.color = 0 self.gray = 100 self.fill = False @@ -142,57 +158,72 @@ class TurtleGraphics: """ Start accumulating points of a polygon to fill. """ self.fill = True self.poly_points = [] + if self.tw.saving_svg: + self.tw.svg_string += '<g>' def stop_fill(self): """ Fill the polygon. """ self.fill = False if len(self.poly_points) == 0: return - minx = self.poly_points[0][0] - miny = self.poly_points[0][1] - maxx = minx - maxy = miny - for p in self.poly_points: - if p[0] < minx: - minx = p[0] - elif p[0] > maxx: - maxx = p[0] - if p[1] < miny: - miny = p[1] - elif p[1] > maxy: - maxy = p[1] - w = maxx - minx - h = maxy - miny - self.canvas.images[0].draw_polygon(self.gc, True, self.poly_points) + self.fill_polygon(self.poly_points) + if self.tw.sharing(): + shared_poly_points = [] + for p in self.poly_points: + shared_poly_points.append((self.screen_to_turtle_coordinates( + p[0], p[1]))) + event = "F|%s" % (data_to_string([self._get_my_nick(), + shared_poly_points])) + self.tw.send_event(event) + self.poly_points = [] + if self.tw.saving_svg: + self.tw.svg_string += '</g>' + + def fill_polygon(self, poly_points): + minx, miny, w, h = calc_poly_bounds(poly_points) + self.canvas.images[0].draw_polygon(self.gc, True, poly_points) self.invalt(minx - self.pensize * self.tw.coord_scale / 2 - 3, miny - self.pensize * self.tw.coord_scale / 2 - 3, w + self.pensize * self.tw.coord_scale + 6, h + self.pensize * self.tw.coord_scale + 6) - self.poly_points = [] + if self.tw.saving_svg and self.pendown: + self.svg.set_fill_color("#%02x%02x%02x" % (self.fgrgb[0], + self.fgrgb[1], + self.fgrgb[2])) + self.tw.svg_string += self.svg.new_path(poly_points[0][0], + poly_points[0][1]) + for p in range(len(poly_points)): + if p > 0: + self.tw.svg_string += self.svg.line_to(poly_points[p][0], + poly_points[p][1]) + self.tw.svg_string += "\"\n" + self.tw.svg_string += self.svg.style() + self.svg.set_fill_color('none') def clearscreen(self, share=True): """Clear the canvas and reset most graphics attributes to defaults.""" - rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) + rect = gtk.gdk.Rectangle(0, 0, self.width * 2, self.height * 2) self.gc.set_foreground(self.bgcolor) self.canvas.images[0].draw_rectangle(self.gc, True, *rect) self.invalt(0, 0, self.width, self.height) self.setpensize(5, share) self.setgray(100, share) self.setcolor(0, share) - self.settextcolor(70) self.setshade(50, share) for turtle_key in iter(self.tw.turtles.dict): - self.set_turtle(turtle_key) - self.tw.active_turtle.set_color(0) - self.tw.active_turtle.set_shade(50) - self.tw.active_turtle.set_gray(100) - self.tw.active_turtle.set_pen_size(5) - self.tw.active_turtle.reset_shapes() - self.seth(0, share) - self.setpen(False, share) - self.setxy(0, 0, share) - self.setpen(True, share) - self.tw.active_turtle.hide() + # Don't reset remote turtles + if not self.tw.remote_turtle(turtle_key): + self.set_turtle(turtle_key) + self.tw.active_turtle.set_color(0) + self.tw.active_turtle.set_shade(50) + self.tw.active_turtle.set_gray(100) + self.tw.active_turtle.set_pen_size(5) + self.tw.active_turtle.reset_shapes() + self.seth(0, share) + self.setpen(False, share) + self.setxy(0, 0, share) + self.setpen(True, share) + self.tw.active_turtle.hide() self.set_turtle(self.tw.default_turtle_name) self.tw.svg_string = '' self.svg.reset_min_max() @@ -208,44 +239,46 @@ class TurtleGraphics: self.xcor += nn * sin(self.heading * DEGTOR) self.ycor += nn * cos(self.heading * DEGTOR) except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return if self.pendown: self.draw_line(oldx, oldy, self.xcor, self.ycor) self.move_turtle() - if self.tw.saving_svg and self.pendown: - self.tw.svg_string += self.svg.new_path(oldx, - self.height / 2 - oldy) - self.tw.svg_string += self.svg.line_to(self.xcor, - self.height / 2 - self.ycor) - self.tw.svg_string += "\"\n" - self.tw.svg_string += self.svg.style() - event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)])) - self._send_event(event, share) + + if self.tw.sharing() and share: + event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)])) + self.tw.send_event(event) def seth(self, n, share=True): """ Set the turtle heading. """ try: self.heading = n except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.heading %= 360 self.turn_turtle() - event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "r|%s" % (data_to_string([self._get_my_nick(), + round_int(self.heading)])) + self.tw.send_event(event) def right(self, n, share=True): """ Rotate turtle clockwise """ try: self.heading += n except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.heading %= 360 self.turn_turtle() - event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "r|%s" % (data_to_string([self._get_my_nick(), + round_int(self.heading)])) + self.tw.send_event(event) def arc(self, a, r, share=True): """ Draw an arc """ @@ -257,11 +290,14 @@ class TurtleGraphics: else: self.rarc(a, rr) except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.move_turtle() - event = "a|%s" % (data_to_string([self._get_my_nick(), [round_int(a), round_int(r)]])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "a|%s" % (data_to_string([self._get_my_nick(), + [round_int(a), round_int(r)]])) + self.tw.send_event(event) def rarc(self, a, r): """ draw a clockwise arc """ @@ -274,8 +310,7 @@ class TurtleGraphics: oldx, oldy = self.xcor, self.ycor cx = self.xcor + r * cos(self.heading * DEGTOR) cy = self.ycor - r * sin(self.heading * DEGTOR) - x = self.width / 2 + int(cx - r) - y = self.height / 2 - int(cy + r) + x, y = self.turtle_to_screen_coordinates(int(cx - r), int(cy + r)) w = int(2 * r) h = w if self.pendown: @@ -289,10 +324,10 @@ class TurtleGraphics: self.xcor = cx - r * cos(self.heading * DEGTOR) self.ycor = cy + r * sin(self.heading * DEGTOR) if self.tw.saving_svg and self.pendown: - self.tw.svg_string += self.svg.new_path(oldx, - self.height / 2 - oldy) - self.tw.svg_string += self.svg.arc_to(self.xcor, - self.height / 2 - self.ycor, r, a, 0, s) + x, y = self.turtle_to_screen_coordinates(oldx, oldy) + self.tw.svg_string += self.svg.new_path(x, y) + x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) + self.tw.svg_string += self.svg.arc_to(x, y, r, a, 0, s) self.tw.svg_string += "\"\n" self.tw.svg_string += self.svg.style() @@ -307,8 +342,7 @@ class TurtleGraphics: oldx, oldy = self.xcor, self.ycor cx = self.xcor - r * cos(self.heading * DEGTOR) cy = self.ycor + r * sin(self.heading * DEGTOR) - x = self.width / 2 + int(cx - r) - y = self.height / 2 - int(cy + r) + x, y = self.turtle_to_screen_coordinates(int(cx - r), int(cy + r)) w = int(2 * r) h = w if self.pendown: @@ -323,11 +357,10 @@ class TurtleGraphics: self.xcor = cx + r * cos(self.heading * DEGTOR) self.ycor = cy - r * sin(self.heading * DEGTOR) if self.tw.saving_svg and self.pendown: - self.tw.svg_string += self.svg.new_path(oldx, - self.height / 2 - oldy) - self.tw.svg_string += self.svg.arc_to(self.xcor, - self.height / 2 - self.ycor, - r, a, 0, s) + x, y = self.turtle_to_screen_coordinates(oldx, oldy) + self.tw.svg_string += self.svg.new_path(x, y) + x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) + self.tw.svg_string += self.svg.arc_to(x, y, r, a, 0, s) self.tw.svg_string += "\"\n" self.tw.svg_string += self.svg.style() @@ -339,16 +372,19 @@ class TurtleGraphics: try: self.xcor, self.ycor = x, y except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return if self.pendown and pendown: self.gc.set_foreground(self.fgcolor) self.draw_line(oldx, oldy, self.xcor, self.ycor) - self.move_turtle() - event = "x|%s" % (data_to_string([self._get_my_nick(), [round_int(x), round_int(y)]])) - self._send_event(event, share) + + if self.tw.sharing() and share: + event = "x|%s" % (data_to_string([self._get_my_nick(), + [round_int(x), round_int(y)]])) + self.tw.send_event(event) def setpensize(self, ps, share=True): """ Set the pen size """ @@ -357,81 +393,85 @@ class TurtleGraphics: ps = 0 self.pensize = ps except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.tw.active_turtle.set_pen_size(ps) self.gc.set_line_attributes(int(self.pensize * self.tw.coord_scale), - gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER) + gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER) self.svg.set_stroke_width(self.pensize) - event = "w|%s" % (data_to_string([self._get_my_nick(), round_int(ps)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "w|%s" % (data_to_string([self._get_my_nick(), + round_int(ps)])) + self.tw.send_event(event) def setcolor(self, c, share=True): """ Set the pen color """ try: self.color = c - self.tcolor = c except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.tw.active_turtle.set_color(c) self.set_fgcolor() - self.set_textcolor() - event = "c|%s" % (data_to_string([self._get_my_nick(), round_int(c)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "c|%s" % (data_to_string([self._get_my_nick(), + round_int(c)])) + self.tw.send_event(event) def setgray(self, g, share=True): """ Set the gray level """ try: self.gray = g except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return if self.gray < 0: self.gray = 0 if self.gray > 100: self.gray = 100 self.set_fgcolor() - self.set_textcolor() self.tw.active_turtle.set_gray(self.gray) - event = "g|%s" % (data_to_string([self._get_my_nick(), round_int(self.gray)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "g|%s" % (data_to_string([self._get_my_nick(), + round_int(self.gray)])) + self.tw.send_event(event) - def settextcolor(self, c): + def settextcolor(self, c): # deprecated """ Set the text color """ - try: - self.tcolor = c - except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) - return - self.set_textcolor() + return - def settextsize(self, c): # depreciated + def settextsize(self, c): # deprecated """ Set the text size """ try: self.tw.textsize = c except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) def setshade(self, s, share=True): """ Set the color shade """ try: self.shade = s except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return self.tw.active_turtle.set_shade(s) self.set_fgcolor() - self.set_textcolor() - event = "s|%s" % (data_to_string([self._get_my_nick(), round_int(s)])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "s|%s" % (data_to_string([self._get_my_nick(), + round_int(s)])) + self.tw.send_event(event) def fillscreen(self, c, s): """ Fill screen with color/shade and reset to defaults """ oldc, olds = self.color, self.shade self.setcolor(c, False) self.setshade(s, False) - rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) + rect = gtk.gdk.Rectangle(0, 0, self.width * 2, self.height * 2) self.gc.set_foreground(self.fgcolor) self.bgrgb = self.fgrgb[:] self.canvas.images[0].draw_rectangle(self.gc, True, *rect) @@ -473,16 +513,17 @@ class TurtleGraphics: def set_textcolor(self): """ Set the text color to foreground color. """ - self.tw.textcolor = self.fgcolor + return def setpen(self, bool, share=True): """ Lower or raise the pen """ self.pendown = bool self.tw.active_turtle.set_pen_state(bool) - event = "p|%s" % (data_to_string([self._get_my_nick(), bool])) - self._send_event(event, share) + if self.tw.sharing() and share: + event = "p|%s" % (data_to_string([self._get_my_nick(), bool])) + self.tw.send_event(event) - def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path): + def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): """ Draw a pixbuf """ w *= self.tw.coord_scale h *= self.tw.coord_scale @@ -492,20 +533,40 @@ class TurtleGraphics: if self.tw.running_sugar: # In Sugar, we need to embed the images inside the SVG self.tw.svg_string += self.svg.image(x - self.width / 2, - y, w, h, path, image_to_base64(pixbuf, self.tw.activity)) + y, w, h, path, image_to_base64(pixbuf, + get_path(self.tw.activity, 'instance'))) else: + # Outside of Sugar, we save a path self.tw.svg_string += self.svg.image(x - self.width / 2, y, w, h, path) - - def draw_text(self, label, x, y, size, w): + if self.tw.sharing() and share: + if self.tw.running_sugar: + tmp_path = get_path(self.tw.activity, 'instance') + else: + tmp_path = '/tmp' + data = image_to_base64(pixbuf, tmp_path) + height = pixbuf.get_height() + width = pixbuf.get_width() + x, y = self.screen_to_turtle_coordinates(x, y) + event = "P|%s" % (data_to_string([self._get_my_nick(), + [round_int(a), round_int(b), + round_int(x), round_int(y), + round_int(w), round_int(h), + round_int(width), + round_int(height), + data]])) + self.tw.send_event(event) + + def draw_text(self, label, x, y, size, w, share=True): """ Draw text """ w *= self.tw.coord_scale - self.gc.set_foreground(self.tw.textcolor) + self.gc.set_foreground(self.fgcolor) fd = pango.FontDescription('Sans') try: fd.set_size(int(size * self.tw.coord_scale) * pango.SCALE) except TypeError, ValueError: - _logger.debug("bad value sent to %s" % (__name__)) + debug_output("bad value sent to %s" % (__name__), + self.tw.running_sugar) return if self.tw.interactive_mode: if type(label) == str or type(label) == unicode: @@ -529,32 +590,57 @@ class TurtleGraphics: context.move_to(x, y + h) context.show_text(message) - if self.tw.saving_svg and self.pendown: - self.tw.svg_string += self.svg.text(x - self.width / 2, + if self.tw.saving_svg: # and self.pendown: + self.tw.svg_string += self.svg.text(x, # - self.width / 2, y + size, size, w, label) + if self.tw.sharing() and share: + event = "W|%s" % (data_to_string([self._get_my_nick(), + [label, round_int(x), + round_int(y), round_int(size), + round_int(w)]])) + self.tw.send_event(event) + + def turtle_to_screen_coordinates(self, x, y): + """ The origin of turtle coordinates is the center of the screen """ + return self.width / 2 + x, self.invert_y_coordinate(y) + + def screen_to_turtle_coordinates(self, x, y): + """ The origin of the screen coordinates is the upper left corner """ + return x - self.width / 2, self.invert_y_coordinate(y) + + def invert_y_coordinate(self, y): + """ Positive y goes up in turtle coordinates, down in sceeen + coordinates """ + return self.height / 2 - y def draw_line(self, x1, y1, x2, y2): """ Draw a line """ - x1, y1 = self.width / 2 + int(x1), self.height / 2 - int(y1) - x2, y2 = self.width / 2 + int(x2), self.height / 2 - int(y2) + x1, y1 = self.turtle_to_screen_coordinates(x1, y1) + x2, y2 = self.turtle_to_screen_coordinates(x2, y2) if x1 < x2: - minx, maxx = x1, x2 + minx, maxx = int(x1), int(x2) else: - minx, maxx = x2, x1 + minx, maxx = int(x2), int(x1) if y1 < y2: - miny, maxy = y1, y2 + miny, maxy = int(y1), int(y2) else: - miny, maxy = y2, y1 + miny, maxy = int(y2), int(y1) w, h = maxx - minx, maxy - miny - self.canvas.images[0].draw_line(self.gc, x1, y1, x2, y2) + self.canvas.images[0].draw_line(self.gc, int(x1), int(y1), int(x2), + int(y2)) if self.fill and self.poly_points == []: - self.poly_points.append((x1, y1)) + self.poly_points.append((int(x1), int(y1))) if self.fill: - self.poly_points.append((x2, y2)) - self.invalt(minx - self.pensize * self.tw.coord_scale / 2 - 3, - miny - self.pensize * self.tw.coord_scale / 2 - 3, + self.poly_points.append((int(x2), int(y2))) + self.invalt(minx - int(self.pensize * self.tw.coord_scale / 2) - 3, + miny - int(self.pensize * self.tw.coord_scale / 2) - 3, w + self.pensize * self.tw.coord_scale + 6, h + self.pensize * self.tw.coord_scale + 6) + if self.tw.saving_svg and self.pendown: + self.tw.svg_string += self.svg.new_path(x1, y1) + self.tw.svg_string += self.svg.line_to(x2, y2) + self.tw.svg_string += "\"\n" + self.tw.svg_string += self.svg.style() def turn_turtle(self): """ Change the orientation of the turtle """ @@ -562,8 +648,7 @@ class TurtleGraphics: def move_turtle(self): """ Move the turtle """ - x, y = self.width / 2 + int(self.xcor), \ - self.height / 2 - int(self.ycor) + x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) self.tw.active_turtle.move( (int(self.cx + x - self.tw.active_turtle.spr.rect.width / 2), int(self.cy + y - self.tw.active_turtle.spr.rect.height / 2))) @@ -611,9 +696,9 @@ class TurtleGraphics: def get_pixel(self): """ Read the pixel at x, y """ if self.tw.interactive_mode: - return self.canvas.get_pixel( - (self.width / 2 + int(self.xcor), - self.height / 2 - int(self.ycor)), 0, self.tw.color_mode) + x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) + return self.canvas.get_pixel((int(x), int(y)), 0, + self.tw.color_mode) else: return(-1, -1, -1, -1) @@ -625,13 +710,16 @@ class TurtleGraphics: self.seth(0, False) self.setxy(0, 0, False, pendown=False) self.tw.active_turtle.set_pen_state(True) - self.tw.active_turtle = self.tw.turtles.get_turtle(k, False) + elif colors is not None: + self.tw.active_turtle = self.tw.turtles.get_turtle(k, False) + self.tw.active_turtle.set_turtle_colors(colors) + else: + self.tw.active_turtle = self.tw.turtles.get_turtle(k, False) self.tw.active_turtle.show() tx, ty = self.tw.active_turtle.get_xy() - self.xcor = -self.width / 2 + tx + \ - self.tw.active_turtle.spr.rect.width / 2 - self.ycor = self.height / 2 - ty - \ - self.tw.active_turtle.spr.rect.height / 2 + self.xcor, self.ycor = self.screen_to_turtle_coordinates(tx, ty) + self.xcor += self.tw.active_turtle.spr.rect.width / 2 + self.ycor -= self.tw.active_turtle.spr.rect.height / 2 self.heading = self.tw.active_turtle.get_heading() self.setcolor(self.tw.active_turtle.get_color(), False) self.setgray(self.tw.active_turtle.get_gray(), False) @@ -651,11 +739,3 @@ class TurtleGraphics: def _get_my_nick(self): return self.tw.nick - - def _send_event(self, entry, share): - if not share: - return - - if self.tw.sharing(): - print "Sending: %s" % entry - self.tw.send_event(entry) diff --git a/TurtleArt/tacollaboration.py b/TurtleArt/tacollaboration.py index 52164e0..e1534a4 100644 --- a/TurtleArt/tacollaboration.py +++ b/TurtleArt/tacollaboration.py @@ -1,9 +1,34 @@ +#Copyright (c) 2011, Walter Bender +#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/> + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. from dbus.service import signal from dbus.gobject_service import ExportedGObject -import logging import telepathy -from TurtleArt.tautils import data_to_string, data_from_string + +import gtk +import base64 + +from TurtleArt.tautils import data_to_string, data_from_string, get_path, \ + base64_to_image, debug_output, error_output +from TurtleArt.taconstants import DEFAULT_TURTLE_COLORS try: from sugar import profile @@ -17,43 +42,63 @@ except: SERVICE = 'org.laptop.TurtleArtActivity' IFACE = SERVICE PATH = '/org/laptop/TurtleArtActivity' -_logger = logging.getLogger('turtleart-activity') + class Collaboration(): def __init__(self, tw, activity): """ A simplistic sharing model: the sharer is the master """ self._tw = tw self._tw.send_event = self.send_event + self._tw.remote_turtle_dictionary = {} self._activity = activity + self._setup_dispatch_table() def setup(self): # TODO: hand off role of master is sharer leaves self.pservice = presenceservice.get_instance() - self.initiating = None # sharing (True) or joining (False) + self.initiating = None # sharing (True) or joining (False) # Add my buddy object to the list owner = self.pservice.get_owner() self.owner = owner self._tw.buddies.append(self.owner) - self._share = "" - + self._share = '' self._activity.connect('shared', self._shared_cb) self._activity.connect('joined', self._joined_cb) + def _setup_dispatch_table(self): + self._processing_methods = { + 't': self._turtle_request, + 'T': self._receive_turtle_dict, + 'f': self._move_forward, + 'a': self._move_in_arc, + 'r': self._rotate_turtle, + 'x': self._setxy, + 'W': self._draw_text, + 'c': self._set_pen_color, + 'g': self._set_pen_gray_level, + 's': self._set_pen_shade, + 'w': self._set_pen_width, + 'p': self._set_pen_state, + 'F': self._fill_polygon, + 'P': self._draw_pixbuf + } + def _shared_cb(self, activity): self._shared_activity = self._activity._shared_activity if self._shared_activity is None: - _logger.error("Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()") + debug_output('Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()', + self._tw.running_sugar) return self._tw.set_sharing(True) self.initiating = True self.waiting_for_turtles = False - self.turtle_dictionary = self._get_dictionary() - - _logger.debug('I am sharing...') + self._tw.remote_turtle_dictionary = self._get_dictionary() + + debug_output('I am sharing...', self._tw.running_sugar) self.conn = self._shared_activity.telepathy_conn self.tubes_chan = self._shared_activity.telepathy_tubes_chan @@ -62,7 +107,8 @@ class Collaboration(): self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) - _logger.debug('This is my activity: making a tube...') + debug_output('This is my activity: making a tube...', + self._tw.running_sugar) id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) @@ -70,8 +116,9 @@ class Collaboration(): def _joined_cb(self, activity): self._shared_activity = self._activity._shared_activity if self._shared_activity is None: - _logger.error("Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()") + debug_output('Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()', + self._tw.running_sugar) return self._tw.set_sharing(True) @@ -85,7 +132,8 @@ class Collaboration(): self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) - _logger.debug('I am joining an activity: waiting for a tube...') + debug_output('I am joining an activity: waiting for a tube...', + self._tw.running_sugar) self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) @@ -98,13 +146,13 @@ class Collaboration(): self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): - _logger.error('ListTubes() failed: %s', e) + error_output('ListTubes() failed: %s' % (e), self._tw.running_sugar) def _new_tube_cb(self, id, initiator, type, service, params, state): """ Create a new tube. """ - _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' - 'params=%r state=%d', id, initiator, type, service, - params, state) + debug_output('New tube: ID=%d initator=%d type=%d service=%s \ + params=%r state=%d' % (id, initiator, type, service, + params, state), self._tw.running_sugar) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: @@ -120,106 +168,35 @@ class Collaboration(): self.event_received_cb) # Now that we have the tube, we can ask for the turtle dictionary. - if self.waiting_for_turtles: - _logger.debug("Sending a request for the turtle dictionary") - # we need to send our own nick and colors + if self.waiting_for_turtles: # A joiner must wait for turtles. + debug_output('Sending a request for the turtle dictionary', + self._tw.running_sugar) + # We need to send our own nick, colors, and turtle position colors = self._get_colors() - event = "t|" + data_to_string([self._get_nick(), colors]) - _logger.debug(event) + event = 't|' + data_to_string([self._get_nick(), colors]) + debug_output(event, self._tw.running_sugar) self.send_event(event) - def event_received_cb(self, text): + def event_received_cb(self, event_message): """ Events are sent as a tuple, nick|cmd, where nick is a turle name and cmd is a turtle event. Everyone gets the turtle dictionary from the sharer and watches for 't' events, which indicate that a new turtle has joined. """ - if len(text) == 0: + if len(event_message) == 0: return - # Save active Turtle + + # Save active Turtle save_active_turtle = self._tw.active_turtle - e = text.split("|", 2) - text = e[1] - if e[0] == 't': # request for turtle dictionary - if text > 0: - [nick, colors] = data_from_string(text) - if nick != self._tw.nick: - # There may not be a turtle dictionary. - if hasattr(self, "turtle_dictionary"): - self.turtle_dictionary[nick] = colors - else: - self.turtle_dictionary = {nick: colors} - # Add new turtle for the joiner. - self._tw.canvas.set_turtle(nick, colors) - # Sharer should send turtle dictionary. - if self.initiating: - text = data_to_string(self.turtle_dictionary) - self.send_event("T|" + text) - elif e[0] == 'T': # Receiving the turtle dictionary. - if self.waiting_for_turtles: - if len(text) > 0: - self.turtle_dictionary = data_from_string(text) - for nick in self.turtle_dictionary: - if nick != self._tw.nick: - colors = self.turtle_dictionary[nick] - # add new turtle for the joiner - self._tw.canvas.set_turtle(nick, colors) - self.waiting_for_turtles = False - elif e[0] == 'f': # move a turtle forward - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.forward(x, False) - elif e[0] == 'a': # move a turtle in an arc - if len(text) > 0: - [nick, [a, r]] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.arc(a, r, False) - elif e[0] == 'r': # rotate turtle - if len(text) > 0: - [nick, h] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.seth(h, False) - elif e[0] == 'x': # set turtle xy position - if len(text) > 0: - [nick, [x, y]] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setxy(x, y, False) - elif e[0] == 'c': # set turtle pen color - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setcolor(x, False) - elif e[0] == 'g': # set turtle pen gray level - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setgray(x, False) - elif e[0] == 's': # set turtle pen shade - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setshade(x, False) - elif e[0] == 'w': # set turtle pen width - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setpensize(x, False) - elif e[0] == 'p': # set turtle pen state - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setpen(x, False) + + try: + command, payload = event_message.split('|', 2) + self._processing_methods[command](payload) + except ValueError: + debug_output('Could not split event message.', + self._tw.running_sugar) + # Restore active Turtle self._tw.canvas.set_turtle(self._tw.turtles.get_turtle_key( save_active_turtle)) @@ -229,19 +206,193 @@ class Collaboration(): if hasattr(self, 'chattube') and self.chattube is not None: self.chattube.SendText(entry) + def _turtle_request(self, payload): + ''' incoming turtle from a joiner ''' + if payload > 0: + [nick, colors] = data_from_string(payload) + if nick != self._tw.nick: # It is not me. + # There may not be a turtle dictionary. + if hasattr(self._tw, 'remote_turtle_dictionary'): + # Make sure it is not a "rejoin". + if not nick in self._tw.remote_turtle_dictionary: + # Add new turtle for the joiner. + self._tw.canvas.set_turtle(nick, colors) + self._tw.label_remote_turtle(nick, colors) + self._tw.remote_turtle_dictionary[nick] = colors + else: + self._tw.remote_turtle_dictionary = self._get_dictionary() + # Add new turtle for the joiner. + self._tw.canvas.set_turtle(nick, colors) + self._tw.label_remote_turtle(nick, colors) + + # Sharer should send the updated remote turtle dictionary to everyone. + if self.initiating: + if not self._tw.nick in self._tw.remote_turtle_dictionary: + self._tw.remote_turtle_dictionary[self._tw.nick] = \ + self._get_colors() + event_payload = data_to_string(self._tw.remote_turtle_dictionary) + self.send_event('T|' + event_payload) + self._send_my_xy() # And the sender should report her xy position. + + def _receive_turtle_dict(self, payload): + ''' Any time there is a new joiner, an updated turtle dictionary is + circulated. Everyone must report their turtle positions so that we + are in sync. ''' + if self.waiting_for_turtles: + if len(payload) > 0: + # Grab the new remote turtles dictionary. + remote_turtle_dictionary = data_from_string(payload) + # Add see what is new. + for nick in remote_turtle_dictionary: + if nick == self._tw.nick: + debug_output('skipping my nick %s' \ + % (nick), self._tw.running_sugar) + elif nick != self._tw.remote_turtle_dictionary: + # Add new the turtle. + colors = remote_turtle_dictionary[nick] + self._tw.remote_turtle_dictionary[nick] = colors + self._tw.canvas.set_turtle(nick, colors) + # Label the remote turtle. + self._tw.label_remote_turtle(nick, colors) + debug_output('adding %s to remote turtle dictionary' \ + % (nick), self._tw.running_sugar) + else: + debug_output('%s already in remote turtle dictionary' \ + % (nick), self._tw.running_sugar) + self.waiting_for_turtles = False + self._send_my_xy() + + def _send_my_xy(self): + ''' Set xy location so joiner can sync turtle positions. ''' + self._tw.canvas.set_turtle(self._get_nick()) + if self._tw.canvas.pendown: + self.send_event('p|%s' % (data_to_string([self._get_nick(), + False]))) + put_pen_back_down = True + else: + put_pen_back_down = False + self.send_event('x|%s' % (data_to_string([self._get_nick(), + [int(self._tw.canvas.xcor), int(self._tw.canvas.ycor)]]))) + if put_pen_back_down: + self.send_event('p|%s' % (data_to_string([self._get_nick(), + True]))) + self.send_event('r|%s' % (data_to_string([self._get_nick(), + int(self._tw.canvas.heading)]))) + + def _draw_pixbuf(self, payload): + if len(payload) > 0: + [nick, [a, b, x, y, w, h, width, height, data]] =\ + data_from_string(payload) + if nick != self._tw.nick: + if self._tw.running_sugar: + tmp_path = get_path(self._tw.activity, 'instance') + else: + tmp_path = '/tmp' + file_name = base64_to_image(data, tmp_path) + pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name, + width, height) + x, y = self._tw.canvas.turtle_to_screen_coordinates(x, y) + self._tw.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h, + file_name, False) + + def _move_forward(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.forward(x, False) + + def _move_in_arc(self, payload): + if len(payload) > 0: + [nick, [a, r]] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.arc(a, r, False) + + def _rotate_turtle(self, payload): + if len(payload) > 0: + [nick, h] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.seth(h, False) + + def _setxy(self, payload): + if len(payload) > 0: + [nick, [x, y]] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setxy(x, y, False) + + def _draw_text(self, payload): + if len(payload) > 0: + [nick, [label, x, y, size, w]] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.draw_text(label, x, y, size, w, False) + + def _set_pen_color(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setcolor(x, False) + + def _set_pen_gray_level(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setgray(x, False) + + def _set_pen_shade(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setshade(x, False) + + def _set_pen_width(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setpensize(x, False) + + def _set_pen_state(self, payload): + if len(payload) > 0: + [nick, x] = data_from_string(payload) + if nick != self._tw.nick: + self._tw.canvas.set_turtle(nick) + self._tw.canvas.setpen(x, False) + + def _fill_polygon(self, payload): + # Check to make sure that the poly_point array is passed properly + if len(payload) > 0: + [nick, poly_points] = data_from_string(payload) + shared_poly_points = [] + for i in range(len(poly_points)): + shared_poly_points.append(( + self._tw.canvas.turtle_to_screen_coordinates( + poly_points[i][0], poly_points[i][1]))) + self._tw.canvas.fill_polygon(shared_poly_points) + def _get_dictionary(self): - d = { self._get_nick(): self._get_colors()} - return d + return {self._get_nick(): self._get_colors()} def _get_nick(self): return self._tw.nick def _get_colors(self): - if profile: - colors = profile.get_color().to_string() + colors = None + if self._tw.running_sugar: + if profile.get_color() is not None: + colors = profile.get_color().to_string() else: colors = self._activity.get_colors() - return colors + if colors is None: + colors = '%s,%s' % (DEFAULT_TURTLE_COLORS[0], + DEFAULT_TURTLE_COLORS[1]) + return colors.split(',') + class ChatTube(ExportedGObject): @@ -249,7 +400,7 @@ class ChatTube(ExportedGObject): """Class for setting up tube for sharing.""" super(ChatTube, self).__init__(tube, PATH) self.tube = tube - self.is_initiator = is_initiator # Are we sharing or joining activity? + self.is_initiator = is_initiator # Are we sharing or joining activity? self.stack_received_cb = stack_received_cb self.stack = '' diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py index 77ebefb..30920d6 100644 --- a/TurtleArt/taconstants.py +++ b/TurtleArt/taconstants.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#Copyright (c) 2010, Walter Bender +#Copyright (c) 2010-11 Walter Bender #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -19,79 +19,6 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -""" -This file contains the constants that by-in-large determine the -behavior of Turtle Art. Notably, the block palettes are defined -below. If you want to add a new block to Turtle Art, it is generally a -matter of modifying some tables below and then adding the primitive to -talogo.py. For example, if we want to add a new turtle command, -'uturn', we'd make the following changes: - -(1) We'd add 'uturn' to the PALETTES list of lists: - -PALETTES = [['forward', 'back', 'clean', 'left', 'right', 'uturn', 'show', - 'seth', 'setxy', 'heading', 'xcor', 'ycor', 'setscale', - 'arc', 'scale'], - ['penup','pendown', 'setpensize', 'fillscreen', 'pensize',... - -(2) Then we'd add it to one of the block-style definitions. Since it takes -no arguments, we'd add it here: - -BASIC_STYLE = ['clean', 'penup', 'pendown', 'stack1', 'stack2', 'vspace', - 'hideblocks', 'showblocks', 'clearheap', 'printheap', 'kbinput', 'uturn'] - -(3) Then we give it a name (Note the syntax _('string to be -translated') used by the language-internationalization system; also -note that the name is an array, as some blocks contain multiple -strings.): - -BLOCK_NAMES = { -... - 'uturn':[_('u-turn')], -... - } - -(4) and a help-menu entry: - -HELP_STRINGS = { -... - 'uturn':_('change the heading of the turtle 180 degrees'), -... - } - -(5) Next, we need to define it as a primitive for the Logo command -parser (generally just the same name): - -PRIMITIVES = { -... - 'uturn':'uturn', -... - } - -(6) Since there are no default arguments, we don't need to do anything -else here. But we do need to define the actual function in talogo.py - -DEFPRIM = { -... - 'uturn':[0, lambda self: self.tw.canvas.seth(self.tw.canvas.heading+180)], -... - } - -That's it. When you next run Turtle Art, you will have a 'uturn' block -on the Turtle Palette. - -Adding a new palette is simply a matter of: (1) adding an additional -entry to PALETTE_NAMES; (2) new list of blocks to PALETTES; and (3) an -additional entry in COLORS. However you will have to: (4) create icons -for the palette-selector buttons. These are kept in the icons -subdirectory. You need two icons: yourpalettenameoff.svg and -yourpalettenameon.svg, where yourpalettename is the same string as the -entry you added to the PALETTE_NAMES list. Note that the icons should -be the same size (55x55) as the others. This is the default icon size -for Sugar toolbars. - -""" - from gettext import gettext as _ # @@ -108,52 +35,7 @@ TAB_LAYER = 710 STATUS_LAYER = 900 TOP_LAYER = 1000 -# -# Block-palette categories -# - -PALETTE_NAMES = ['turtle', 'pen', 'colors', 'numbers', 'flow', 'blocks', - 'extras', 'sensor', 'portfolio', 'trash'] - -PALETTES = [['clean', 'forward', 'back', 'show', 'left', 'right', - 'seth', 'setxy2', 'heading', 'xcor', 'ycor', 'setscale', - 'arc', 'scale', 'leftpos', 'toppos', 'rightpos', - 'bottompos'], - ['penup', 'pendown', 'setpensize', 'fillscreen', 'pensize', - 'setcolor', 'setshade', 'setgray', 'color', 'shade', - 'gray', 'startfill', 'stopfill'], - ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', - 'white', 'black'], - ['plus2', 'minus2', 'product2', - 'division2', 'identity2', 'remainder2', 'sqrt', 'random', - 'number', 'greater2', 'less2', 'equal2', 'not', 'and2', 'or2'], - ['wait', 'forever', 'repeat', 'if', 'ifelse', 'while', 'until', - 'hspace', 'vspace', 'stopstack'], - ['start', 'hat1', 'stack1', 'hat', 'hat2', 'stack2', 'stack', - 'storeinbox1', 'storeinbox2', 'string', 'box1', 'box2', 'box', - 'storein'], - ['push', 'printheap', 'clearheap', 'pop', 'comment', 'print', - 'myfunc1arg', 'userdefined', - 'cartesian', 'width', 'height', 'polar', 'addturtle', 'reskin', - 'sandwichtop_no_label', 'sandwichbottom'], - ['kbinput', 'keyboard', 'readpixel', 'see', - 'sound', 'volume', 'pitch'], - ['journal', 'audio', 'video', 'description', 'hideblocks', - 'showblocks', 'fullscreen', 'savepix', 'savesvg', 'mediawait', - 'picturelist', 'picture1x1a', 'picture1x1', 'picture2x2', - 'picture2x1', 'picture1x2'], - ['empty', 'restoreall']] - -# -# Block-style attributes -# - -COLORS = [["#00FF00", "#00A000"], ["#00FFFF", "#00A0A0"], - ["#00FFFF", "#00A0A0"], ["#FF00FF", "#A000A0"], - ["#FFC000", "#A08000"], ["#FFFF00", "#A0A000"], - ["#FF0000", "#A00000"], ["#FF0000", "#A00000"], - ["#0000FF", "#0000A0"], ["#FFFF00", "#A0A000"]] - +# Special-case some block colors BOX_COLORS = {'red': ["#FF0000", "#A00000"], 'orange': ["#FFD000", "#AA8000"], 'yellow': ["#FFFF00", "#A0A000"], @@ -173,9 +55,10 @@ SELECTOR_WIDTH = 55 ICON_SIZE = 55 GRADIENT_COLOR = "#FFFFFF" STANDARD_STROKE_WIDTH = 1.0 -BLOCK_SCALE = 2.0 +BLOCK_SCALE = [0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0] PALETTE_SCALE = 1.5 DEFAULT_TURTLE = 'Yertle' +DEFAULT_TURTLE_COLORS = ['#008000', '#00A000'] HORIZONTAL_PALETTE = 0 VERTICAL_PALETTE = 1 BLACK = -9999 @@ -190,78 +73,23 @@ DEFAULT_SCALE = 33 XO1 = 'xo1' XO15 = 'xo1.5' UNKNOWN = 'unknown' -SENSOR_AC_NO_BIAS = 'external' -SENSOR_AC_BIAS = 'sound' -SENSOR_DC_NO_BIAS = 'voltage' -SENSOR_DC_BIAS = 'resistance' -# -# Block-style definitions -# -BASIC_STYLE_HEAD = ['start', 'hat1', 'hat2', 'restore', 'restoreall'] -BASIC_STYLE_HEAD_1ARG = ['hat'] -BASIC_STYLE_TAIL = ['stopstack', 'empty'] -BASIC_STYLE = [] -BASIC_STYLE_EXTENDED_VERTICAL = ['clean', 'penup', 'pendown', 'stack1', - 'stack2', 'hideblocks', 'showblocks', 'clearheap', 'printheap', 'kbinput', - 'fullscreen', 'cartesian', 'polar', 'startfill', 'mediawait', - 'stopfill', 'readpixel', 'readcamera', 'vspace'] -INVISIBLE = ['sandwichcollapsed'] -BASIC_STYLE_EXTENDED = ['picturelist', 'picture1x1', 'picture2x2', - 'picture2x1', 'picture1x2', 'picture1x1a'] -BASIC_STYLE_1ARG = ['forward', 'back', 'left', 'right', 'seth', 'show', 'image', - 'setscale', 'setpensize', 'setcolor', 'setshade', 'print', 'showaligned', - 'settextsize', 'settextcolor', 'print', 'wait', 'storeinbox1', 'savepix', - 'storeinbox2', 'wait', 'stack', 'push', 'nop', 'addturtle', 'comment', - 'savesvg', 'setgray', 'skin', 'reskin'] -BASIC_STYLE_VAR_ARG = ['userdefined', 'userdefined2args', 'userdefined3args'] -BULLET_STYLE = ['templatelist', 'list'] -BASIC_STYLE_2ARG = ['arc', 'setxy', 'setxy2', 'fillscreen', 'storein', 'write'] -BOX_STYLE = ['number', 'xcor', 'ycor', 'heading', 'pensize', 'color', 'shade', - 'textcolor', 'textsize', 'box1', 'box2', 'string', 'leftpos', 'scale', - 'toppos', 'rightpos', 'bottompos', 'width', 'height', 'pop', 'keyboard', - 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'white', - 'black', 'titlex', 'titley', 'leftx', 'topy', 'rightx', 'bottomy', - 'sound', 'volume', 'pitch', 'voltage', 'resistance', 'gray', 'see', 'rfid', - 'luminance'] -BOX_STYLE_MEDIA = ['description', 'audio', 'journal', 'video', 'camera'] -NUMBER_STYLE = ['plus2', 'product2', 'myfunc'] -NUMBER_STYLE_VAR_ARG = ['myfunc1arg', 'myfunc2arg', 'myfunc3arg'] -NUMBER_STYLE_BLOCK = ['random'] -NUMBER_STYLE_PORCH = ['minus2', 'division2', 'remainder2'] -NUMBER_STYLE_1ARG = ['sqrt', 'identity2'] -NUMBER_STYLE_1STRARG = ['box'] -COMPARE_STYLE = ['greater2', 'less2', 'equal2'] -BOOLEAN_STYLE = ['and2', 'or2'] -NOT_STYLE = ['not'] -FLOW_STYLE = ['forever'] -FLOW_STYLE_TAIL = ['hspace'] -FLOW_STYLE_1ARG = ['repeat'] -FLOW_STYLE_BOOLEAN = ['if', 'while', 'until'] -FLOW_STYLE_WHILE = ['while2'] -FLOW_STYLE_ELSE = ['ifelse'] -COLLAPSIBLE_TOP = ['sandwichtop'] -COLLAPSIBLE_TOP_NO_ARM = ['sandwichtop_no_arm'] -COLLAPSIBLE_TOP_NO_LABEL = ['sandwichtop_no_label'] -COLLAPSIBLE_TOP_NO_ARM_NO_LABEL = ['sandwichtop_no_arm_no_label'] -COLLAPSIBLE_BOTTOM = ['sandwichbottom'] - -# Depreciated block styles -PORTFOLIO_STYLE_2x2 = ['template2x2'] -PORTFOLIO_STYLE_1x1 = ['template1x1', 'template1x1a'] -PORTFOLIO_STYLE_2x1 = ['template2x1'] -PORTFOLIO_STYLE_1x2 = ['template1x2'] +CONSTANTS = {'leftpos': None, 'toppos': None, 'rightpos': None, + 'bottompos': None, 'width': None, 'height': None, 'red': 0, + 'orange': 10, 'yellow': 20, 'green': 40, 'cyan': 50, 'blue': 70, + 'purple': 90, 'titlex': None, 'titley': None, 'leftx': None, + 'topy': None, 'rightx': None, 'bottomy': None} # # Blocks that are expandable # -EXPANDABLE = ['vspace', 'hspace', 'identity2'] +EXPANDABLE_STYLE = ['boolean-style', 'compare-porch-style', 'compare-style', + 'number-style-porch', 'number-style', 'basic-style-2arg', + 'number-style-block'] -EXPANDABLE_BLOCKS = ['plus2', 'minus2', 'division2', 'remainder2', 'product2', - 'random', 'equal2', 'greater2', 'less2', 'and2', 'or2', - 'arc', 'setxy', 'setxy2', 'fillscreen', 'storein', 'write'] +EXPANDABLE = ['vspace', 'hspace', 'identity2'] -EXPANDABLE_ARGS = ['templatelist', 'list', 'myfunc1arg', 'myfunc2arg', +EXPANDABLE_ARGS = ['list', 'myfunc1arg', 'myfunc2arg', 'myfunc3arg', 'userdefined', 'userdefined2args', 'userdefined3args'] # @@ -270,418 +98,18 @@ EXPANDABLE_ARGS = ['templatelist', 'list', 'myfunc1arg', 'myfunc2arg', COLLAPSIBLE = ['sandwichbottom', 'sandwichcollapsed'] # -# Depreciated block styles that need dock adjustments +# Deprecated block styles that need dock adjustments # OLD_DOCK = ['and', 'or', 'plus', 'minus', 'division', 'product', 'remainder'] # -# Blocks that contain media -# -CONTENT_BLOCKS = ['number', 'string', 'description', 'audio', 'video', - 'journal', 'camera'] - -# # These blocks get a special skin # BLOCKS_WITH_SKIN = ['journal', 'audio', 'description', 'nop', 'userdefined', - 'video', 'userdefined2args', 'userdefined3args'] + 'video', 'userdefined2args', 'userdefined3args', 'camera'] PYTHON_SKIN = ['nop', 'userdefined', 'userdefined2args', 'userdefined3args'] -# -# These blocks hold constants -# -CONSTANTS = {'leftpos':None, 'toppos':None, 'rightpos':None, 'bottompos':None, - 'width':None, 'height':None, 'red':0, 'orange':10, 'yellow':20, - 'green':40, 'cyan':50, 'blue':70, 'purple':90, 'titlex':None, - 'titley':None, 'leftx':None, 'topy':None, 'rightx':None, - 'bottomy':None} - -# -# Block-name dictionary used for labels -# -BLOCK_NAMES = { - 'addturtle': [_('turtle')], - 'and2': [_('and')], - 'arc': [_('arc'), _('angle'), _('radius')], - 'audio': [' '], - 'back': [_('back')], - 'black': [_('black')], - 'blocks': [_('blocks')], - 'blue': [_('blue')], - 'bottompos': [_('bottom')], - 'bottomy': [_('picture bottom')], - 'box': [_('box')], - 'box1': [_('box 1')], - 'box2': [_('box 2')], - 'camera': [' '], - 'cartesian': [_('Cartesian')], - 'clean': [_(' clean ')], - 'clearheap': [_('empty heap')], - 'color': [_('color')], - 'colors': [_('colors')], - 'comment': [_('comment')], - 'cyan': [_('cyan')], - 'decription': [' '], - 'division2': ['/'], - 'empty': [_('empty trash')], - 'equal2': ['='], - 'extras': [_('extras')], - 'fillscreen': [_('fill screen'), _('color'), _('shade')], - 'flow': [_('flow')], - 'forever': [_('forever')], - 'forward': [_('forward')], - 'fullscreen': [_('full screen')], - 'gray': [_('gray')], - 'greater2': [">"], - 'green': [_('green')], - 'hat': [_('action')], - 'hat1': [_('action 1')], - 'hat2': [_('action 2')], - 'heading': [_('heading')], - 'height': [_('height')], - 'hideblocks': [_('hide blocks')], - 'hspace': [' '], - 'identity2': ['←'], - 'if': [' ', _('if'), _('then')], - 'ifelse': [' ', _('if'), _('then else')], - 'image': [_('show')], - 'journal': [' '], - 'kbinput': [_('query keyboard')], - 'keyboard': [_('keyboard')], - 'left': [_('left')], - 'leftpos': [_('left')], - 'leftx': [_('picture left')], - 'less2': ['<'], - 'list': ['list'], - 'luminance': [_('brightness')], - 'mediawait': [_('media wait')], - 'minus2': ['–'], - 'myfunc': [_('Python'), 'f(x)', 'x'], - 'myfunc1arg': [_('Python'), 'f(x)', 'x'], - 'myfunc2arg': [_('Python'), 'f(x,y)', ' '], - 'myfunc3arg': [_('Python'), 'f(x,y,z)', ' '], - 'nop': [_(' ')], - 'not': [_('not')], - 'number': ['100'], - 'numbers': [_('numbers')], - 'orange': [_('orange')], - 'or2': [_('or')], - 'pen': [_('pen')], - 'pendown': [_('pen down')], - 'pensize': [_('pen size')], - 'penup': [_('pen up')], - 'picturelist': [' '], - 'picture1x1': [' '], - 'picture1x1a': [' '], - 'picture2x2': [' '], - 'picture2x1': [' '], - 'picture1x2': [' '], - 'pitch': [_('pitch')], - 'plus2': [' + '], - 'polar': [_('polar')], - 'pop': [_('pop')], - 'portfolio': [_('portfolio')], - 'printheap': [_('show heap')], - 'print': [_('print')], - 'product2': ['×'], - 'purple': [_('purple')], - 'push': [_('push')], - 'random': [_('random'), _('min'), _('max')], - 'readcamera': [_('read camera')], - 'readpixel': [_('read pixel')], - 'red': [_('red')], - 'remainder2': [_('mod')], - 'repeat': [' ', _('repeat')], - 'reskin': [_('turtle shell')], - 'resistance': [_('resistance')], - 'restore': [_('restore last')], - 'restoreall': [_('restore all')], - 'rfid': [_('RFID')], - 'right': [_('right')], - 'rightpos': [_('right')], - 'rightx': [_('picture right')], - 'savepix': [_('save picture')], - 'savesvg': [_('save SVG')], - 'sandwichbottom': [' ', ' '], - 'sandwichcollapsed': [' '], - 'sandwichtop': [_('top of stack')], - 'sandwichtop_no_arm': [_('top of stack')], - 'sandwichtop_no_label': [' ', ' '], - 'sandwichtop_no_arm_no_label': [' ', _('click to open')], - 'scale': [_('scale')], - 'see': [_('turtle sees')], - 'sensor': [_('sensors')], - 'setcolor': [_('set color')], - 'setgray': [_('set gray')], - 'seth': [_('set heading')], - 'setpensize': [_('set pen size')], - 'setscale': [_('set scale')], - 'setshade': [_('set shade')], - 'settextcolor': [_('set text color')], - 'settextsize': [_('set text size')], - 'setxy': [_('set xy'), _('x'), _('y')], - 'setxy2': [_('set xy'), _('x'), _('y')], - 'shade': [_('shade')], - 'show': [_('show')], - 'showblocks': [_('show blocks')], - 'showaligned': [_('show aligned')], - 'skin': [_('turtle shell')], - 'sound': [_('sound')], - 'sqrt': ['√'], - 'stack': [_('action')], - 'stack1': [_('action 1')], - 'stack2': [_('action 2')], - 'start': [_('start')], - 'startfill': [_('start fill')], - 'stopfill': [_('end fill')], - 'stopstack': [_('stop action')], - 'storein': [_('store in'), _('box'), _('value')], - 'storeinbox1': [_('store in box 1')], - 'storeinbox2': [_('store in box 2')], - 'string': [_('text')], - 'template1x1': [' '], - 'template1x1a': [' '], - 'template1x2': [' '], - 'template2x1': [' '], - 'template2x2': [' '], - 'templatelist': [' '], - 'textsize': [_('text size')], - 'titlex': [_('title x')], - 'titley': [_('title y')], - 'toppos': [_('top')], - 'topy': [_('picture top')], - 'trash': [_('trash')], - 'turtle': [_('turtle')], - 'until': [_('until')], - 'userdefined': [_(' ')], - 'userdefined2args': [_(' ')], - 'userdefined3args': [_(' ')], - 'video': [' '], - 'voltage': [_('voltage')], - 'volume': [_('volume')], - 'vspace': [' '], - 'wait': [_('wait')], - 'while': [_('while')], - 'while2': [_('while')], - 'white': [_('white')], - 'width': [_('width')], - 'write': [_('write')], - 'xcor': [_('xcor')], - 'ycor': [_('ycor')], - 'yellow': [_('yellow')]} - -# -# Logo primitives -# - -PRIMITIVES = { - 'addturtle': 'turtle', - 'and2': 'and', - 'arc': 'arc', - 'back': 'back', - 'black': 'black', - 'blue': 'blue', - 'bottompos': 'bpos', - 'bottomy': 'boty', - 'box1': 'box1', - 'box2': 'box2', - 'box': 'box', - 'cartesian': 'cartesian', - 'clean': 'clean', - 'clearheap': 'clearheap', - 'color': 'color', - 'comment': 'comment', - 'cyan': 'cyan', - 'division2': 'division', - 'equal2': 'equal?', - 'fillscreen': 'fillscreen', - 'forever': 'forever', - 'forward': 'forward', - 'fullscreen': 'fullscreen', - 'gray': 'gray', - 'greater2': 'greater?', - 'green': 'green', - 'hat': 'nop3', - 'hat1': 'nop1', - 'hat2': 'nop2', - 'heading': 'heading', - 'height': 'vres', - 'hideblocks': 'hideblocks', - 'hspace': 'nop', - 'identity2': 'id', - 'if': 'if', - 'ifelse': 'ifelse', - 'image': 'show', - 'kbinput': 'kbinput', - 'keyboard': 'keyboard', - 'left': 'left', - 'leftpos': 'lpos', - 'leftx': 'leftx', - 'less2': 'less?', - 'list': 'bulletlist', - 'luminance': 'luminance', - 'mediawait': 'mediawait', - 'minus2': 'minus', - 'myfunc': 'myfunction', - 'myfunc1arg': 'myfunction', - 'myfunc2arg': 'myfunction2', - 'myfunc3arg': 'myfunction3', - 'nop': 'userdefined', - 'not': 'not', - 'orange': 'orange', - 'or2': 'or', - 'pendown': 'pendown', - 'pensize': 'pensize', - 'penup': 'penup', - 'pitch': 'pitch', - 'plus2': 'plus', - 'polar': 'polar', - 'pop': 'pop', - 'printheap': 'printheap', - 'print': 'print', - 'product2': 'product', - 'purple': 'purple', - 'push': 'push', - 'random': 'random', - 'red': 'red', - 'readcamera': 'readcamera', - 'readpixel': 'readpixel', - 'remainder2': 'mod', - 'repeat': 'repeat', - 'resistance': 'resistance', - 'rfid': 'rfid', - 'right': 'right', - 'rightpos': 'rpos', - 'rightx': 'rightx', - 'sandwichtop': 'comment', - 'sandwichtop_no_arm': 'comment', - 'sandwichtop_no_label': 'nop', - 'sandwichtop_no_arm_no_label': 'nop', - 'sandwichbottom': 'nop', - 'sandwichcollapsed': 'nop', - 'savepix': 'savepix', - 'savesvg': 'savesvg', - 'see': 'see', - 'scale': 'scale', - 'setcolor': 'setcolor', - 'setgray': 'setgray', - 'seth': 'seth', - 'setpensize': 'setpensize', - 'setscale': 'setscale', - 'setshade': 'setshade', - 'settextsize': 'settextsize', - 'settextcolor': 'settextcolor', - 'setxy': 'setxy', - 'setxy2': 'setxy2', - 'shade': 'shade', - 'show': 'show', - 'showblocks': 'showblocks', - 'showaligned': 'showaligned', - 'skin': 'skin', - 'sound': 'sound', - 'sqrt': 'sqrt', - 'stack': 'stack', - 'stack1': 'stack1', - 'stack2': 'stack2', - 'start': 'start', - 'startfill': 'startfill', - 'stopfill': 'stopfill', - 'stopstack': 'stopstack', - 'storein': 'storeinbox', - 'storeinbox1': 'storeinbox1', - 'storeinbox2': 'storeinbox2', - 'template1x1': 't1x1', - 'template1x1a': 't1x1a', - 'template1x2': 't1x2', - 'template2x1': 't2x1', - 'template2x2': 't2x2', - 'templatelist': 'bullet', - 'textsize': 'textsize', - 'titlex': 'titlex', - 'titley': 'titley', - 'toppos': 'tpos', - 'topy': 'topy', - 'userdefined': 'userdefined', - 'userdefined2args': 'userdefined2', - 'userdefined3args': 'userdefined3', - 'voltage': 'voltage', - 'volume': 'volume', - 'vspace': 'nop', - 'wait': 'wait', - 'while2': 'while', - 'white': 'white', - 'width': 'hres', - 'write': 'write', - 'xcor': 'xcor', - 'ycor': 'ycor', - 'yellow': 'yellow'} - -# -# block default values -# - -DEFAULTS = { - 'addturtle': [1], - 'arc': [90, 100], - 'audio': [None], - 'back': [100], - 'box': [_('my box')], - 'camera': ['CAMERA'], - 'comment': [_('comment')], - 'description': [None], - 'fillscreen': [60, 80], - 'forever': [None, 'vspace'], - 'forward': [100], - 'hat': [_('action')], - 'if': [None, None, 'vspace'], - 'ifelse': [None, 'vspace', None, 'vspace'], - 'journal': [None], - 'left': [90], - 'list': ['∙ ', '∙ '], - 'media': [None], - 'myfunc': ['x', 100], - 'myfunc1arg': ['x', 100], - 'myfunc2arg': ['x+y', 100, 100], - 'myfunc3arg': ['x+y+z', 100, 100, 100], - 'nop': [100], - 'number': [100], - 'random': [0, 100], - 'repeat': [4, None, 'vspace'], - 'right': [90], - 'sandwichtop': [_('label')], - 'sandwichtop_no_arm': [_('label')], - 'savepix': [_('picture name')], - 'savesvg': [_('picture name')], - 'setcolor': [0], - 'setgray': [100], - 'seth': [0], - 'setpensize': [5], - 'setscale': [33], - 'setshade': [50], - 'settextsize': [48], - 'settextcolor': [0], - 'setxy': [0, 0], - 'setxy2': [0, 0], - 'show': [_('text')], - 'showaligned': [_('text')], - 'stack': [_('action')], - 'storeinbox1': [100], - 'storeinbox2': [100], - 'storein': [_('my box'), 100], - 'string': [_('text')], - 'template1x1': [_('Title'), 'None'], - 'template1x1a': [_('Title'), 'None'], - 'template1x2': [_('Title'), 'None', 'None'], - 'template2x1': [_('Title'), 'None', 'None'], - 'template2x2': [_('Title'), 'None', 'None', 'None', 'None'], - 'templatelist': [_('Title'), '∙ '], - 'userdefined': [100], - 'userdefined2args': [100, 100], - 'userdefined3args': [100, 100, 100], - 'video': [None], - 'wait': [1], - 'write': [_('text'), 32]} # # Blocks that can interchange strings and numbers for their arguments @@ -697,6 +125,9 @@ STRING_OR_NUMBER_ARGS = ['plus2', 'equal2', 'less2', 'greater2', 'box', CONTENT_ARGS = ['show', 'showaligned', 'push', 'storein', 'storeinbox1', 'storeinbox2'] +PREFIX_DICTIONARY = {'journal': '#smedia_', 'description': '#sdescr_', + 'audio': '#saudio_', 'video': '#svideo_'} + # # Status blocks # @@ -709,7 +140,7 @@ MEDIA_SHAPES = ['audiooff', 'audioon', 'audiosmall', 'pythonoff', 'pythonon', 'pythonsmall', 'list', '1x1', '1x1a', '2x1', '1x2', '2x2'] -OVERLAY_SHAPES = ['Cartesian', 'Cartesian_labeled', 'polar'] +OVERLAY_SHAPES = ['Cartesian', 'Cartesian_labeled', 'polar', 'metric'] STATUS_SHAPES = ['status', 'info', 'nostack', 'dupstack', 'noinput', 'emptyheap', 'emptybox', 'nomedia', 'nocode', 'overflowerror', @@ -735,7 +166,8 @@ OLD_NAMES = {'product': 'product2', 'storeinbox': 'storein', 'minus': 'minus2', 'template1': 'template1x1', 'template2': 'template2x1', 'template6': 'template1x2', 'template7': 'template2x2', 'template4': 'template1x1a', 'hres': 'width', 'vres': 'height', - 'sandwichtop2': 'sandwichtop'} + 'sandwichtop2': 'sandwichtop', 'image': 'show', + 'container': 'indentity2', 'insertimage': 'show'} # # Define the relative size and postion of media objects @@ -744,7 +176,7 @@ OLD_NAMES = {'product': 'product2', 'storeinbox': 'storein', 'minus': 'minus2', TITLEXY = (0.9375, 0.875) # -# Relative placement of portfolio objects (used by depreciated blocks) +# Relative placement of portfolio objects (used by deprecated blocks) # TEMPLATES = {'t1x1': (0.5, 0.5, 0.0625, 0.125, 1.05, 0), 't2z1': (0.5, 0.5, 0.0625, 0.125, 1.05, 1.05), @@ -755,215 +187,21 @@ TEMPLATES = {'t1x1': (0.5, 0.5, 0.0625, 0.125, 1.05, 0), 'insertimage': (0.333, 0.333)} # -# Names for blocks without names for popup help -# -SPECIAL_NAMES = { - 'audio': _('audio'), - 'camera': _('camera'), - 'division2': _('divide'), - 'equal2': _('equal'), - 'greater2': _('greater than'), - 'hspace': _('horizontal space'), - 'identity2': _('identity'), - 'if': _('if then'), - 'ifelse': _('if then else'), - 'journal': _('journal'), - 'less2': _('less than'), - 'minus2': _('minus'), - 'nop': _('Python code'), - 'number': _('number'), - 'plus2': _('plus'), - 'product2': _('multiply'), - 'sqrt': _('square root'), - 'template1x1': _('presentation 1x1'), - 'template1x1a': _('presentation 1x1'), - 'template1x2': _('presentation 1x2'), - 'template2x1': _('presentation 2x1'), - 'template2x2': _('presentation 2x2'), - 'templatelist': _('presentation bulleted list'), - 'textsize': _('text size'), - 'video': _('video'), - 'vspace': _('vertical space')} - -# -# Help messages -# -HELP_STRINGS = { - 'addturtle': _("chooses which turtle to command"), - 'and2': _("logical AND operator"), - 'arc': _("moves turtle along an arc"), - 'audio': _("Sugar Journal audio object"), - 'back': _("moves turtle backward"), - 'blocks': _("Palette of variable blocks"), - 'bottompos': _("ycor of bottom of screen"), - 'box1': _("Variable 1 (numeric value)"), - 'box2': _("Variable 2 (numeric value)"), - 'box': _("named variable (numeric value)"), - 'camera': _('camera output'), - 'cartesian': _("displays Cartesian coordinates"), - 'clean': _("clears the screen and reset the turtle"), - 'clearheap': _("emptys FILO (first-in-last-out heap)"), - 'color': _("holds current pen color (can be used in place of a number block)"), - 'colors': _("Palette of pen colors"), - 'comment': _("places a comment in your code"), - 'debugoff': _("Debug"), - 'description': _("Sugar Journal description field"), - 'division2': _("divides top numeric input (numerator) by bottom numeric input (denominator)"), - 'empty': _("permanently deletes items in trash"), - 'eraseron': _("Clean"), - 'equal2': _("logical equal-to operator"), - 'extras': _("Palette of extra options"), - 'fillscreen': _("fills the background with (color, shade)"), - 'flow': _("Palette of flow operators"), - 'forever': _("loops forever"), - 'forward': _("moves turtle forward"), - 'fullscreen': _("hides the Sugar toolbars"), - 'gray': _("holds current gray level (can be used in place of a number block)"), - 'greater2': _("logical greater-than operator"), - 'hat1': _("top of Action 1 stack"), - 'hat2': _("top of Action 2 stack"), - 'hat': _("top of nameable action stack"), - 'heading': _("holds current heading value of the turtle (can be used in place of a number block)"), - 'height': _("the canvas height"), - 'hideblocks': _("declutters canvas by hiding blocks"), - 'hideshowoff': _("Hide blocks"), - 'hspace': _("jogs stack right"), - 'identity2': _("identity operator used for extending blocks"), - 'ifelse': _("if-then-else operator that uses boolean operators from Numbers palette"), - 'if': _("if-then operator that uses boolean operators from Numbers palette"), - 'journal': _("Sugar Journal media object"), - 'kbinput': _("query for keyboard input (results stored in keyboard block)"), - 'keyboard': _("holds results of query-keyboard block"), - 'leftpos': _("xcor of left of screen"), - 'left': _("turns turtle counterclockwise (angle in degrees)"), - 'less2': _("logical less-than operator"), - 'luminance': _("light level detected by camera"), - 'mediawait': _("wait for current video or audio to complete"), - 'minus2': _("subtracts bottom numeric input from top numeric input"), - 'myfunc': _("a programmable block: used to add advanced math equations, e.g., sin(x)"), - 'myfunc1arg': _("a programmable block: used to add advanced single-variable math equations, e.g., sin(x)"), - 'myfunc2arg': _("a programmable block: used to add advanced multi-variable math equations, e.g., sqrt(x*x+y*y)"), - 'myfunc3arg': _("a programmable block: used to add advanced multi-variable math equations, e.g., sin(x+y+z)"), - 'next': _('displays next palette'), - 'nop': _("runs code found in the tamyblock.py module found in the Journal"), - 'not': _("logical NOT operator"), - 'numbers': _("Palette of numeric operators"), - 'number': _("used as numeric input in mathematic operators"), - 'or': _("logical OR operator"), - 'orientation': _("changes the orientation of the palette of blocks"), - 'pendown': _("Turtle will draw when moved."), - 'pen': _("Palette of pen commands"), - 'pensize': _("holds current pen size (can be used in place of a number block)"), - 'penup': _("Turtle will not draw when moved."), - 'picture1x1': _("presentation template: select Journal object (with description)"), - 'picture1x1a': _("presentation template: select Journal object (no description)"), - 'picture1x2': _("presentation template: select two Journal objects"), - 'picture2x1': _("presentation template: select two Journal objects"), - 'picture2x2': _("presentation template: select four Journal objects"), - 'picturelist': _("presentation template: list of bullets"), - 'pitch': _('microphone input pitch'), - 'plus2': _("adds two alphanumeric inputs"), - 'polar': _("displays polar coordinates"), - 'pop': _("pops value off FILO (first-in last-out heap)"), - 'portfolio': _("Palette of presentation templates"), - 'print': _("prints value in status block at bottom of the screen"), - 'printheap': _("shows values in FILO (first-in last-out heap)"), - 'product2': _("multiplies two numeric inputs"), - 'push': _("pushes value onto FILO (first-in last-out heap)"), - 'random': _("returns random number between minimum (top) and maximum (bottom) values"), - 'readcamera': _("Average RGB color from camera is pushed to the stack"), - 'readpixel': _("RGB color under the turtle is pushed to the stack"), - 'remainder2': _("modular (remainder) operator"), - 'repeat': _("loops specified number of times"), - 'resistance': _("sensor input resistance"), - 'reskin': _("put a custom 'shell' on the turtle"), - 'restore': _("restores most recent blocks from trash"), - 'restoreall': _("restore all blocks from trash"), - 'rfid': _("RFID"), - 'rightpos': _("xcor of right of screen"), - 'right': _("turns turtle clockwise (angle in degrees)"), - 'run-fastoff': _("Run"), - 'run-slowoff': _("Step"), - 'sandwichbottom': _("bottom block in a collapsibe stack: click to collapse"), - 'sandwichcollapsed': _("bottom block in a collapsed stack: click to open"), - 'sandwichtop': _("top of a collapsible stack"), - 'sandwichtop_no_label': _("top of a collapsed stack"), - 'sandwichtop_no_arm': _("top of a collapsible stack"), - 'sandwichtop_no_arm_no_label': _("top of a collapsed stack"), - 'savepix': _("saves a picture to the Sugar Journal"), - 'savesvg': _("saves turtle graphics as an SVG file in the Sugar Journal"), - 'scale': _("holds current scale value"), - 'see': _('returns the color that the turtle "sees"'), - 'sensor': _("Palette of sensor blocks"), - 'setcolor': _("sets color of the line drawn by the turtle"), - 'setgray': _("sets gray level of the line drawn by the turtle"), - 'seth': _("sets the heading of the turtle (0 is towards the top of the screen.)"), - 'setpensize': _("sets size of the line drawn by the turtle"), - 'setscale': _("sets the scale of media"), - 'setshade': _("sets shade of the line drawn by the turtle"), - 'settextcolor': _("sets color of text drawn by the turtle"), - 'settextsize': _("sets size of text drawn by turtle"), - 'setxy': _("moves turtle to position xcor, ycor; (0, 0) is in the center of the screen."), - 'setxy2': _("moves turtle to position xcor, ycor; (0, 0) is in the center of the screen."), - 'shade': _("holds current pen shade"), - 'show': _("draws text or show media from the Journal"), - 'showblocks': _("restores hidden blocks"), - 'skin': _("put a custom 'shell' on the turtle"), - 'sound': _("raw microphone input signal"), - 'sqrt': _("calculates square root"), - 'stack1': _("invokes Action 1 stack"), - 'stack2': _("invokes Action 2 stack"), - 'stack': _("invokes named action stack"), - 'start': _("connects action to toolbar run buttons"), - 'startfill': _("starts filled polygon (used with end fill block)"), - 'stopfill': _("completes filled polygon (used with start fill block)"), - 'stopiton': _("Stop turtle"), - 'stopstack': _("stops current action"), - 'storeinbox1': _("stores numeric value in Variable 1"), - 'storeinbox2': _("stores numeric value in Variable 2"), - 'storein': _("stores numeric value in named variable"), - 'string': _("string value"), - 'template1x1': _("presentation template: select Journal object (with description)"), - 'template1x1a': _("presentation template: select Journal object (no description)"), - 'template1x2': _("presentation template: select two Journal objects"), - 'template2x1': _("presentation template: select two Journal objects"), - 'template2x2': _("presentation template: select four Journal objects"), - 'templatelist': _("presentation template: list of bullets"), - 'textcolor': _("holds current text color (can be used in place of a number block)"), - 'textsize': _("holds current text size (can be used in place of a number block)"), - 'toppos': _("ycor of top of screen"), - 'trash': _("Trashcan"), - 'turtle': _("Palette of turtle commands"), - 'until': _("do-until-True operator that uses boolean operators from Numbers palette"), - 'userdefined': _("runs code found in the tamyblock.py module found in the Journal"), - 'userdefined2args': _("runs code found in the tamyblock.py module found in the Journal"), - 'userdefined3args': _("runs code found in the tamyblock.py module found in the Journal"), - 'video': _("Sugar Journal video object"), - 'voltage': _("sensor voltage"), - 'volume': _("microphone input volume"), - 'vspace': _("jogs stack down"), - 'wait': _("pauses program execution a specified number of seconds"), - 'while': _("do-while-True operator that uses boolean operators from Numbers palette"), - 'width': _("the canvas width"), - 'xcor': _("holds current x-coordinate value of the turtle (can be used in place of a number block)"), - 'ycor': _("holds current y-coordinate value of the turtle (can be used in place of a number block)")} - -# # 'dead key' Unicode dictionaries # DEAD_KEYS = ['grave', 'acute', 'circumflex', 'tilde', 'diaeresis', 'abovering'] -DEAD_DICTS = [{'A':192, 'E':200, 'I':204, 'O':210, 'U':217, 'a':224, 'e':232, - 'i':236, 'o':242, 'u':249}, - {'A':193, 'E':201, 'I':205, 'O':211, 'U':218, 'a':225, 'e':233, - 'i':237, 'o':243, 'u':250}, - {'A':194, 'E':202, 'I':206, 'O':212, 'U':219, 'a':226, 'e':234, - 'i':238, 'o':244, 'u':251}, - {'A':195, 'O':211, 'N':209, 'U':360, 'a':227, 'o':245, 'n':241, - 'u':361}, - {'A':196, 'E':203, 'I':207, 'O':211, 'U':218, 'a':228, 'e':235, - 'i':239, 'o':245, 'u':252}, - {'A':197, 'a':229}] +DEAD_DICTS = [{'A': 192, 'E': 200, 'I': 204, 'O': 210, 'U': 217, 'a': 224, + 'e': 232, 'i': 236, 'o': 242, 'u': 249}, + {'A': 193, 'E': 201, 'I': 205, 'O': 211, 'U': 218, 'a': 225, + 'e': 233, 'i': 237, 'o': 243, 'u': 250}, + {'A': 194, 'E': 202, 'I': 206, 'O': 212, 'U': 219, 'a': 226, + 'e': 234, 'i': 238, 'o': 244, 'u': 251}, + {'A': 195, 'O': 211, 'N': 209, 'U': 360, 'a': 227, 'o': 245, + 'n': 241, 'u': 361}, + {'A': 196, 'E': 203, 'I': 207, 'O': 211, 'U': 218, 'a': 228, + 'e': 235, 'i': 239, 'o': 245, 'u': 252}, + {'A': 197, 'a': 229}] NOISE_KEYS = ['Shift_L', 'Shift_R', 'Control_L', 'Caps_Lock', 'Pause', 'Alt_L', 'Alt_R', 'KP_Enter', 'ISO_Level3_Shift', 'KP_Divide', 'Escape', 'Return', 'KP_Page_Up', 'Up', 'Down', 'Menu', diff --git a/TurtleArt/taexporthtml.py b/TurtleArt/taexporthtml.py index 09042f8..843b8b0 100644 --- a/TurtleArt/taexporthtml.py +++ b/TurtleArt/taexporthtml.py @@ -22,67 +22,68 @@ import pygtk pygtk.require('2.0') import gtk import os.path -from tautils import data_to_string, save_picture, image_to_base64 -from gettext import gettext as _ from cgi import escape +from gettext import gettext as _ +from tautils import data_to_string, save_picture, image_to_base64, get_path -def save_html(self, tw, embed_flag=True): - """ Either: Save canvas and code or pictures to HTML """ - self.embed_images = embed_flag - - # A dictionary to define the HTML wrappers around template elements - self.html_glue = { - 'doctype': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 ' + \ - 'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n', - 'html': ('<html>\n", "</html>\n'), - 'html_svg': ('<html xmlns="http://www.w3.org/1999/xhtml">\n', - '</html>\n'), - 'head': ('<head>\n<!-- Created by Turtle Art -->\n', '</head>\n'), - 'meta': '<meta http-equiv="content-type" content="text/html; ' + \ - 'charset=UTF-8"/>\n', - 'title': ('<title>', '</title>\n'), - 'style': ('<style type="text/css">\n<!--\n', '-->\n</style>\n'), - 'body': ('<body>\n', '\n</body>\n'), - 'div': ('<div>\n', '</div>\n'), - 'slide': ('\n<a name="slide', '"></a>\n'), - 'h1': ('<h1>', '</h1>\n'), - 'table': ('<table cellpadding="10\'>\n', '</table>\n'), - 'tr': ('<tr>\n', '</tr>\n'), - 'td': ('<td valign="top" width="400" height="300">\n', - '\n</td>\n'), - 'img': ('<img width="400" height="300" alt="Image" ' + \ +# A dictionary to define the HTML wrappers around template elements +HTML_GLUE = { + 'doctype': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 ' + \ + 'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n', + 'html': ('<html>\n', '</html>\n'), + 'html_svg': ('<html xmlns="http://www.w3.org/1999/xhtml">\n', + '</html>\n'), + 'head': ('<head>\n<!-- Created by Turtle Art -->\n', '</head>\n'), + 'meta': '<meta http-equiv="content-type" content="text/html; ' + \ + 'charset=UTF-8"/>\n', + 'title': ('<title>', '</title>\n'), + 'style': ('<style type="text/css">\n<!--\n', '-->\n</style>\n'), + 'body': ('<body>\n', '\n</body>\n'), + 'div': ('<div>\n', '</div>\n'), + 'slide': ('\n<a name="slide', '"></a>\n'), + 'h1': ('<h1>', '</h1>\n'), + 'table': ('<table cellpadding="10\'>\n', '</table>\n'), + 'tr': ('<tr>\n', '</tr>\n'), + 'td': ('<td valign="top" width="400" height="300">\n', + '\n</td>\n'), + 'img': ('<img width="400" height="300" alt="Image" ' + \ 'src="file://"', '".png" />\n'), - 'img2': ('<img alt="Image" src="image"', '".png" />\n'), - 'img3': ('<img alt="Image" src="file://"', '"" />\n'), - 'ul': ('<table>\n', '</table>\n'), - 'li': ('<tr><td>', '</td></tr>\n')} + 'img2': ('<img alt="Image" src="image"', '".png" />\n'), + 'img3': ('<img alt="Image" src="file://"', '"" />\n'), + 'ul': ('<table>\n', '</table>\n'), + 'li': ('<tr><td>', '</td></tr>\n')} - comment = '<!--\n\<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"' + \ - ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\n\ +COMMENT = '<!--\n\<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"' + \ + ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\n\ <!ENTITY ns_svg "http://www.w3.org/2000/svg">\n\ <!ENTITY ns_xlink "http://www.w3.org/1999/xlink">\n\ ]>\n\ -->\n' - if self.embed_images == True: - self.html_glue['img'] = ('<img width="400" height="300" alt=" + \ - ""Image" src="data:image/png;base64,\n"', - '" "/>\n') - self.html_glue['img2'] = ('<img alt="Image" src="data:image/png;" + \ - "base64,\n"', '" "/>\n') + + +def save_html(self, tw, embed_flag=True): + """ Either save the canvas and code or pictures to HTML """ + + if embed_flag: + HTML_GLUE['img'] = ('<img width="400" height="300" alt=' + \ + '"Image" src="data:image/png;base64,\n', + '"/>\n') + HTML_GLUE['img2'] = ('<img alt="Image" src="data:image/png;"' + \ + '"base64,\n"', '"/>\n') """ - If there are saved_pictures, put them into a .html; otherwise, save a - screendump and the turtle project code. + If there are saved_pictures, put them into a .html; otherwise, + save a screendump and the turtle project code. """ - code = '' + htmlcode = '' if len(tw.saved_pictures) > 0: for i, p in enumerate(tw.saved_pictures): - code += self.html_glue['slide'][0] + str(i) - code += self.html_glue['slide'][1] + \ - self.html_glue['div'][0] + \ - self.html_glue['h1'][0] - if self.embed_images == True: + htmlcode += HTML_GLUE['slide'][0] + str(i) + htmlcode += HTML_GLUE['slide'][1] + \ + HTML_GLUE['div'][0] + \ + HTML_GLUE['h1'][0] + if embed_flag: f = open(p, 'r') imgdata = f.read() f.close() @@ -90,10 +91,11 @@ def save_html(self, tw, embed_flag=True): tmp = imgdata else: pixbuf = gtk.gdk.pixbuf_new_from_file(p) - imgdata = image_to_base64(pixbuf, tw.activity) - tmp = self.html_glue['img2'][0] + imgdata = image_to_base64(pixbuf, + get_path(tw.activity, 'instance')) + tmp = HTML_GLUE['img2'][0] tmp += imgdata - tmp += self.html_glue['img2'][1] + tmp += HTML_GLUE['img2'][1] else: if p.endswith(('.svg')): f = open(p, 'r') @@ -101,49 +103,49 @@ def save_html(self, tw, embed_flag=True): f.close() tmp = imgdata else: - tmp = self.html_glue['img3'][0] + tmp = HTML_GLUE['img3'][0] tmp += p - tmp += self.html_glue['img3'][1] - code += tmp + \ - self.html_glue['h1'][1] + \ - self.html_glue['div'][1] + tmp += HTML_GLUE['img3'][1] + htmlcode += tmp + \ + HTML_GLUE['h1'][1] + \ + HTML_GLUE['div'][1] else: - if self.embed_images == True: + if embed_flag: imgdata = image_to_base64(save_picture(self.tw.canvas), - tw.activity) + get_path(tw.activity, 'instance')) else: imgdata = os.path.join(self.tw.load_save_folder, 'image') self.tw.save_as_image(imgdata) - code += (self.html_glue['img'][0] + imgdata + \ - self.html_glue['img'][1]) - code += self.html_glue['div'][0] - code += escape(data_to_string(tw.assemble_data_to_save(False, True))) - code += self.html_glue['div'][1] + htmlcode += (HTML_GLUE['img'][0] + imgdata + \ + HTML_GLUE['img'][1]) + htmlcode += HTML_GLUE['div'][0] + htmlcode += escape(data_to_string( + tw.assemble_data_to_save(False, True))) + htmlcode += HTML_GLUE['div'][1] if tw.running_sugar: title = _('Turtle Art') + ' ' + tw.activity.metadata['title'] else: title = _('Turtle Art') - header = self.html_glue['doctype'] + \ - self.html_glue['html'][0] - style = self.html_glue['style'][0] + \ - self.html_glue['style'][1] + header = HTML_GLUE['doctype'] + \ + HTML_GLUE['html'][0] + style = HTML_GLUE['style'][0] + \ + HTML_GLUE['style'][1] if len(tw.saved_pictures) > 0: if tw.saved_pictures[0].endswith(('.svg')): - header = self.html_glue['html_svg'][0] - style = comment + header = HTML_GLUE['html_svg'][0] + style = COMMENT - code = header + \ - self.html_glue['head'][0] + \ - self.html_glue['meta'] + \ - self.html_glue['title'][0] + \ + return header + \ + HTML_GLUE['head'][0] + \ + HTML_GLUE['meta'] + \ + HTML_GLUE['title'][0] + \ title + \ - self.html_glue['title'][1] + \ + HTML_GLUE['title'][1] + \ style + \ - self.html_glue['head'][1] + \ - self.html_glue['body'][0] + \ - code + \ - self.html_glue['body'][1] + \ - self.html_glue['html'][1] - return code + HTML_GLUE['head'][1] + \ + HTML_GLUE['body'][0] + \ + htmlcode + \ + HTML_GLUE['body'][1] + \ + HTML_GLUE['html'][1] diff --git a/TurtleArt/taexportlogo.py b/TurtleArt/taexportlogo.py index bd8a1e6..127689b 100644 --- a/TurtleArt/taexportlogo.py +++ b/TurtleArt/taexportlogo.py @@ -1,4 +1,4 @@ -#Copyright (c) 2008-10, Walter Bender +#Copyright (c) 2008-11, Walter Bender #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -18,351 +18,255 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -IGNORE = ['hideblocks', 'showblocks', 'fullscreen', 'polar', 'cartesian', - 'sandwichbottom', 'id'] +from gettext import gettext as _ -import math try: from sugar.datastore import datastore + HAS_DATASTORE = True except: - pass + HAS_DATASTORE = False + +from TurtleArt.tapalette import logo_commands, logo_functions +from TurtleArt.taconstants import TITLEXY, CONSTANTS def save_logo(tw): """ Set up the Turtle Art color palette and color processing. """ - color_processing = '\ -to tasetpalette :i :r :g :b :myshade \r\ -make "s ((:myshade - 50) / 50) \r\ -ifelse lessp :s 0 [ \r\ -make "s (1 + (:s *0.8)) \r\ -make "r (:r * :s) \r\ -make "g (:g * :s) \r\ -make "b (:b * :s) \r\ -] [ \ -make "s (:s * 0.9) \r\ -make "r (:r + ((99-:r) * :s)) \r\ -make "g (:g + ((99-:g) * :s)) \r\ -make "b (:b + ((99-:b) * :s)) \r\ -] \ -setpalette :i (list :r :g :b) \r\ -end \r\ -\ -to rgb :myi :mycolors :myshade \r\ -make "myr first :mycolors \r\ -make "mycolors butfirst :mycolors \r\ -make "myg first :mycolors \r\ -make "mycolors butfirst :mycolors \r\ -make "myb first :mycolors \r\ -make "mycolors butfirst :mycolors \r\ -tasetpalette :myi :myr :myg :myb :myshade \r\ -output :mycolors \r\ -end \r\ -\ -to processcolor :mycolors :myshade \r\ -if emptyp :mycolors [stop] \r\ -make "i :i + 1 \r\ -processcolor (rgb :i :mycolors :myshade) :myshade \r\ -end \r\ -\ -to tasetshade :shade \r\ -make "myshade modulo :shade 200 \r\ -if greaterp :myshade 99 [make "myshade (199-:myshade)] \r\ -make "i 7 \r\ -make "mycolors :colors \r\ -processcolor :mycolors :myshade \r\ -end \r\ -\ -to tasetpencolor :c \r\ -make "color (modulo (round :c) 100) \r\ -setpencolor :color + 8 \r\ -end \r\ -\ -make "colors [ \ -99 0 0 99 5 0 99 10 0 99 15 0 99 20 0 \ -99 25 0 99 30 0 99 35 0 99 40 0 99 45 0 \ -99 50 0 99 55 0 99 60 0 99 65 0 99 70 0 \ -99 75 0 99 80 0 99 85 0 99 90 0 99 95 0 \ -99 99 0 90 99 0 80 99 0 70 99 0 60 99 0 \ -50 99 0 40 99 0 30 99 0 20 99 0 10 99 0 \ - 0 99 0 0 99 5 0 99 10 0 99 15 0 99 20 \ - 0 99 25 0 99 30 0 99 35 0 99 40 0 99 45 \ - 0 99 50 0 99 55 0 99 60 0 99 65 0 99 70 \ - 0 99 75 0 99 80 0 99 85 0 99 90 0 99 95 \ - 0 99 99 0 95 99 0 90 99 0 85 99 0 80 99 \ - 0 75 99 0 70 99 0 65 99 0 60 99 0 55 99 \ - 0 50 99 0 45 99 0 40 99 0 35 99 0 30 99 \ - 0 25 99 0 20 99 0 15 99 0 10 99 0 5 99 \ - 0 0 99 5 0 99 10 0 99 15 0 99 20 0 99 \ -25 0 99 30 0 99 35 0 99 40 0 99 45 0 99 \ -50 0 99 55 0 99 60 0 99 65 0 99 70 0 99 \ -75 0 99 80 0 99 85 0 99 90 0 99 95 0 99 \ -99 0 99 99 0 90 99 0 80 99 0 70 99 0 60 \ -99 0 50 99 0 40 99 0 30 99 0 20 99 0 10] \r\ -make "shade 50 \r\ -tasetshade :shade \r' - - bs = tw.just_blocks() - code = '' + + # We need to catch several special cases: stacks, boxes, labels, etc. + dispatch_table = { + 'label': _add_label, + 'to action': _add_named_stack, + 'action': _add_reference_to_stack, + 'storeinbox': _add_named_box, + 'box': _add_reference_to_box + } + constants_table = { + 'lpos': _lpos, + 'tpos': _tpos, + 'rpos': _rpos, + 'bpos': _bpos, + 'red': _red, + 'orange': _orange, + 'yellow': _yellow, + 'green': _green, + 'cyan': _cyan, + 'blue': _blue, + 'purple': _purple, + 'white': _white, + 'black': _black, + 'titlex': _titlex, + 'titley': _titley, + 'leftx': _leftx, + 'topy': _topy, + 'rightx': _rightx, + 'bottomy': _bottomy, + 'width': _width, + 'height': _height + } + + stacks_of_blocks = tw.just_blocks() stack_count = 0 - show = 0 - - # These flags are used to trigger the prepending of additional procedures. - random = False - fillscreen = False - setcolor = False - setxy = False - setxy2 = False - pensize = False - setpensize = False - arc = False - heap = False - write = False - minus = False - division = False - image = False + + logocode = '' """ Walk through the code, substituting UCB Logo for Turtle Art primitives. """ - for b in bs: + for stack in stacks_of_blocks: this_stack = '' - data = walk_stack(tw.lc, b, tw.block_list.list) - # We need to catch several special cases: stacks, random, etc. - stack = False - namedstack = False - namedbox = False - refstack = False - refbox = False - myvar = '' - for d in data: - if type(d) == type((1, 2)): - (d, b) = d - if type(d) is float: - if namedbox: - myvar += str(d) - myvar += ' ' - elif write: - this_stack += 'labelsize ' - this_stack += str(d) - write = False - else: - this_stack += str(d) - elif show == 2: - # Use title for Journal objects - if d[0:8] == '#smedia_': - try: - dsobject = datastore.get(d[8:]) - this_stack += dsobject.metadata['title'] - dsobject.destroy() - except: - this_stack += str(d) - else: - this_stack += str(d) - show = 0 + psuedocode = _walk_stack(tw, stack) + if psuedocode == []: + continue + + skip = False + for i in range(len(psuedocode)): + if skip: + skip = False + continue + blk = psuedocode[i] + if type(blk) == type((1, 2)): + (blk, _blk_no) = blk + if blk in logo_commands: + logo_command = logo_commands[blk] else: - # Translate some Turtle Art primitives into UCB Logo - if namedstack: - this_stack += 'to ' - this_stack += d[2:].replace(' ', '_') - this_stack += '\r' - stack = True - namedstack = False - elif namedbox: - if d[0:2] == '#s': - this_stack += 'make "' - this_stack += d[2:].replace(' ', '_') - this_stack += ' ' - this_stack += myvar - namedbox = False - myvar = '' - else: - myvar += d - elif refstack: - this_stack += d[2:].replace(' ', '_') - this_stack += ' ' - refstack = False - elif refbox: - this_stack += ':' - this_stack += d[2:].replace(' ', '_') - refbox = False - elif d == 'stack': - refstack = True - elif d == 'box': - refbox = True - elif d == 'storeinbox': - namedbox = True - elif d == 'storeinbox1': - this_stack += 'make "box1' - elif d == 'box1': - this_stack += ':box1' - elif d == 'storeinbox2': - this_stack += 'make "box2' - elif d == 'box2': - this_stack += ':box2' - elif d == 'shade': - this_stack += ':shade' - elif d == 'setshade': - setcolor = True - this_stack += 'tasetshade' - elif d == 'color': - this_stack += 'pencolor' - elif d == 'nop': - this_stack += ' ' - elif d == 'start': - this_stack += 'to start\r' - stack = True - elif d == 'nop1': - this_stack += 'to stack1\r' - stack = True - elif d == 'nop2': - this_stack += 'to stack2\r' - stack = True - elif d == 'nop3': - namedstack = True - elif d == 'stopstack': - this_stack += 'stop' - elif d == 'clean': - this_stack += 'clearscreen' - elif d == 'setxy': - setxy = True - this_stack += 'tasetxypenup' - elif d == 'setxy2': - setxy2 = True - this_stack += 'tasetxy' - elif d == 'color': - this_stack += ':color' - elif d == 'plus': - this_stack += 'sum' - elif d == 'setcolor': - setcolor = True - this_stack += 'tasetpencolor' - elif d == 'fillscreen': - fillscreen = True - setcolor = True - this_stack += 'tasetbackground' - elif d == 'random': - random = True - this_stack += 'tarandom' - elif d == 'pensize': - pensize = True - this_stack += 'tapensize' - elif d == 'setpensize': - setpensize = True - this_stack += 'tasetpensize' - elif d == 'arc': - arc = True - this_stack += 'taarc' - elif d == 'pop': - heap = True - this_stack += 'tapop' - elif d == 'push': - heap = True - this_stack += 'tapush' - elif d == 'heap': - heap = True - this_stack += 'taprintheap' - elif d == 'emptyheap': - heap = True - this_stack += 'taclearheap' - elif d == 'kbinput': - this_stack += 'make "keyboard readchar' - elif d == 'keyboard': - this_stack += ':keyboard' - elif d == 'insertimage': - image = True - elif image: - # Skip this arg - image = 2 - elif image == 2: - # Skip this arg - image = False - elif d[0:2] == '#s': - # output single characters as a string - if len(d[2:]): - this_stack += '"' - this_stack += d[2:] - # make a sentence out of everything else - else: - this_stack += 'sentence ' - this_stack += d[2:].replace('\s', ' "') - this_stack += '\r' - elif d == 'write': - this_stack += 'label' - write = True - elif d == 'show' or d == 'showaligned': - this_stack += 'label' - show = 1 - elif d == 'minus2': - this_stack += 'taminus' - minus = True - elif d == 'division': - this_stack += 'quotient' - elif d == 'lpos': - this_stack += str(-tw.canvas.width / (tw.coord_scale * 2)) - elif d == 'rpos': - this_stack += str(tw.canvas.width / (tw.coord_scale * 2)) - elif d == 'bpos': - this_stack += str(-tw.canvas.height / (tw.coord_scale * 2)) - elif d == 'tpos': - this_stack += str(tw.canvas.height / (tw.coord_scale * 2)) - elif d in IGNORE: - this_stack += ' ' - elif show == 1 and d[0:2] == '#s': - this_stack += d[2:] - # We don't handle depreciated 'template' blocks + logo_command = None + if i == 0 and not logo_command in ['to stack1\r', 'to stack2\r', + 'to action', 'to start\r']: + this_stack = 'to turtleblocks_%d\r' % (stack_count) + stack_count += 1 + if logo_command in dispatch_table: + if i + 1 < len(psuedocode): + this_stack += dispatch_table[logo_command]( + psuedocode[i + 1]) + skip = True else: - this_stack += d + print 'missing arg to %s' % (logo_command) + elif logo_command in constants_table: + this_stack += str(constants_table[logo_command](tw)) + elif logo_command is not None: + this_stack += logo_command + else: # assume it is an argument + if not blk in ['nop', 'nop1', 'nop2', 'nop3']: + if type(blk) == str and blk[0:2] == '#s': + this_stack += str(blk[2:]).replace(' ', '_') + else: + this_stack += str(blk).replace(' ', '_') this_stack += ' ' - if stack: - stack = False - # if it is not a stack, we need to add a 'to ta#' label - elif len(data) > 0: - this_stack = 'to ta' + str(stack_count) + '\r' + this_stack - stack_count += 1 - if len(data) > 0: - code += this_stack - code += '\rend\r' - - # We need to define some additional procedures. - if minus: # Logo minus only takes one argument. - code = 'to taminus :y :x\routput sum :x minus :y\rend\r' + code - if random: # to avoid negative numbers - code = 'to tarandom :min :max\r' + \ - 'output (random (:max - :min)) + :min\rend\r' + code - if fillscreen: # Set shade than background color - code = 'to tasetbackground :color :shade\r' + \ - 'tasetshade :shade\rsetbackground :color\rend\r' + code - if setcolor: # Load the Turtle Art color palette. - code = color_processing + code - if setpensize: # Set int of pensize - code = 'to tasetpensize :a\rsetpensize round :a\rend\r' + code - if pensize: # Return only the first argument. - code = 'to tapensize\routput first round pensize\rend\r' + code - if setxy2: # Swap and round arguments - code = 'to tasetxy :x :y\rsetxy :x :y\rend\r' + code - if setxy: # Swap and round arguments and add pen up/down - code = 'to tasetxy :x :y\rpenup\rsetxy :x :y\rpendown\rend\r' + code - if arc: # Turtle Art 'arc' needs to be redefined. - c = (2 * math.pi) / 360 - code = 'to taarc :a :r\rrepeat round :a [right 1 forward (' + \ - str(c) + ' * :r)]\rend\r' + code - if heap: # Add psuedo 'push' and 'pop' - code = 'to tapush :foo\rmake "taheap fput :foo :taheap\rend\r' + \ - 'to tapop\rif emptyp :taheap [stop]\rmake \'tmp first :taheap\r' +\ - 'make "taheap butfirst :taheap\routput :tmp\rend\r' + \ - 'to taclearheap\rmake "taheap []\rend\r' + \ - 'to taprintheap \rprint :taheap\rend\r' + \ - 'make "taheap []\r' + code - code = 'window\r' + code - return code - - -def walk_stack(lc, blk, list): + + logocode += this_stack + logocode += '\rend\r' + + # We may need to prepend some additional procedures. + for key in logo_functions.iterkeys(): + if key in logocode: + logocode = logo_functions[key] + logocode + + if 'tasetshade' in logocode or 'tasetpencolor' in logocode or \ + 'tasetbackground' in logocode: + logocode = logo_functions['tacolor'] + logocode + + logocode = 'window\r' + logocode + return logocode + + +def _add_label(string): + if type(string) == str and string[0:8] in ['#smedia_', '#saudio_', + '#svideo_', '#sdescr_']: + string = string[8:] + if HAS_DATASTORE: + dsobject = datastore.get(string[8:]) + if 'title' in dsobject.metadata: + string = dsobject.metadata['title'] + else: + string = str(string) + if string[0:2] == '#s': + string = string[2:] + string = '"' + string + if string.count(' ') > 0: + return 'label sentence %s\r' % (string.replace(' ', ' "')) + else: + return 'label %s' % (string.replace(' ', '_')) + + +def _add_named_stack(action): + if type(action) == str and action[0:2] == '#s': + return 'to %s\r' % (str(action[2:]).replace(' ', '_')) + else: + return 'to %s\r' % (str(action).replace(' ', '_')) + + +def _add_reference_to_stack(action): + if type(action) == str and action[0:2] == '#s': + return '%s' % (str(action[2:]).replace(' ', '_')) + else: + return '%s' % (str(action).replace(' ', '_')) + + +def _add_named_box(box_name): + if type(box_name) == str and box_name[0:2] == '#s': + return 'make "%s' % (str(box_name[2:]).replace(' ', '_')) + else: + return 'make "%s' % (str(box_name).replace(' ', '_')) + + +def _add_reference_to_box(box_name): + if type(box_name) == str and box_name[0:2] == '#s': + return ':%s' % (str(box_name[2:]).replace(' ', '_')) + else: + return ':%s' % (str(box_name).replace(' ', '_')) + + +def _lpos(tw): + return int(-tw.canvas.width / (tw.coord_scale * 2)) + + +def _tpos(tw): + return int(tw.canvas.height / (tw.coord_scale * 2)) + + +def _rpos(tw): + return int(tw.canvas.width / (tw.coord_scale * 2)) + + +def _bpos(tw): + return int(-tw.canvas.height / (tw.coord_scale * 2)) + + +def _width(tw): + return int(tw.canvas.width / tw.coord_scale) + + +def _height(tw): + int(tw.canvas.height / tw.coord_scale) + + +def _titlex(tw): + return int(-(tw.canvas.width * TITLEXY[0]) / (tw.coord_scale * 2)) + + +def _titley(tw): + return int((tw.canvas.height * TITLEXY[1]) / (tw.coord_scale * 2)) + + +def _leftx(tw): + return int(-(tw.canvas.width * TITLEXY[0]) / (tw.coord_scale * 2)) + + +def _topy(tw): + return int((tw.canvas.height * (TITLEXY[1] - 0.125)) / (tw.coord_scale * 2)) + + +def _rightx(tw): + return 0 + + +def _bottomy(tw): + return 0 + + +def _red(tw): + return CONSTANTS['red'] + + +def _orange(tw): + return CONSTANTS['orange'] + + +def _yellow(tw): + return CONSTANTS['yellow'] + + +def _green(tw): + return CONSTANTS['green'] + + +def _cyan(tw): + return CONSTANTS['cyan'] + + +def _blue(tw): + return CONSTANTS['blue'] + + +def _purple(tw): + return CONSTANTS['purple'] + + +def _white(tw): + return 1 + + +def _black(tw): + return 0 + + +def _walk_stack(tw, blk_in_stack): """ Convert blocks to logo psuedocode. """ from tautils import find_top_block - top = find_top_block(blk) - if blk == top: - code = lc.run_blocks(top, list, False) - return code + top = find_top_block(blk_in_stack) + if blk_in_stack == top: + psuedocode = tw.lc.run_blocks(top, tw.block_list.list, False) + return psuedocode else: return [] diff --git a/TurtleArt/tagettext.py b/TurtleArt/tagettext.py new file mode 100644 index 0000000..ebeebb3 --- /dev/null +++ b/TurtleArt/tagettext.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2010-11 Walter Bender, Martin Langhoff +# License: GPLv2 + +# Defines the magic global _() with the right params so all modules +# can use it. +# +# Plugins that want to override MUST use a different technique. See +# the developer notes in the TA wikipage. +# +import gettext +import os + +# In a git checkout, locale is in the root of the project +# so one dir "up" from tagettext.py +localedir = os.path.join(os.path.dirname(os.path.dirname(__file__)), + 'locale' ) + +if os.path.exists(localedir): + # works from a git checkout + gettext.install('org.laptop.TurtleArtActivity', localedir) +else: + # fallback for packaged TA (rpm, xo) + gettext.install('org.laptop.TurtleArtActivity') diff --git a/TurtleArt/tajail.py b/TurtleArt/tajail.py index 081248f..0444dc7 100644 --- a/TurtleArt/tajail.py +++ b/TurtleArt/tajail.py @@ -19,14 +19,14 @@ #THE SOFTWARE. # A naive approach to running myfunc in a jail -import logging -_logger = logging.getLogger('turtleart-activity') import traceback from time import * from math import * +from gettext import gettext as _ def myfunc(f, args): + ''' Run inline Python code ''' # check to make sure no import calls are made if len(args) == 1: myf = 'def f(x): return ' + f.replace('import', '') @@ -45,11 +45,16 @@ def myfunc(f, args): return userdefined.values()[0](args[0], args[1], args[2]) -def myfunc_import(lc, f, x): +def myfunc_import(parent, f, x): + ''' Run Python code imported from Journal ''' + if 'def myblock(lc,' in f: + base_class = parent.tw.lc # pre-v107, we passed lc + else: + base_class = parent.tw # as of v107, we pass tw userdefined = {} try: exec f in globals(), userdefined - return userdefined['myblock'](lc, x) + return userdefined['myblock'](base_class, x) except: traceback.print_exc() return None diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py index e3576fa..12be92f 100644 --- a/TurtleArt/talogo.py +++ b/TurtleArt/talogo.py @@ -22,65 +22,22 @@ #THE SOFTWARE. import gtk - from time import time -from math import sqrt -from numpy import append -from numpy.fft import rfft -from random import uniform -from operator import isNumberType -from fcntl import ioctl - -import os.path +from operator import isNumberType from UserDict import UserDict -try: - from sugar.datastore import datastore -except ImportError: - pass - -from taconstants import TAB_LAYER, BLACK, WHITE, \ - DEFAULT_SCALE, ICON_SIZE, BLOCK_NAMES, CONSTANTS, SENSOR_DC_NO_BIAS, \ - SENSOR_DC_BIAS, XO1, XO15 -from tagplay import play_audio_from_file, play_movie_from_file, stop_media, \ - media_playing -from tacamera import Camera -import v4l2 -from tajail import myfunc, myfunc_import +from taconstants import TAB_LAYER, DEFAULT_SCALE, PREFIX_DICTIONARY +from tapalette import block_names, value_blocks from tautils import get_pixbuf_from_journal, convert, data_from_file, \ - text_media_type, round_int, chr_to_ord, strtype, get_path - -from RtfParser import RtfTextOnly + text_media_type, round_int, debug_output -from ringbuffer import RingBuffer1d +from util.RtfParser import RtfTextOnly from gettext import gettext as _ -VALUE_BLOCKS = ['box1', 'box2', 'color', 'shade', 'gray', 'scale', 'pensize', - 'heading', 'xcor', 'ycor', 'pop', 'see', 'keyboard', - 'sound', 'volume', 'pitch', 'resistance', 'voltage', - 'luminance'] - -import logging -_logger = logging.getLogger('turtleart-activity') - - -def find_device(): - """ Search for RFID devices. Return a device instance or None. """ - device = None - for i in os.listdir(os.path.join('.', 'devices')): - if not os.path.isdir(os.path.join('.', 'devices', i)): - try: - _tempmod = __import__('devices.%s' % i.split('.')[0], - globals(), locals(), ['RFIDReader'], -1) - devtemp = _tempmod.RFIDReader() - if devtemp.get_present() == True: - device = devtemp - except Exception, e: - _logger.error('FIND_DEVICE: %s: %s' % (i, e)) - pass - return device +media_blocks_dictionary = {} # new media blocks get added here +primitive_dictionary = {} # new block primitives get added here class noKeyError(UserDict): @@ -112,213 +69,6 @@ class logoerror(Exception): # Utility functions - -def _num_type(x): - """ Is x a number type? """ - if type(x) == int: - return True - if type(x) == float: - return True - if type(x) == ord: - return True - return False - - -def _string_to_num(x): - """ Try to comvert a string to a number """ - xx = convert(x.replace(self.tw.decimal_point, '.'), float) - if type(xx) is float: - return xx - else: - xx, xflag = chr_to_ord(x) - if xflag: - return xx - else: - raise logoerror("#syntaxerror") - - -def _and(x, y): - """ Logical and """ - return x & y - - -def _or(x, y): - """ Logical or """ - return x | y - - -def _careful_divide(x, y): - """ Raise error on divide by zero """ - try: - return x / y - except ZeroDivisionError: - raise logoerror("#zerodivide") - except TypeError: - try: - return _string_to_num(x) / _string_to_num(y) - except ZeroDivisionError: - raise logoerror("#zerodivide") - except ValueError: - raise logoerror("#syntaxerror") - except TypeError: - raise logoerror("#notanumber") - - -def _equal(x, y): - """ Numeric and logical equal """ - try: - return float(x) == float(y) - except TypeError: - typex, typey = False, False - if strtype(x): - typex = True - if strtype(y): - typey = True - if typex and typey: - return x == y - try: - return _string_to_num(x) == _string_to_num(y) - except ValueError: - raise logoerror("#syntaxerror") - - -def _less(x, y): - """ Compare numbers and strings """ - try: - return float(x) < float(y) - except ValueError: - typex, typey = False, False - if strtype(x): - typex = True - if strtype(y): - typey = True - if typex and typey: - return x < y - try: - return _string_to_num(x) < _string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - - -def _more(x, y): - """ Compare numbers and strings """ - return _less(y, x) - - -def _plus(x, y): - """ Add numbers, concat strings """ - if _num_type(x) and _num_type(y): - return(x + y) - else: - if _num_type(x): - xx = str(round_int(x)) - else: - xx = str(x) - if _num_type(y): - yy = str(round_int(y)) - else: - yy = str(y) - return(xx + yy) - - -def _minus(x, y): - """ Numerical subtraction """ - if _num_type(x) and _num_type(y): - return(x - y) - try: - return _string_to_num(x) - _string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - - -def _product(x, y): - """ Numerical multiplication """ - if _num_type(x) and _num_type(y): - return(x * y) - try: - return _string_to_num(x) * _string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - - -def _mod(x, y): - """ Numerical mod """ - if _num_type(x) and _num_type(y): - return(x % y) - try: - return _string_to_num(x) % _string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - except ValueError: - raise logoerror("#syntaxerror") - - -def _sqrt(x): - """ Square root """ - if _num_type(x): - if x < 0: - raise logoerror("#negroot") - return sqrt(x) - try: - return sqrt(_string_to_num(x)) - except ValueError: - raise logoerror("#negroot") - except TypeError: - raise logoerror("#notanumber") - - -def _random(x, y): - """ Random integer """ - if _num_type(x) and _num_type(y): - return(int(round(uniform(x, y), 0))) - xx, xflag = chr_to_ord(x) - yy, yflag = chr_to_ord(y) - if xflag and yflag: - return chr(int(round(uniform(xx, yy), 0))) - if not xflag: - xx = _string_to_num(x) - if not yflag: - yy = _string_to_num(y) - try: - return(int(round(uniform(xx, yy), 0))) - except TypeError: - raise logoerror("#notanumber") - - -def _identity(x): - """ Identity function """ - return(x) - - -def _avg(array, abs_value=False): - """ Calc. the average value of an array """ - if len(array) == 0: - return 0 - array_sum = 0 - if abs_value: - for a in array: - array_sum += abs(a) - else: - for a in array: - array_sum += a - return float(array_sum) / len(array) - - -def stop_logo(tw): - """ Stop logo is called from the Stop button on the toolbar """ - tw.step_time = 0 - tw.lc.step = _just_stop() - stop_media(tw.lc) - if tw.camera_available: - if tw.lc._video_capture_device is not None: - # restore AG and then close device - tw.lc._set_ag(1) - tw.lc._video_capture_device.close() - tw.lc._video_capture_device = None - tw.lc.camera.stop_camera_input() - tw.active_turtle.show() - - def _just_stop(): """ yield False to stop stack """ yield False @@ -337,164 +87,15 @@ class LogoCode: self.tw = tw self.oblist = {} - DEFPRIM = { - '(': [1, lambda self, x: self._prim_opar(x)], - 'and': [2, lambda self, x, y: _and(x, y)], - 'arc': [2, lambda self, x, y: self._prim_arc(self.tw.canvas.arc, x, - y)], - 'audio': [1, lambda self, x: self._play_sound(x)], - 'back': [1, lambda self, x: self._prim_move(self.tw.canvas.forward, - -x)], - 'black': [0, lambda self: BLACK], - 'blue': [0, lambda self: CONSTANTS['blue']], - 'bpos': [0, lambda self: CONSTANTS['bottompos']], - 'boty': [0, lambda self: CONSTANTS['bottomy']], - 'box1': [0, lambda self: self.boxes['box1']], - 'box': [1, lambda self, x: self._box(x)], - 'box2': [0, lambda self: self.boxes['box2']], - 'bullet': [1, self._prim_bullet, True], - 'bulletlist': [1, self._prim_list, True], - 'cartesian': [0, lambda self: self.tw.set_cartesian(True)], - 'clean': [0, lambda self: self.prim_clear()], - 'clearheap': [0, lambda self: self._empty_heap()], - 'color': [0, lambda self: self.tw.canvas.color], - 'gray': [0, lambda self: self.tw.canvas.gray], - 'comment': [1, lambda self, x: self._prim_print(x, True)], - 'container': [1, lambda self, x: x], - 'cyan': [0, lambda self: CONSTANTS['cyan']], - 'define': [2, self._prim_define], - 'division': [2, lambda self, x, y: _careful_divide(x, y)], - 'equal?': [2, lambda self, x, y: _equal(x, y)], - 'fillscreen': [2, lambda self, x, y: self.tw.canvas.fillscreen(x, y)], - 'forever': [1, self._prim_forever, True], - 'forward': [1, lambda self, x: self._prim_move(self.tw.canvas.forward, - x)], - 'fullscreen': [0, lambda self: self.tw.set_fullscreen()], - 'greater?': [2, lambda self, x, y: _more(x, y)], - 'green': [0, lambda self: CONSTANTS['green']], - 'heading': [0, lambda self: self.tw.canvas.heading], - 'hideblocks': [0, lambda self: self.tw.hideblocks()], - 'hres': [0, lambda self: CONSTANTS['width']], - 'id': [1, lambda self, x: _identity(x)], - 'if': [2, self._prim_if, True], - 'ifelse': [3, self._prim_ifelse, True], - 'insertimage': [1, lambda self, x: self._insert_image(False, - filepath=x)], - 'kbinput': [0, lambda self: self._prim_kbinput()], - 'keyboard': [0, lambda self: self.keyboard], - 'left': [1, lambda self, x: self._prim_right(-x)], - 'leftx': [0, lambda self: CONSTANTS['leftx']], - 'lpos': [0, lambda self: CONSTANTS['leftpos']], - 'less?': [2, lambda self, x, y: _less(x, y)], - 'luminance': [0, lambda self: self._read_camera(True)], - 'mediawait': [0, self._media_wait, True], - 'minus': [2, lambda self, x, y: _minus(x, y)], - 'mod': [2, lambda self, x, y: _mod(x, y)], - 'myfunction': [2, lambda self, f, x: self._myfunction(f, [x])], - 'myfunction2': [3, lambda self, f, x, y: self._myfunction(f, [x, y])], - 'myfunction3': [4, lambda self, f, x, y, z: self._myfunction( - f, [x, y, z])], - 'nop': [0, lambda self: None], - 'nop1': [0, lambda self: None], - 'nop2': [0, lambda self: None], - 'nop3': [1, lambda self, x: None], - 'not': [1, lambda self, x: not x], - 'orange': [0, lambda self: CONSTANTS['orange']], - 'or': [2, lambda self, x, y: _or(x, y)], - 'pendown': [0, lambda self: self.tw.canvas.setpen(True)], - 'pensize': [0, lambda self: self.tw.canvas.pensize], - 'penup': [0, lambda self: self.tw.canvas.setpen(False)], - 'pitch': [0, lambda self: self._get_pitch()], - 'plus': [2, lambda self, x, y: _plus(x, y)], - 'polar': [0, lambda self: self.tw.set_polar(True)], - 'pop': [0, lambda self: self._prim_pop()], - 'print': [1, lambda self, x: self._prim_print(x, False)], - 'printheap': [0, lambda self: self._prim_print_heap()], - 'product': [2, lambda self, x, y: _product(x, y)], - 'purple': [0, lambda self: CONSTANTS['purple']], - 'push': [1, lambda self, x: self._prim_push(x)], - 'random': [2, lambda self, x, y: _random(x, y)], - 'readcamera': [0, lambda self: self._read_camera()], - 'readpixel': [0, lambda self: self._read_pixel()], - 'red': [0, lambda self: CONSTANTS['red']], - 'repeat': [2, self._prim_repeat, True], - 'resistance': [0, lambda self: self._get_resistance()], - 'rfid': [0, lambda self: self.tw.rfid_idn], - 'right': [1, lambda self, x: self._prim_right(x)], - 'rightx': [0, lambda self: CONSTANTS['rightx']], - 'rpos': [0, lambda self: CONSTANTS['rightpos']], - 'savepix': [1, lambda self, x: self._save_picture(x)], - 'savesvg': [1, lambda self, x: self._save_svg(x)], - 'scale': [0, lambda self: self.scale], - 'see': [0, lambda self: self.see()], - 'setcolor': [1, lambda self, x: self._prim_set('color', - self.tw.canvas.setcolor, x)], - 'setgray': [1, lambda self, x: self._prim_set('gray', - self.tw.canvas.setgray, x)], - 'seth': [1, lambda self, x: self._prim_set('heading', - self.tw.canvas.seth, x)], - 'setpensize': [1, lambda self, x: self._prim_set('pensize', - self.tw.canvas.setpensize, x)], - 'setscale': [1, lambda self, x: self._prim_set('scale', - self._set_scale, x)], - 'setshade': [1, lambda self, x: self._prim_set('shade', - self.tw.canvas.setshade, x)], - 'settextcolor': [1, lambda self, x: self.tw.canvas.settextcolor(x)], - 'settextsize': [1, lambda self, x: self.tw.canvas.settextsize(x)], - 'setxy2': [2, lambda self, x, y: self._prim_move(self.tw.canvas.setxy, - x, y)], - 'setxy': [2, lambda self, x, y: self._prim_move(self.tw.canvas.setxy, - x, y, pendown=False)], - 'shade': [0, lambda self: self.tw.canvas.shade], - 'show': [1, lambda self, x: self._show(x, True)], - 'showaligned': [1, lambda self, x: self._show(x, False)], - 'showblocks': [0, lambda self: self.tw.showblocks()], - 'skin': [1, lambda self, x: self._reskin(x)], - 'sound': [0, lambda self: self._get_sound()], - 'sqrt': [1, lambda self, x: _sqrt(x)], - 'stack1': [0, self._prim_stack1, True], - 'stack': [1, self._prim_stack, True], - 'stack2': [0, self._prim_stack2, True], - 'start': [0, lambda self: self._prim_start()], - 'startfill': [0, lambda self: self.tw.canvas.start_fill()], - 'stopfill': [0, lambda self: self.tw.canvas.stop_fill()], - 'stopstack': [0, lambda self: self._prim_stopstack()], - 'storeinbox1': [1, lambda self, x: self._prim_setbox('box1', None, x)], - 'storeinbox2': [1, lambda self, x: self._prim_setbox('box2', None, x)], - 'storeinbox': [2, lambda self, x, y: self._prim_setbox('box3', x, y)], - 't1x1': [2, lambda self, x, y: self._show_template1x1(x, y)], - 't1x1a': [2, lambda self, x, y: self._show_template1x1a(x, y)], - 't1x2': [3, lambda self, x, y, z: self._show_template1x2(x, y, z)], - 't2x1': [3, lambda self, x, y, z: self._show_template2x1(x, y, z)], - 't2x2': [5, lambda self, x, y, z, a, b: self._show_template2x2( - x, y, z, a, b)], - 'textcolor': [0, lambda self: self.tw.canvas.textcolor], - 'textsize': [0, lambda self: self.tw.textsize], - 'titlex': [0, lambda self: CONSTANTS['titlex']], - 'titley': [0, lambda self: CONSTANTS['titley']], - 'topy': [0, lambda self: CONSTANTS['topy']], - 'tpos': [0, lambda self: CONSTANTS['toppos']], - 'turtle': [1, lambda self, x: self.tw.canvas.set_turtle(x)], - 'userdefined': [1, lambda self, x: self._prim_myblock([x])], - 'userdefined2': [2, lambda self, x, y: self._prim_myblock([x, y])], - 'userdefined3': [3, lambda self, x, y, - z: self._prim_myblock([x, y, z])], - 'video': [1, lambda self, x: self._play_video(x)], - 'voltage': [0, lambda self: self._get_voltage()], - 'volume': [0, lambda self: self._get_volume()], - 'vres': [0, lambda self: CONSTANTS['height']], - 'wait': [1, self._prim_wait, True], - 'white': [0, lambda self: WHITE], - 'write': [2, lambda self, x, y: self._write(self, x, y)], - 'xcor': [0, lambda self: self.tw.canvas.xcor / self.tw.coord_scale], - 'ycor': [0, lambda self: self.tw.canvas.ycor / self.tw.coord_scale], - 'yellow': [0, lambda self: CONSTANTS['yellow']]} + DEFPRIM = {'(': [1, lambda self, x: self._prim_opar(x)], + 'define': [2, self._prim_define], + 'nop': [0, lambda self: None]} for p in iter(DEFPRIM): if len(DEFPRIM[p]) == 2: - self._def_prim(p, DEFPRIM[p][0], DEFPRIM[p][1]) + self.def_prim(p, DEFPRIM[p][0], DEFPRIM[p][1]) else: - self._def_prim(p, DEFPRIM[p][0], DEFPRIM[p][1], DEFPRIM[p][2]) + self.def_prim(p, DEFPRIM[p][0], DEFPRIM[p][1], DEFPRIM[p][2]) self.symtype = type(self._intern('print')) self.listtype = type([]) @@ -520,38 +121,26 @@ class LogoCode: self.trace = 0 self.update_values = False self.gplay = None - self.ag = None self.filepath = None self.dsobject = None + self.start_time = None - # Scale factors for depreciated portfolio blocks - self.title_height = int((self.tw.canvas.height / 20) * self.tw.scale) self.body_height = int((self.tw.canvas.height / 40) * self.tw.scale) - self.bullet_height = int((self.tw.canvas.height / 30) * self.tw.scale) self.scale = DEFAULT_SCALE - self.max_samples = 1500 - self.input_step = 1 - - self.ringbuffer = RingBuffer1d(self.max_samples, dtype='int16') - if self.tw.hw == XO1: - self.voltage_gain = 0.00002225 - self.voltage_bias = 1.140 - elif self.tw.hw == XO15: - self.voltage_gain = -0.0001471 - self.voltage_bias = 1.695 - - if self.tw.camera_available: - self._video_capture_device = None - if self.tw.running_sugar: - self.imagepath = get_path(self.tw.activity, - 'data/turtlepic.png') - else: - self.imagepath = '/tmp/turtlepic.png' - self.camera = Camera(self.imagepath) + def stop_logo(self): + """ Stop logo is called from the Stop button on the toolbar """ + self.tw.step_time = 0 + self.step = _just_stop() + for plugin in self.tw._plugins: + plugin.stop() + if self.tw.gst_available: + from tagplay import stop_media + stop_media(self) + self.tw.active_turtle.show() - def _def_prim(self, name, args, fcn, rprim=False): + def def_prim(self, name, args, fcn, rprim=False): """ Define the primitives associated with the blocks """ sym = self._intern(name) sym.nargs, sym.fcn = args, fcn @@ -573,8 +162,6 @@ class LogoCode: self.stacks['stack2'] = None self.tw.saving_svg = False - self.find_value_blocks() - self._update_audio_mode() if self.trace > 0: self.update_values = True else: @@ -592,7 +179,11 @@ class LogoCode: if b.connections is not None and len(b.connections) > 1 and \ b.connections[1] is not None: code = self._blocks_to_code(b) - x = b.connections[1].values[0] + try: + x = b.connections[1].values[0] + except IndexError: + self.tw.showlabel('#nostack') + return None if type(convert(x, float, False)) == float: if int(float(x)) == x: x = int(x) @@ -600,7 +191,8 @@ class LogoCode: code = self._blocks_to_code(blk) if run_flag: - _logger.debug("running code: %s" % (code)) + # debug_output("running code: %s" % (code), self.tw.running_sugar) + self.start_time = time() self._setup_cmd(code) if not self.tw.hide: self.tw.display_coordinates() @@ -625,35 +217,22 @@ class LogoCode: code.append(float(blk.values[0])) except ValueError: code.append(float(ord(blk.values[0][0]))) - elif blk.name == 'string' or blk.name == 'title': + elif blk.name == 'string' or \ + blk.name == 'title': # deprecated block if type(blk.values[0]) == float or type(blk.values[0]) == int: if int(blk.values[0]) == blk.values[0]: blk.values[0] = int(blk.values[0]) code.append('#s' + str(blk.values[0])) else: code.append('#s' + blk.values[0]) - elif blk.name == 'journal': - if blk.values[0] is not None: - code.append('#smedia_' + str(blk.values[0])) - else: - code.append('#smedia_None') - elif blk.name == 'description': + elif blk.name in PREFIX_DICTIONARY: if blk.values[0] is not None: - code.append('#sdescr_' + str(blk.values[0])) + code.append(PREFIX_DICTIONARY[blk.name] + \ + str(blk.values[0])) else: - code.append('#sdescr_None') - elif blk.name == 'audio': - if blk.values[0] is not None: - code.append('#saudio_' + str(blk.values[0])) - else: - code.append('#saudio_None') - elif blk.name == 'video': - if blk.values[0] is not None: - code.append('#svideo_' + str(blk.values[0])) - else: - code.append('#svideo_None') - elif blk.name == 'camera': - code.append('#smedia_CAMERA') + code.append(PREFIX_DICTIONARY[blk.name] + 'None') + elif blk.name in media_blocks_dictionary: + code.append('#smedia_' + blk.name.upper()) else: return ['%nothing%'] else: @@ -718,7 +297,7 @@ class LogoCode: elif self.tw.interactive_mode: self.tw.toolbar_shapes['stopiton'].set_layer(TAB_LAYER) self.running = True - self._icall(self._evline, blklist) + self.icall(self.evline, blklist) yield True if self.tw.running_sugar: self.tw.activity.stop_turtle_button.set_icon("stopitoff") @@ -727,12 +306,12 @@ class LogoCode: yield False self.running = False - def _icall(self, fcn, *args): + def icall(self, fcn, *args): """ Add a function and its arguments to the program stack. """ self.istack.append(self.step) self.step = fcn(*(args)) - def _evline(self, blklist): + def evline(self, blklist): """ Evaluate a line of code from the list. """ oldiline = self.iline self.iline = blklist[:] @@ -762,7 +341,7 @@ class LogoCode: (token, self.bindex) = self.iline[1] # Process the token and any arguments. - self._icall(self._eval) + self.icall(self._eval) yield True # Time to unhighlight the current block. @@ -778,7 +357,7 @@ class LogoCode: self.tw.block_list.list[self.bindex].highlight() raise logoerror(str(self.iresult)) self.iline = oldiline - self._ireturn() + self.ireturn() if not self.tw.hide and self.tw.step_time > 0: self.tw.display_coordinates() yield True @@ -795,7 +374,7 @@ class LogoCode: # We highlight blocks here in case an error occurs... if not self.tw.hide and bindex is not None: self.tw.block_list.list[bindex].highlight() - self._icall(self._evalsym, token) + self.icall(self._evalsym, token) yield True # and unhighlight if everything was OK. if not self.tw.hide and bindex is not None: @@ -804,7 +383,7 @@ class LogoCode: else: res = token - self._ireturn(res) + self.ireturn(res) yield True def _evalsym(self, token): @@ -817,32 +396,31 @@ class LogoCode: raise logoerror("#noinput") for i in range(token.nargs): self._no_args_check() - self._icall(self._eval) + self.icall(self._eval) yield True self.arglist.append(self.iresult) if self.cfun.rprim: if type(self.cfun.fcn) == self.listtype: - _logger.debug("evalsym rprim list: %s" % (str(token))) - self._icall(self._ufuncall, self.cfun.fcn) + # debug_output('evalsym rprim list: %s' % (str(token)), + # self.tw.running_sugar) + self.icall(self._ufuncall, self.cfun.fcn) yield True else: - # print "evalsym rprim: ", token - self._icall(self.cfun.fcn, *self.arglist) + self.icall(self.cfun.fcn, *self.arglist) yield True result = None else: - # print "evalsym: ", token result = self.cfun.fcn(self, *self.arglist) self.cfun, self.arglist = oldcfun, oldarglist if self.arglist is not None and result == None: raise logoerror("%s %s %s" % \ (oldcfun.name, _("did not output to"), self.cfun.name)) - self._ireturn(result) + self.ireturn(result) yield True def _ufuncall(self, body): """ ufuncall """ - self._ijmp(self._evline, body) + self.ijmp(self.evline, body) yield True def doevalstep(self): @@ -869,12 +447,12 @@ class LogoCode: return False return True - def _ireturn(self, res=None): + def ireturn(self, res=None): """ return value """ self.step = self.istack.pop() self.iresult = res - def _ijmp(self, fcn, *args): + def ijmp(self, fcn, *args): """ ijmp """ self.step = fcn(*(args)) @@ -898,123 +476,6 @@ class LogoCode: # Primitives # - def prim_clear(self): - """ Clear screen """ - stop_media(self) - self.tw.canvas.clearscreen() - self.scale = DEFAULT_SCALE - self.tw.set_polar(False) - self.tw.set_cartesian(False) - self.hidden_turtle = None - for name in VALUE_BLOCKS: - self.update_label_value(name) - - def _prim_start(self): - """ Start block: recenter """ - if self.tw.running_sugar: - self.tw.activity.recenter() - - def _prim_wait(self, time): - """ Show the turtle while we wait """ - self.tw.active_turtle.show() - endtime = _millisecond() + time * 1000. - while _millisecond() < endtime: - yield True - self.tw.active_turtle.hide() - self._ireturn() - yield True - - def _prim_repeat(self, num, blklist): - """ Repeat list num times. """ - num = self._int(num) - for i in range(num): - self._icall(self._evline, blklist[:]) - yield True - if self.procstop: - break - self._ireturn() - yield True - - def _prim_bullet(self, blklist): - """ Depreciated bullet-list block style """ - self._show_bullets(blklist) - self._ireturn() - yield True - - def _prim_list(self, blklist): - """ Expandable list block """ - self._show_list(blklist) - self._ireturn() - yield True - - def _myfunction(self, f, x): - """ Programmable block """ - try: - y = myfunc(f, x) - if str(y) == 'nan': - _logger.debug("python function returned nan") - stop_logo(self.tw) - raise logoerror("#notanumber") - else: - return y - except ZeroDivisionError: - stop_logo(self.tw) - raise logoerror("#zerodivide") - except ValueError, e: - stop_logo(self.tw) - raise logoerror('#' + str(e)) - except SyntaxError, e: - stop_logo(self.tw) - raise logoerror('#' + str(e)) - except NameError, e: - stop_logo(self.tw) - raise logoerror('#' + str(e)) - except OverflowError: - stop_logo(self.tw) - raise logoerror("#overflowerror") - except TypeError: - stop_logo(self.tw) - raise logoerror("#notanumber") - - def _prim_forever(self, blklist): - """ Do list forever """ - while True: - self._icall(self._evline, blklist[:]) - yield True - if self.procstop: - break - self._ireturn() - yield True - - ''' - def _prim_while(self, list1, list2): - list = [self._intern('if')] - for i in list1: - list.append(i) - list.append(list2) - while self._icall(self._evline, list[:]): - yield True - self._ireturn() - yield True - ''' - - def _prim_if(self, boolean, blklist): - """ If bool, do list """ - if boolean: - self._icall(self._evline, blklist[:]) - yield True - self._ireturn() - yield True - - def _prim_ifelse(self, boolean, list1, list2): - """ If bool, do list1, else do list2 """ - if boolean: - self._ijmp(self._evline, list1[:]) - yield True - else: - self._ijmp(self._evline, list2[:]) - yield True - def _prim_opar(self, val): self.iline.pop(0) return val @@ -1026,49 +487,19 @@ class LogoCode: name.nargs, name.fcn = 0, body name.rprim = True - def _prim_stack(self, x): - """ Process a named stack """ - if type(convert(x, float, False)) == float: - if int(float(x)) == x: - x = int(x) - if 'stack3' + str(x) not in self.stacks or\ - self.stacks['stack3' + str(x)] is None: - raise logoerror("#nostack") - self._icall(self._evline, self.stacks['stack3' + str(x)][:]) - yield True - self.procstop = False - self._ireturn() - yield True - - def _prim_stack1(self): - """ Process Stack 1 """ - if self.stacks['stack1'] is None: - raise logoerror("#nostack") - self._icall(self._evline, self.stacks['stack1'][:]) - yield True - self.procstop = False - self._ireturn() - yield True - - def _prim_stack2(self): - """ Process Stack 2 """ - if self.stacks['stack2'] is None: - raise logoerror("#nostack") - self._icall(self._evline, self.stacks['stack2'][:]) - yield True - self.procstop = False - self._ireturn() - yield True - - def _prim_stopstack(self): - """ Stop execution of a stack """ - self.procstop = True - - def _prim_print_heap(self): - """ Display contents of heap """ - self.tw.showlabel('status', str(self.heap) + ' ') + def prim_clear(self): + """ Clear screen """ + if self.tw.gst_available: + from tagplay import stop_media + # stop_media(self) # TODO: gplay variable + self.tw.canvas.clearscreen() + self.tw.lc.scale = DEFAULT_SCALE + self.tw.lc.hidden_turtle = None + self.tw.lc.start_time = time() + for name in value_blocks: + self.tw.lc.update_label_value(name) - def _int(self, n): + def int(self, n): """ Raise an error if n doesn't convert to int. """ if type(n) == int: return n @@ -1080,96 +511,20 @@ class LogoCode: raise logoerror("%s %s %s %s" \ % (self.cfun.name, _("doesn't like"), str(n), _("as input"))) - def _box(self, x): - """ Retrieve value from named box """ - if type(convert(x, float, False)) == float: - if int(float(x)) == x: - x = int(x) - try: - return self.boxes['box3' + str(x)] - except KeyError: - raise logoerror("#emptybox") - - def _prim_myblock(self, x): - """ Run Python code imported from Journal """ - if self.bindex is not None and self.bindex in self.tw.myblock: - try: - if len(x) == 1: - myfunc_import(self, self.tw.myblock[self.bindex], x[0]) - else: - myfunc_import(self, self.tw.myblock[self.bindex], x) - except: - raise logoerror("#syntaxerror") - - def _prim_print(self, n, flag): - """ Print object n """ - if flag and (self.tw.hide or self.tw.step_time == 0): - return - if type(n) == str or type(n) == unicode: - if n[0:6] == 'media_' and n[6:] != 'CAMERA': - try: - if self.tw.running_sugar: - try: - dsobject = datastore.get(n[6:]) - except: - _logger.debug("Couldn't open %s" % (n[6:])) - self.tw.showlabel('status', dsobject.metadata['title']) - dsobject.destroy() - else: - self.tw.showlabel('status', n[6:]) - except IOError: - self.tw.showlabel('status', n) - else: - self.tw.showlabel('status', n) - elif type(n) == int: - self.tw.showlabel('status', n) - else: - self.tw.showlabel('status', - str(round_int(n)).replace('.', self.tw.decimal_point)) - - def _prim_kbinput(self): - """ Query keyboard """ - if len(self.tw.keypress) == 1: - self.keyboard = ord(self.tw.keypress[0]) - else: - try: - self.keyboard = {'Escape': 27, 'space': 32, ' ': 32, - 'Return': 13, \ - 'KP_Up': 2, 'KP_Down': 4, 'KP_Left': 1, \ - 'KP_Right': 3}[self.tw.keypress] - except KeyError: - self.keyboard = 0 - self.update_label_value('keyboard', self.keyboard) - self.tw.keypress = '' - def find_value_blocks(self): """ Find any value blocks that may need label updates """ - self.value_blocks = {} - for name in VALUE_BLOCKS: - self.value_blocks[name] = self.tw.block_list.get_similar_blocks( - 'block', name) - - def _update_audio_mode(self): - """ If there are sensor blocks, set the appropriate audio mode """ - for name in ['sound', 'volume', 'pitch']: - if len(self.value_blocks[name]) > 0: - self.tw.audiograb.set_sensor_type() - return - if len(self.value_blocks['resistance']) > 0: - self.tw.audiograb.set_sensor_type(SENSOR_DC_BIAS) - return - elif len(self.value_blocks['voltage']) > 0: - self.tw.audiograb.set_sensor_type(SENSOR_DC_NO_BIAS) - return + self.value_blocks_to_update = {} + for name in value_blocks: + self.value_blocks_to_update[name] = \ + self.tw.block_list.get_similar_blocks('block', name) def update_label_value(self, name, value=None): """ Update the label of value blocks to reflect current value """ - if self.tw.hide or not self.tw.interactive_mode or \ - not hasattr(self, 'value_blocks'): + if self.tw.hide or not self.tw.interactive_mode: return if value is None: - for block in self.value_blocks[name]: - block.spr.set_label(BLOCK_NAMES[name][0]) + for block in self.value_blocks_to_update[name]: + block.spr.set_label(block_names[name][0]) block.resize() elif self.update_values: if type(value) == float: @@ -1177,70 +532,10 @@ class LogoCode: self.tw.decimal_point) else: valstring = str(value) - for block in self.value_blocks[name]: - block.spr.set_label(BLOCK_NAMES[name][0] + ' = ' + valstring) + for block in self.value_blocks_to_update[name]: + block.spr.set_label(block_names[name][0] + ' = ' + valstring) block.resize() - def _prim_set(self, name, cmd, value=None): - """ Set a value and update the associated value blocks """ - if value is not None: - cmd(value) - self.update_label_value(name, value) - - def _prim_right(self, value): - self.tw.canvas.right(float(value)) - self.update_label_value('heading', self.tw.canvas.heading) - - def _prim_move(self, cmd, value1, value2=None, pendown=True): - if value2 is None: - cmd(value1) - else: - cmd(float(value1), float(value2), pendown=pendown) - self.update_label_value('xcor', - self.tw.canvas.xcor / self.tw.coord_scale) - self.update_label_value('ycor', - self.tw.canvas.ycor / self.tw.coord_scale) - if len(self.value_blocks['see']) > 0: - self.see() - - def _prim_arc(self, cmd, value1, value2): - cmd(float(value1), float(value2)) - self.update_label_value('xcor', - self.tw.canvas.xcor / self.tw.coord_scale) - self.update_label_value('ycor', - self.tw.canvas.ycor / self.tw.coord_scale) - self.update_label_value('heading', self.tw.canvas.heading) - if len(self.value_blocks['see']) > 0: - self.see() - - def _prim_setbox(self, name, x, val): - """ Define value of named box """ - if x is not None: - if type(convert(x, float, False)) == float: - if int(float(x)) == x: - x = int(x) - self.boxes[name + str(x)] = val - return - - self.boxes[name] = val - self.update_label_value(name, val) - - def _prim_push(self, val): - """ Push value onto FILO """ - self.heap.append(val) - self.update_label_value('pop', val) - - def _prim_pop(self): - """ Pop value off of FILO """ - if len(self.heap) == 0: - raise logoerror("#emptyheap") - else: - if len(self.heap) == 1: - self.update_label_value('pop') - else: - self.update_label_value('pop', self.heap[-2]) - return self.heap.pop(-1) - def push_file_data_to_heap(self, dsobject): """ push contents of a data store object (assuming json encoding) """ data = data_from_file(dsobject.file_path) @@ -1249,188 +544,59 @@ class LogoCode: self.heap.append(val) self.update_label_value('pop', self.heap[-1]) - def _empty_heap(self): - """ Empty FILO """ - self.heap = [] - - def _save_picture(self, name): - """ Save canvas to file as PNG """ - self.tw.save_as_image(name) - - def _save_svg(self, name): - """ Save SVG to file """ - self.tw.canvas.svg_close() - self.tw.save_as_image(name, True) - - def _show_list(self, sarray): - """ Display list of media objects """ - x = self.tw.canvas.xcor / self.tw.coord_scale - y = self.tw.canvas.ycor / self.tw.coord_scale - for s in sarray: - self.tw.canvas.setxy(x, y, pendown=False) - self._show(s) - y -= int(self.tw.canvas.textsize * self.tw.lead) - - def _set_scale(self, x): - """ Set scale used by media object display """ - self.scale = x - - def _reskin(self, media): - """ Reskin the turtle with an image from a file """ - scale = int(ICON_SIZE * float(self.scale) / DEFAULT_SCALE) - if scale < 1: - return - self.filepath = None - dsobject = None - if os.path.exists(media[6:]): # is it a path? - self.filepath = media[6:] - elif self.tw.running_sugar: # is it a datastore object? - try: - dsobject = datastore.get(media[6:]) - except: - _logger.debug("Couldn't open skin %s" % (media[6:])) - if dsobject is not None: - self.filepath = dsobject.file_path - if self.filepath == None: - self.tw.showlabel('nojournal', self.filepath) - return - pixbuf = None - try: - pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.filepath, scale, - scale) - except: - self.tw.showlabel('nojournal', self.filepath) - _logger.debug("Couldn't open skin %s" % (self.filepath)) - if pixbuf is not None: - self.tw.active_turtle.set_shapes([pixbuf]) - pen_state = self.tw.active_turtle.get_pen_state() - if pen_state: - self.tw.canvas.setpen(False) - self.tw.canvas.forward(0) - if pen_state: - self.tw.canvas.setpen(True) - - def _x(self): + def x2tx(self): """ Convert screen coordinates to turtle coordinates """ return int(self.tw.canvas.width / 2) + int(self.tw.canvas.xcor) - def _y(self): + def y2ty(self): """ Convert screen coordinates to turtle coordinates """ return int(self.tw.canvas.height / 2) - int(self.tw.canvas.ycor) - def _w(self): - """ Convert screen coordinates to turtle coordinates """ + def wpercent(self): + """ width as a percentage of screen coordinates """ return int((self.tw.canvas.width * self.scale) / 100.) - def _h(self): - """ Convert screen coordinates to turtle coordinates """ + def hpercent(self): + """ height as a percentage of screen coordinates """ return int((self.tw.canvas.height * self.scale) / 100.) - def _show(self, string, center=False): - """ Show is the general-purpose media-rendering block. """ - if type(string) == str or type(string) == unicode: - if string in ['media_', 'descr_', 'audio_', 'video_', - 'media_None', 'descr_None', 'audio_None', - 'video_None']: - pass - elif string[0:6] in ['media_', 'descr_', 'audio_', 'video_']: - self.filepath = None - self.dsobject = None - if string[6:] == 'CAMERA': - if self.tw.camera_available: - self.camera.save_camera_input_to_file() - self.camera.stop_camera_input() - self.filepath = self.imagepath - elif os.path.exists(string[6:]): # is it a path? - self.filepath = string[6:] - elif self.tw.running_sugar: # is it a datastore object? - try: - self.dsobject = datastore.get(string[6:]) - except: - _logger.debug("Couldn't find dsobject %s" % ( - string[6:])) - if self.dsobject is not None: - self.filepath = self.dsobject.file_path - if self.filepath == None: - if self.dsobject is not None: - self.tw.showlabel('nojournal', - self.dsobject.metadata['title']) - else: - self.tw.showlabel('nojournal', string[6:]) - _logger.debug("Couldn't open %s" % (string[6:])) - elif string[0:6] == 'media_': - self._insert_image(center) - elif string[0:6] == 'descr_': - mimetype = None - if self.dsobject is not None and \ - 'mime_type' in self.dsobject.metadata: - mimetype = self.dsobject.metadata['mime_type'] - description = None - if self.dsobject is not None and \ - 'description' in self.dsobject.metadata: - description = self.dsobject.metadata['description'] - self._insert_desc(mimetype, description) - elif string[0:6] == 'audio_': - self._play_sound() - elif string[0:6] == 'video_': - self._play_video() - if self.dsobject is not None: - self.dsobject.destroy() - else: # assume it is text to display - x = self._x() - y = self._y() - if center: - y -= self.tw.canvas.textsize - self.tw.canvas.draw_text(string, x, y, - int(self.tw.canvas.textsize * \ - self.scale / 100.), - self.tw.canvas.width - x) - elif type(string) == float or type(string) == int: - string = round_int(string) - x = self._x() - y = self._y() - if center: - y -= self.tw.canvas.textsize - self.tw.canvas.draw_text(string, x, y, - int(self.tw.canvas.textsize * \ - self.scale / 100.), - self.tw.canvas.width - x) - - def _insert_image(self, center=False, filepath=None): + def insert_image(self, center=False, filepath=None): """ Image only (at current x, y) """ if filepath is not None: self.filepath = filepath pixbuf = None - w = self._w() - h = self._h() + w, h = self.wpercent(), self.hpercent() if w < 1 or h < 1: return - if self.filepath is not None and self.filepath != '': + if self.dsobject is not None: + try: + pixbuf = get_pixbuf_from_journal(self.dsobject, w, h) + except: + debug_output("Couldn't open dsobject %s" % (self.dsobject), + self.tw.running_sugar) + if pixbuf is None and \ + self.filepath is not None and \ + self.filepath != '': try: pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.filepath, w, h) except: self.tw.showlabel('nojournal', self.filepath) - _logger.debug("Couldn't open filepath %s" % (self.filepath)) - elif self.dsobject is not None: - try: - pixbuf = get_pixbuf_from_journal(self.dsobject, w, h) - except: - self.tw.showlabel('nojournal', self.dsobject) - _logger.debug("Couldn't open dsobject %s" % (self.dsobject)) + debug_output("Couldn't open filepath %s" % (self.filepath), + self.tw.running_sugar) if pixbuf is not None: if center: self.tw.canvas.draw_pixbuf(pixbuf, 0, 0, - self._x() - int(w / 2), - self._y() - int(h / 2), w, h, + self.x2tx() - int(w / 2), + self.y2ty() - int(h / 2), w, h, self.filepath) else: - self.tw.canvas.draw_pixbuf(pixbuf, 0, 0, self._x(), self._y(), - w, h, self.filepath) + self.tw.canvas.draw_pixbuf(pixbuf, 0, 0, self.x2tx(), + self.y2ty(), w, h, self.filepath) - def _insert_desc(self, mimetype=None, description=None): + def insert_desc(self, mimetype=None, description=None): """ Description text only (at current x, y) """ - w = self._w() + w = self.wpercent() if w < 1: return text = None @@ -1448,378 +614,38 @@ class LogoCode: f.close() except IOError: self.tw.showlabel('nojournal', self.filepath) - _logger.debug("Couldn't open filepath %s" % \ - (self.filepath)) + debug_output("Couldn't open %s" % (self.filepath), + self.tw.running_sugar) else: if description is not None: text = str(description) else: text = self.filepath if text is not None: - self.tw.canvas.draw_text(text, self._x(), self._y(), + self.tw.canvas.draw_text(text, self.x2tx(), self.y2ty(), self.body_height, w) - def _media_wait(self): + def media_wait(self): """ Wait for media to stop playing """ - while(media_playing(self)): - yield True - self._ireturn() + if self.tw.gst_available: + from tagplay import media_playing + while(media_playing(self)): + yield True + self.ireturn() yield True - def _play_sound(self): + def play_sound(self): """ Sound file from Journal """ - play_audio_from_file(self, self.filepath) + if self.tw.gst_available: + from tagplay import play_audio_from_file + play_audio_from_file(self, self.filepath) - def _play_video(self): + def play_video(self): """ Movie file from Journal """ - w = self._w() - h = self._h() + w, h = self.wpercent(), self.hpercent() if w < 1 or h < 1: return - play_movie_from_file(self, self.filepath, self._x(), self._y(), - self._w(), self._h()) - - def see(self): - """ Read r, g, b from the canvas and return a corresponding palette - color """ - r, g, b, a = self.tw.canvas.get_pixel() - color_index = self.tw.canvas.get_color_index(r, g, b) - self.update_label_value('see', color_index) - return color_index - - def _read_pixel(self): - """ Read r, g, b, a from the canvas and push b, g, r to the stack """ - r, g, b, a = self.tw.canvas.get_pixel() - self.heap.append(b) - self.heap.append(g) - self.heap.append(r) - - def _read_camera(self, luminance_only=False): - """ Read average pixel from camera and push b, g, r to the stack """ - - if not self.tw.camera_available: - if not luminance_only: - self.heap.append(-1) - self.heap.append(-1) - self.heap.append(-1) - return -1 - - pixbuf = None - array = None - w = self._w() - if w < 1: - w = 1 - h = self._h() - if h < 1: - h = 1 - - self._video_capture_device = None - try: - self._video_capture_device = open('/dev/video0', 'rw') - except: - _logger.debug('video capture device not available') - if self._video_capture_device is not None: - self._set_ag(0) # disable autogain - - self.camera.save_camera_input_to_file() - self.camera.stop_camera_input() - - if self._video_capture_device is not None: - self._set_ag(1) # restore autogain and close device - self._video_capture_device.close() - self._video_capture_device = None - - pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.imagepath, w, h) - array = pixbuf.get_pixels() - - array_length = len(array) / 3 - r = 0 - g = 0 - b = 0 - i = 0 - for j in range(array_length): - r += ord(array[i]) - i += 1 - g += ord(array[i]) - i += 1 - b += ord(array[i]) - i += 1 - if luminance_only: - lum = int((r * 0.3 + g * 0.6 + b * 0.1) / array_length) - self.update_label_value('luminance', lum) - return lum - else: - self.heap.append(int((b / array_length))) - self.heap.append(int((g / array_length))) - self.heap.append(int((r / array_length))) - - def _set_ag(self, value): - """ set camera autogain (0==off, 1==on) """ - self._ag_control = v4l2.v4l2_control(v4l2.V4L2_CID_AUTOGAIN) - try: - ioctl(self._video_capture_device, v4l2.VIDIOC_G_CTRL, - self._ag_control) - self._ag_control.value = value - ioctl(self._video_capture_device, v4l2.VIDIOC_S_CTRL, - self._ag_control) - ioctl(self._video_capture_device, v4l2.VIDIOC_G_CTRL, - self._ag_control) - except: - _logger.debug('AUTOGAIN control not available') - - def _get_volume(self): - """ return mic in value """ - #TODO: Adjust gain for different HW - buf = self.ringbuffer.read(None, self.input_step) - if len(buf) > 0: - volume = float(_avg(buf, abs_value=True)) - self.update_label_value('volume', volume) - return volume - else: - return 0 - - def _get_sound(self): - """ return raw mic in value """ - buf = self.ringbuffer.read(None, self.input_step) - if len(buf) > 0: - sound = float(buf[0]) - self.update_label_value('sound', sound) - return sound - else: - return 0 - - def _get_pitch(self): - """ return index of max value in fft of mic in values """ - buf = [] - for i in range(4): - buf = append(buf, self.ringbuffer.read(None, self.input_step)) - if len(buf) > 0: - r = [] - for j in rfft(buf): - r.append(abs(j)) - # Convert output to Hertz - pitch = r.index(max(r)) * 48000 / len(buf) - self.update_label_value('pitch', pitch) - return pitch - else: - return 0 - - def _get_resistance(self): - """ return resistance sensor value """ - buf = self.ringbuffer.read(None, self.input_step) - if len(buf) > 0: - # See <http://bugs.sugarlabs.org/ticket/552#comment:7> - # TODO: test this calibration on XO 1.5 - if self.tw.hw == XO1: - resistance = 2.718 ** ((float(_avg(buf)) * 0.000045788) + \ - 8.0531) - else: - avg_buf = float(_avg(buf)) - if avg_buf > 0: - resistance = (420000000 / avg_buf) - 13500 - else: - resistance = 420000000 - self.update_label_value('resistance', resistance) - return resistance - else: - return 0 - - def _get_voltage(self): - """ return voltage sensor value """ - buf = self.ringbuffer.read(None, self.input_step) - if len(buf) > 0: - # See <http://bugs.sugarlabs.org/ticket/552#comment:7> - voltage = float(_avg(buf)) * self.voltage_gain + self.voltage_bias - self.update_label_value('voltage', voltage) - return voltage - else: - return 0 - - # Depreciated block methods - - def _show_template1x1(self, title, media): - """ title, one image, and description """ - xo = self.tw.calc_position('t1x1')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(title) - # calculate and set scale for media blocks - myscale = 45 * (self.tw.canvas.height - self.title_height * 2) \ - / self.tw.canvas.height - self._set_scale(myscale) - # set body text size - self.tw.canvas.settextsize(self.body_height) - # render media object - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media) - if self.tw.running_sugar: - x = 0 - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media.replace('media_', 'descr_')) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _show_template2x1(self, title, media1, media2): - """ title, two images (horizontal), two descriptions """ - xo = self.tw.calc_position('t2x1')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(title) - # calculate and set scale for media blocks - myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \ - self.tw.canvas.height - self._set_scale(myscale) - # set body text size - self.tw.canvas.settextsize(self.body_height) - # render four quadrents - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1) - x = 0 - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media2) - y = -self.title_height - if self.tw.running_sugar: - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media2.replace('media_', 'descr_')) - x = -(self.tw.canvas.width / 2) + xo - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1.replace('media_', 'descr_')) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _show_bullets(self, sarray): - """ title and varible number of bullets """ - xo = self.tw.calc_position('bullet')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(sarray[0]) - # set body text size - self.tw.canvas.settextsize(self.bullet_height) - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - for s in sarray[1:]: - self.tw.canvas.setxy(x, y, pendown=False) - self._show(s) - y -= int(self.bullet_height * 2 * self.tw.lead) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _show_template1x2(self, title, media1, media2): - """ title, two images (vertical), two desciptions """ - xo = self.tw.calc_position('t1x2')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(title) - # calculate and set scale for media blocks - myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \ - self.tw.canvas.height - self._set_scale(myscale) - # set body text size - self.tw.canvas.settextsize(self.body_height) - # render four quadrents - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1) - if self.tw.running_sugar: - x = 0 - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1.replace('media_', 'descr_')) - y = -self.title_height - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media2.replace('media_', 'descr_')) - x = -(self.tw.canvas.width / 2) + xo - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media2) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _show_template2x2(self, title, media1, media2, media3, media4): - """ title and four images """ - xo = self.tw.calc_position('t2x2')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(title) - # calculate and set scale for media blocks - myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \ - self.tw.canvas.height - self._set_scale(myscale) - # set body text size - self.tw.canvas.settextsize(self.body_height) - # render four quadrents - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1) - x = 0 - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media2) - y = -self.title_height - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media4) - x = -(self.tw.canvas.width / 2) + xo - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media3) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _show_template1x1a(self, title, media1): - """ title, one media object """ - xo = self.tw.calc_position('t1x1a')[2] - x = -(self.tw.canvas.width / 2) + xo - y = self.tw.canvas.height / 2 - self.tw.canvas.setxy(x, y, pendown=False) - # save the text size so we can restore it later - save_text_size = self.tw.canvas.textsize - # set title text - self.tw.canvas.settextsize(self.title_height) - self._show(title) - # calculate and set scale for media blocks - myscale = 90 * (self.tw.canvas.height - self.title_height * 2) / \ - self.tw.canvas.height - self._set_scale(myscale) - # set body text size - self.tw.canvas.settextsize(self.body_height) - # render media object - # leave some space below the title - y -= int(self.title_height * 2 * self.tw.lead) - self.tw.canvas.setxy(x, y, pendown=False) - self._show(media1) - # restore text size - self.tw.canvas.settextsize(save_text_size) - - def _write(self, string, fsize): - """ Write string at size """ - x = self.tw.canvas.width / 2 + int(self.tw.canvas.xcor) - y = self.tw.canvas.height / 2 - int(self.tw.canvas.ycor) - self.tw.canvas.draw_text(string, x, y - 15, int(fsize), - self.tw.canvas.width) + if self.tw.gst_available: + from tagplay import play_movie_from_file + play_movie_from_file(self, self.filepath, self.x2tx(), self.y2ty(), + w, h) diff --git a/TurtleArt/tapalette.py b/TurtleArt/tapalette.py new file mode 100644 index 0000000..61fcb14 --- /dev/null +++ b/TurtleArt/tapalette.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python +#Copyright (c) 2011 Walter Bender + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +palette_names = [] +palette_blocks = [] +block_colors = [] +expandable_blocks = [] +block_names = {} +block_primitives = {} +default_values = {} +logo_commands = {} +logo_functions = {} +special_names = {} # Names for blocks without names for popup help +content_blocks = ['number', 'string', 'description', 'audio', 'video', + 'journal'] +value_blocks = [] # blocks whose labels are updated get added here +special_block_colors = {} +block_styles = {'basic-style': [], + 'blank-style': [], + 'basic-style-head': [], + 'basic-style-head-1arg': [], + 'basic-style-tail': [], + 'basic-style-extended': [], + 'basic-style-extended-vertical': [], + 'basic-style-1arg': [], + 'basic-style-2arg': [], + 'basic-style-3arg': [], + 'basic-style-var-arg': [], + 'bullet-style': [], + 'invisible': [], + 'box-style': [], + 'box-style-media': [], + 'number-style': [], + 'number-style-var-arg': [], + 'number-style-block': [], + 'number-style-porch': [], + 'number-style-1arg': [], + 'number-style-1strarg': [], + 'compare-style': [], + 'compare-porch-style': [], + 'boolean-style': [], + 'not-style': [], + 'flow-style': [], + 'flow-style-tail': [], + 'flow-style-1arg': [], + 'flow-style-boolean': [], + 'flow-style-while': [], + 'flow-style-else': [], + 'collapsible-top': [], + 'collapsible-top-no-arm': [], + 'collapsible-top-no-label': [], + 'collapsible-top-no-arm-no-label': [], + 'collapsible-bottom': [], + 'portfolio-style-2x2': [], + 'portfolio-style-1x1': [], + 'portfolio-style-2x1': [], + 'portfolio-style-1x2': []} + +from taconstants import EXPANDABLE_STYLE +from tautils import debug_output + +from gettext import gettext as _ + +help_strings = { + 'next': _('displays next palette'), + 'orientation': _("changes the orientation of the palette of blocks") + } + + +class Palette(): + """ a class for defining new palettes """ + + def __init__(self, name, colors=["#00FF00", "#00A000"], position=None): + self._name = name + self._special_name = _(name) + self._colors = colors + self._help = None + + def add_palette(self, position=None): + if self._name is None: + debug_output('You must specify a name for your palette') + return + + # Insert new palette just before the trash + if 'trash' in palette_names: + i = palette_names.index('trash') + else: + i = len(palette_names) + + if position is not None and type(position) is int and position < i: + i = position + + if self._name not in palette_names: + palette_names.insert(i, self._name) + palette_blocks.insert(i, []) + block_colors.insert(i, self._colors) + else: + # debug_output('Palette %s already defined' % (self._name)) + return + + # Special name entry is needed for help hover mechanism + special_names[self._name] = self._special_name + if self._help is not None: + help_strings[self._name] = self._help + else: + help_strings[self._name] = '' + + def set_help(self, help): + self._help = help + + def set_special_name(self, name): + self._special_name = name + + def add_block(self, block_name, style='basic-block', label=None, + special_name=None, default=None, prim_name=None, + help_string=None, value_block=False, content_block=False, + logo_command=None, hidden=False, colors=None): + """ Add a new block to the palette """ + block = Block(block_name) + block.set_style(style) + if label is not None: + block.set_label(label) + if special_name is not None: + block.set_special_name(special_name) + if default is not None: + if default == 'None': + block.set_default(None) + else: + block.set_default(default) + if prim_name is not None: + block.set_prim_name(prim_name) + if logo_command is not None: + block.set_logo_command(logo_command) + if help_string is not None: + block.set_help(help_string) + if colors is not None: + block.set_colors(colors) + block.set_value_block(value_block) + block.set_content_block(content_block) + if not hidden: + block.set_palette(self._name) + block.add_block() + + +def make_palette(palette_name, colors=None, help_string=None): + """ Palette helper function """ + if colors is None: + palette = Palette(palette_name) + else: + palette = Palette(palette_name, colors) + if help_string is not None: + palette.set_help(help_string) + palette.add_palette() + return palette + + +def palette_name_to_index(palette_name): + ''' Find the index associated with palette_name. ''' + if palette_name in palette_names: + return palette_names.index(palette_name) + else: + return None + +def define_logo_function(key, value): + ''' Add a logo function to the table. ''' + logo_functions[key] = value + +class Block(): + """ a class for defining new block primitives """ + + def __init__(self, name): + self._name = name + self._special_name = None + self._palette = None + self._style = None + self._label = None + self._default = None + self._help = None + self._prim_name = None + self._logo_command = None + self._value_block = False + self._content_block = False + self._colors = None + + def add_block(self, position=None): + if self._name is None: + debug_output('You must specify a name for your block') + return + + if self._style is None: + debug_output('You must specify a style for your block') + return + else: + block_styles[self._style].append(self._name) + + if self._label is not None: + block_names[self._name] = self._label + + if self._palette is not None: + i = palette_names.index(self._palette) + if position is not None and type(position) is int and \ + position < len(palette_blocks[i]): + palette_blocks[i].insert(position, self._name) + else: + palette_blocks[i].append(self._name) + if position is not None: + debug_output('Ignoring position (%s)' % (str(position))) + + if self._help is not None: + help_strings[self._name] = self._help + else: + help_strings[self._name] = '' + + if self._value_block: + value_blocks.append(self._name) + + if self._content_block: + content_blocks.append(self._name) + + if self._prim_name is not None: + block_primitives[self._name] = self._prim_name + + if self._logo_command is not None and self._prim_name is not None: + logo_commands[self._prim_name] = self._logo_command + + if self._default is not None: + default_values[self._name] = self._default + + if self._special_name is not None: + special_names[self._name] = self._special_name + + if self._style in EXPANDABLE_STYLE: + expandable_blocks.append(self._name) + + if self._colors is not None: + special_block_colors[self._name] = self._colors + + def set_colors(self, colors=None): + self._colors = colors + + def set_value_block(self, value=True): + self._value_block = value + + def set_content_block(self, value=True): + self._content_block = value + + def set_palette(self, palette): + if not palette in palette_names: + debug_output('Could not find palette %s' % (palette)) + else: + self._palette = palette + + def set_help(self, help): + self._help = help + + def set_special_name(self, name): + self._special_name = name + + def set_label(self, label): + if type(label) == type([]): + self._label = label[:] + else: + self._label = [label] + + def set_default(self, default): + if type(default) == type([]): + self._default = default[:] + else: + self._default = [default] + + def set_style(self, style): + if style not in block_styles: + debug_output('Unknown style: %s' % (style)) + else: + self._style = style + + def set_prim_name(self, prim_name): + self._prim_name = prim_name + + def set_logo_command(self, logo_command): + self._logo_command = logo_command diff --git a/TurtleArt/tasprite_factory.py b/TurtleArt/tasprite_factory.py index fe9166d..c9fc4c4 100755 --- a/TurtleArt/tasprite_factory.py +++ b/TurtleArt/tasprite_factory.py @@ -24,7 +24,6 @@ import pygtk pygtk.require('2.0') import gtk import os -from gettext import gettext as _ from taconstants import HIT_RED, HIT_GREEN, HIDE_WHITE, SHOW_WHITE @@ -80,6 +79,15 @@ class SVG: self._gradient = False self.margins = [0, 0, 0, 0] + """ + The block construction methods typically start on the left side of + a block and proceed clockwise around the block, first constructing a + left-side connector ("outie"), a corner (1, -1), a slot or hat on along + the top, a corner (1, 1), right side connectors ("innie"), possibly a + "porch" to suggest an order of arguments, another corner (-1, 1), + a tab or tail, and the fourth corner (-1, -1). + """ + def basic_block(self): self.reset_min_max() (x, y) = self._calculate_x_y() @@ -334,12 +342,8 @@ class SVG: yoffset = self._radius * 2 + 2 * self._innie_y2 + \ self._innie_spacer + self._stroke_width / 2.0 + \ self._expand_y - if self._porch is True: - yoffset += self._porch_y svg = self._start_boolean(self._stroke_width / 2.0, yoffset) yoffset = -2 * self._innie_y2 - self._innie_spacer - self._stroke_width - if self._porch is True: - yoffset -= self._porch_y svg += self._rline_to(0, yoffset) self._hide_x = self._x + self._radius / 2 + self._stroke_width @@ -353,12 +357,13 @@ class SVG: svg += self._do_innie() svg += self._rline_to(0, self._expand_y) if self._porch is True: - svg += self._do_porch() + svg += self._do_porch(False) else: svg += self._rline_to(0, 2 * self._innie_y2 + self._innie_spacer) svg += self._do_innie() svg += self._rline_to(0, self._radius) svg += self.line_to(xx, self._y) + svg += self._rline_to(-self._expand_x, 0) self._show_y = self._y + self._radius / 2 @@ -369,7 +374,6 @@ class SVG: self._scale) self.margins[1] = int(self._stroke_width * self._scale) self.margins[2] = int(self._stroke_width * self._scale) - self.margins[3] = int(self._stroke_width * self._scale) return self.header() + svg def turtle(self, colors): @@ -591,7 +595,7 @@ class SVG: def set_gradient(self, flag=False, color='#FFFFFF'): self._gradient = flag - self._gradient_color = color + self._gradient_color = color def set_innie(self, innie_array=[False]): self._innie = innie_array diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py index a7a3205..0ca72e4 100644 --- a/TurtleArt/taturtle.py +++ b/TurtleArt/taturtle.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#Copyright (c) 2010 Walter Bender +#Copyright (c) 2010,11 Walter Bender #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -19,14 +19,15 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -from taconstants import TURTLE_LAYER +from random import uniform +from math import sin, cos, pi +from gettext import gettext as _ + +from taconstants import TURTLE_LAYER, DEFAULT_TURTLE_COLORS from tasprite_factory import SVG, svg_str_to_pixbuf from tacanvas import wrap100, color_table from sprites import Sprite - -import logging -_logger = logging.getLogger('turtleart-activity') - +from tautils import debug_output SHAPES = 36 @@ -59,6 +60,8 @@ class Turtles: else: if colors == None: Turtle(self, k) + elif type(colors) in [list, tuple]: + Turtle(self, k, colors) else: Turtle(self, k, colors.split(',')) return self.dict[k] @@ -120,11 +123,25 @@ class Turtle: self.pen_gray = 100 self.pen_size = 5 self.pen_state = True + self.label_block = None self._prep_shapes(key, turtles, turtle_colors) + # Choose a random angle from which to attach the turtle label if turtles.sprite_list is not None: self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0]) + angle = uniform(0, pi * 4 / 3.0) # 240 degrees + w = self.shapes[0].get_width() + r = w * 0.67 + # Restrict angle the the sides 30-150; 210-330 + if angle > pi * 2 / 3.0: + angle += pi / 2.0 # + 90 + self.label_xy = [int(r * sin(angle)), + int(r * cos(angle) + w / 2.0)] + else: + angle += pi / 6.0 # + 30 + self.label_xy = [int(r * sin(angle) + w / 2.0), + int(r * cos(angle) + w / 2.0)] else: self.spr = None turtles.add_to_dict(key, self) @@ -149,9 +166,16 @@ class Turtle: self.shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: - self.colors = ['#008000', '#00A000'] + self.colors = DEFAULT_TURTLE_COLORS self.shapes = turtles.get_pixbufs() + def set_turtle_colors(self, turtle_colors): + ''' reset the colors of a preloaded turtle ''' + if turtle_colors is not None: + self.colors = turtle_colors[:] + self.shapes = generate_turtle_pixbufs(self.colors) + self.set_heading(self.heading) + def set_shapes(self, shapes): """ Reskin the turtle """ n = len(shapes) @@ -159,7 +183,8 @@ class Turtle: self.shapes = shapes[:] else: if n != 1: - _logger.debug("%d images passed to set_shapes: ignoring" % (n)) + debug_output("%d images passed to set_shapes: ignoring" % (n), + self.tw.running_sugar) images = [shapes[0]] if self.heading == 0: for i in range(3): @@ -213,6 +238,8 @@ class Turtle: """ Hide the turtle. """ if self.spr is not None: self.spr.hide() + if self.label_block is not None: + self.label_block.spr.hide() self.hidden = True def show(self): @@ -222,14 +249,25 @@ class Turtle: self.hidden = False self.move((self.x, self.y)) self.set_heading(self.heading) + if self.label_block is not None: + self.label_block.spr.move((self.x + self.label_xy[0], + self.y + self.label_xy[1])) + self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move(self, pos): """ Move the turtle. """ self.x, self.y = int(pos[0]), int(pos[1]) if not self.hidden and self.spr is not None: self.spr.move(pos) + if self.label_block is not None: + self.label_block.spr.move((pos[0] + self.label_xy[0], + pos[1] + self.label_xy[1])) return(self.x, self.y) + def get_name(self): + ''' return turtle name (key) ''' + return self.name + def get_xy(self): """ Return the turtle's x, y coordinates. """ return(self.x, self.y) diff --git a/TurtleArt/tautils.py b/TurtleArt/tautils.py index c66b322..0c6fe64 100644 --- a/TurtleArt/tautils.py +++ b/TurtleArt/tautils.py @@ -1,5 +1,5 @@ #Copyright (c) 2007-8, Playful Invention Company. -#Copyright (c) 2008-10, Walter Bender +#Copyright (c) 2008-11, Walter Bender #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,8 @@ import gtk import pickle import subprocess import dbus +import os.path +from gettext import gettext as _ try: OLD_SUGAR_SYSTEM = False @@ -37,17 +39,31 @@ except (ImportError, AttributeError): from simplejson import dump as jdump except: OLD_SUGAR_SYSTEM = True - -from taconstants import STRING_OR_NUMBER_ARGS, HIDE_LAYER, CONTENT_ARGS, \ - COLLAPSIBLE, BLOCK_LAYER, CONTENT_BLOCKS, HIT_HIDE, \ - HIT_SHOW, XO1, XO15, UNKNOWN from StringIO import StringIO -import os.path -from gettext import gettext as _ + +from taconstants import HIDE_LAYER, COLLAPSIBLE, BLOCK_LAYER, HIT_HIDE, \ + HIT_SHOW, XO1, XO15, UNKNOWN + import logging _logger = logging.getLogger('turtleart-activity') +def debug_output(message_string, running_sugar=False): + """ unified debugging output """ + if running_sugar: + _logger.debug(message_string) + else: + print(message_string) + + +def error_output(message_string, running_sugar=False): + """ unified debugging output """ + if running_sugar: + _logger.error(message_string) + else: + print(message_string) + + class pythonerror(Exception): def __init__(self, value): @@ -105,15 +121,22 @@ def json_load(text): if OLD_SUGAR_SYSTEM is True: _listdata = json.read(text) else: - # strip out leading and trailing whitespace, nulls, and newlines + # Strip out leading and trailing whitespace, nulls, and newlines clean_text = text.lstrip() clean_text = clean_text.replace('\12', '') clean_text = clean_text.replace('\00', '') - _io = StringIO(clean_text.rstrip()) + clean_text = clean_text.rstrip() + # Look for missing ']'s + left_count = clean_text.count('[') + right_count = clean_text.count(']') + while left_count > right_count: + clean_text += ']' + right_count = clean_text.count(']') + _io = StringIO(clean_text) try: _listdata = jload(_io) except ValueError: - # assume that text is ascii list + # Assume that text is ascii list _listdata = text.split() for i, value in enumerate(_listdata): _listdata[i] = convert(value, float) @@ -147,7 +170,7 @@ def json_dump(data): def get_load_name(suffix, load_save_folder): """ Open a load file dialog. """ - _dialog = gtk.FileChooserDialog("Load...", None, + _dialog = gtk.FileChooserDialog(_('Load...'), None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) _dialog.set_default_response(gtk.RESPONSE_OK) @@ -156,7 +179,7 @@ def get_load_name(suffix, load_save_folder): def get_save_name(suffix, load_save_folder, save_file_name): """ Open a save file dialog. """ - _dialog = gtk.FileChooserDialog("Save...", None, + _dialog = gtk.FileChooserDialog(_('Save...'), None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) _dialog.set_default_response(gtk.RESPONSE_OK) @@ -285,24 +308,36 @@ def get_path(activity, subpath): "org.laptop.TurtleArtActivity", subpath)) -def image_to_base64(pixbuf, activity): - """ Convert an image to base64 """ - _file_name = os.path.join(get_path(activity, 'instance'), 'imagetmp.png') +def image_to_base64(pixbuf, path_name): + """ Convert an image to base64-encoded data """ + file_name = os.path.join(path_name, 'imagetmp.png') if pixbuf != None: - pixbuf.save(_file_name, "png") - _base64 = os.path.join(get_path(activity, 'instance'), 'base64tmp') - _cmd = "base64 <" + _file_name + " >" + _base64 - subprocess.check_call(_cmd, shell=True) - _file_handle = open(_base64, 'r') - _data = _file_handle.read() - _file_handle.close() - return _data + pixbuf.save(file_name, "png") + base64 = os.path.join(path_name, 'base64tmp') + cmd = "base64 <" + file_name + " >" + base64 + subprocess.check_call(cmd, shell=True) + file_handle = open(base64, 'r') + data = file_handle.read() + file_handle.close() + return data + + +def base64_to_image(data, path_name): + """ Convert base64-encoded data to an image """ + base64 = os.path.join(path_name, 'base64tmp') + file_handle = open(base64, 'w') + file_handle.write(data) + file_handle.close() + file_name = os.path.join(path_name, 'imagetmp.png') + cmd = "base64 -d <" + base64 + ">" + file_name + subprocess.check_call(cmd, shell=True) + return file_name def movie_media_type(name): """ Is it movie media? """ return name.lower().endswith(('.ogv', '.vob', '.mp4', '.wmv', '.mov', - '.mpeg', 'ogg')) + '.mpeg', '.ogg', '.webm')) def audio_media_type(name): @@ -318,7 +353,7 @@ def image_media_type(name): def text_media_type(name): """ Is it text media? """ - return name.lower().endswith(('.txt', '.py', '.lg', '.rtf', '.ta')) + return name.lower().endswith(('.txt', '.py', '.lg', '.rtf')) def round_int(num): @@ -326,7 +361,6 @@ def round_int(num): try: float(num) except TypeError: - _logger.debug("error trying to convert %s to number" % (str(num))) raise pythonerror("#syntaxerror") if int(float(num)) == num: @@ -613,39 +647,6 @@ def neg_arg(value): return False -def dock_dx_dy(block1, dock1n, block2, dock2n): - """ Find the distance between the dock points of two blocks. """ - _dock1 = block1.docks[dock1n] - _dock2 = block2.docks[dock2n] - _d1type, _d1dir, _d1x, _d1y = _dock1[0:4] - _d2type, _d2dir, _d2x, _d2y = _dock2[0:4] - if block1 == block2: - return (100, 100) - if _d1dir == _d2dir: - return (100, 100) - if (_d2type is not 'number') or (dock2n is not 0): - if block1.connections is not None and \ - dock1n < len(block1.connections) and \ - block1.connections[dock1n] is not None: - return (100, 100) - if block2.connections is not None and \ - dock2n < len(block2.connections) and \ - block2.connections[dock2n] is not None: - return (100, 100) - if _d1type != _d2type: - if block1.name in STRING_OR_NUMBER_ARGS: - if _d2type == 'number' or _d2type == 'string': - pass - elif block1.name in CONTENT_ARGS: - if _d2type in CONTENT_BLOCKS: - pass - else: - return (100, 100) - (_b1x, _b1y) = block1.spr.get_xy() - (_b2x, _b2y) = block2.spr.get_xy() - return ((_b1x + _d1x) - (_b2x + _d2x), (_b1y + _d1y) - (_b2y + _d2y)) - - def journal_check(blk1, blk2, dock1, dock2): """ Dock blocks only if arg is Journal block """ if blk1 == None or blk2 == None: @@ -796,22 +797,29 @@ def find_blk_below(blk, name): def get_hardware(): """ Determine whether we are using XO 1.0, 1.5, or "unknown" hardware """ - bus = dbus.SystemBus() - - comp_obj = bus.get_object('org.freedesktop.Hal', - '/org/freedesktop/Hal/devices/computer') - dev = dbus.Interface(comp_obj, 'org.freedesktop.Hal.Device') - if dev.PropertyExists('system.hardware.vendor') and \ - dev.PropertyExists('system.hardware.version'): - if dev.GetProperty('system.hardware.vendor') == 'OLPC': - if dev.GetProperty('system.hardware.version') == '1.5': - return XO15 - else: - return XO1 + product = _get_dmi('product_name') + if product is None: + if os.path.exists('/etc/olpc-release') or \ + os.path.exists('/sys/power/olpc-pm'): + return XO1 else: return UNKNOWN - elif os.path.exists('/etc/olpc-release') or \ - os.path.exists('/sys/power/olpc-pm'): + if product != 'XO': + return UNKNOWN + version = _get_dmi('product_version') + if version == '1': return XO1 + elif version == '1.5': + return XO15 else: return UNKNOWN + + +def _get_dmi(node): + ''' The desktop management interface should be a reliable source + for product and version information. ''' + path = os.path.join('/sys/class/dmi/id', node) + try: + return open(path).readline().strip() + except: + return None diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py index ad830d9..64a32fd 100644 --- a/TurtleArt/tawindow.py +++ b/TurtleArt/tawindow.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- #Copyright (c) 2007, Playful Invention Company -#Copyright (c) 2008-10, Walter Bender -#Copyright (c) 2009-10 Raúl Gutiérrez Segalés -#Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy> +#Copyright (c) 2008-11, Walter Bender +#Copyright (c) 2009-11 Raúl Gutiérrez Segalés #Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/> #Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,7 +26,15 @@ import pygtk pygtk.require('2.0') import gtk import gobject -import gst +from gettext import gettext as _ + +try: + import gst + GST_AVAILABLE = True +except ImportError: + # Turtle Art should not fail if gst is not available + GST_AVAILABLE = False + import os import os.path import dbus @@ -36,7 +43,6 @@ from math import atan2, pi DEGTOR = 2 * pi / 360 import locale -from gettext import gettext as _ try: from sugar.datastore import datastore @@ -45,84 +51,74 @@ except ImportError: pass from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \ - PALETTE_NAMES, TITLEXY, MEDIA_SHAPES, STATUS_SHAPES, \ - OVERLAY_SHAPES, TOOLBAR_SHAPES, TAB_LAYER, RETURN, \ - OVERLAY_LAYER, CATEGORY_LAYER, BLOCKS_WITH_SKIN, \ - ICON_SIZE, PALETTES, PALETTE_SCALE, BOX_STYLE_MEDIA, \ - PALETTE_WIDTH, MACROS, TOP_LAYER, BLOCK_LAYER, \ - CONTENT_BLOCKS, DEFAULTS, SPECIAL_NAMES, \ - HELP_STRINGS, CURSOR, EXPANDABLE, COLLAPSIBLE, \ - DEAD_DICTS, DEAD_KEYS, TEMPLATES, PYTHON_SKIN, \ - PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, OLD_NAMES, \ - BOOLEAN_STYLE, BLOCK_NAMES, DEFAULT_TURTLE, \ - TURTLE_LAYER, EXPANDABLE_BLOCKS, COMPARE_STYLE, \ - BOOLEAN_STYLE, EXPANDABLE_ARGS, NUMBER_STYLE, \ - NUMBER_STYLE_PORCH, NUMBER_STYLE_BLOCK, \ - NUMBER_STYLE_VAR_ARG, CONSTANTS, XO1, XO15, UNKNOWN, \ - BASIC_STYLE_VAR_ARG -from talogo import LogoCode, stop_logo + MEDIA_SHAPES, STATUS_SHAPES, OVERLAY_SHAPES, STRING_OR_NUMBER_ARGS, \ + TOOLBAR_SHAPES, TAB_LAYER, RETURN, OVERLAY_LAYER, CATEGORY_LAYER, \ + BLOCKS_WITH_SKIN, ICON_SIZE, PALETTE_SCALE, PALETTE_WIDTH, \ + MACROS, TOP_LAYER, BLOCK_LAYER, OLD_NAMES, DEFAULT_TURTLE, TURTLE_LAYER, \ + CURSOR, EXPANDABLE, COLLAPSIBLE, DEAD_DICTS, DEAD_KEYS, \ + TEMPLATES, PYTHON_SKIN, PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, \ + EXPANDABLE_ARGS, XO1, XO15, UNKNOWN, TITLEXY, CONTENT_ARGS, CONSTANTS +from tapalette import palette_names, palette_blocks, expandable_blocks, \ + block_names, content_blocks, default_values, special_names, block_styles, \ + help_strings +from talogo import LogoCode from tacanvas import TurtleGraphics from tablock import Blocks, Block from taturtle import Turtles, Turtle from tautils import magnitude, get_load_name, get_save_name, data_from_file, \ - data_to_file, round_int, get_id, get_pixbuf_from_journal, \ - movie_media_type, audio_media_type, image_media_type, \ - save_picture, save_svg, calc_image_size, get_path, \ - reset_stack_arm, grow_stack_arm, find_sandwich_top, \ - find_sandwich_bottom, restore_stack, collapse_stack, \ - collapsed, collapsible, hide_button_hit, show_button_hit, \ - arithmetic_check, xy, find_block_to_run, find_top_block, \ - find_start_stack, find_group, find_blk_below, \ - dock_dx_dy, data_to_string, journal_check, chooser, \ - get_hardware + data_to_file, round_int, get_id, get_pixbuf_from_journal, \ + movie_media_type, audio_media_type, image_media_type, save_picture, \ + save_svg, calc_image_size, get_path, reset_stack_arm, grow_stack_arm, \ + find_sandwich_top, find_sandwich_bottom, restore_stack, collapse_stack, \ + collapsed, collapsible, hide_button_hit, show_button_hit, chooser, \ + arithmetic_check, xy, find_block_to_run, find_top_block, journal_check, \ + find_group, find_blk_below, data_to_string, find_start_stack, \ + get_hardware, debug_output, error_output from tasprite_factory import SVG, svg_str_to_pixbuf, svg_from_file -from tagplay import stop_media from sprites import Sprites, Sprite -from audiograb import AudioGrab_Unknown, AudioGrab_XO1, AudioGrab_XO15 -from rfidutils import strhex2bin, strbin2dec, find_device from dbus.mainloop.glib import DBusGMainLoop -HAL_SERVICE = 'org.freedesktop.Hal' -HAL_MGR_PATH = '/org/freedesktop/Hal/Manager' -HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager' -HAL_DEV_IFACE = 'org.freedesktop.Hal.Device' -REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\ - 'a-z,A-Z,0-9,_]*serial_usb_[0-9]' - -import logging -_logger = logging.getLogger('turtleart-activity') +if GST_AVAILABLE: + from tagplay import stop_media class TurtleArtWindow(): """ TurtleArt Window class abstraction """ timeout_tag = [0] + _PLUGIN_SUBPATH = 'plugins' def __init__(self, win, path, parent=None, mycolors=None, mynick=None): self._loaded_project = '' - self.win = None self._sharing = False self.parent = parent - self.send_event = None # method to send events over the network + self.send_event = None # method to send events over the network + self.gst_available = GST_AVAILABLE if type(win) == gtk.DrawingArea: self.interactive_mode = True self.window = win self.window.set_flags(gtk.CAN_FOCUS) + self.window.show_all() if self.parent is not None: self.parent.show_all() self.running_sugar = True else: - self.window.show_all() self.running_sugar = False self.area = self.window.window - self.gc = self.area.new_gc() + if self.area is not None: + self.gc = self.area.new_gc() + else: + # We lose... + debug_output('drawable area is None... punting') + exit() self._setup_events() elif type(win) == gtk.gdk.Pixmap: self.interactive_mode = False self.window = win self.running_sugar = False - self.gc = self.window.new_gc() + if self.window is not None: + self.gc = self.window.new_gc() else: - _logger.debug("bad win type %s" % (type(win))) + debug_output("bad win type %s" % (type(win)), False) if self.running_sugar: self.activity = parent @@ -147,7 +143,10 @@ class TurtleArtWindow(): self.mouse_x = 0 self.mouse_y = 0 - locale.setlocale(locale.LC_NUMERIC, '') + try: + locale.setlocale(locale.LC_NUMERIC, '') + except locale.Error: + debug_output('unsupported locale', self.running_sugar) self.decimal_point = locale.localeconv()['decimal_point'] if self.decimal_point == '' or self.decimal_point is None: self.decimal_point = '.' @@ -155,7 +154,6 @@ class TurtleArtWindow(): self.orientation = HORIZONTAL_PALETTE self.hw = get_hardware() - _logger.debug('running on %s hardware' % (self.hw)) if self.hw in (XO1, XO15): self.lead = 1.0 self.scale = 0.67 @@ -163,14 +161,14 @@ class TurtleArtWindow(): self.color_mode = '565' else: self.color_mode = '888' - if self.running_sugar and not self.activity.new_sugar_system: + if self.running_sugar and not self.activity.has_toolbarbox: self.orientation = VERTICAL_PALETTE else: self.lead = 1.0 self.scale = 1.0 self.color_mode = '888' # TODO: Read visual mode from gtk image - self.block_scale = BLOCK_SCALE + self.block_scale = BLOCK_SCALE[3] self.trash_scale = 0.5 self.myblock = {} self.python_code = None @@ -187,6 +185,7 @@ class TurtleArtWindow(): self.media_shapes = {} self.cartesian = False self.polar = False + self.metric = False self.overlay_shapes = {} self.toolbar_shapes = {} self.toolbar_offset = 0 @@ -196,7 +195,6 @@ class TurtleArtWindow(): self.palette_sprs = [] self.palettes = [] self.palette_button = [] - self.trash_index = PALETTE_NAMES.index('trash') self.trash_stack = [] self.selected_palette = None self.previous_palette = None @@ -219,10 +217,10 @@ class TurtleArtWindow(): self.sprite_list = None self.turtles = Turtles(self.sprite_list) - if mynick is None: + if self.nick is None: self.default_turtle_name = DEFAULT_TURTLE else: - self.default_turtle_name = mynick + self.default_turtle_name = self.nick if mycolors is None: Turtle(self.turtles, self.default_turtle_name) else: @@ -252,99 +250,100 @@ class TurtleArtWindow(): CONSTANTS['width'] = int(self.canvas.width / self.coord_scale) CONSTANTS['height'] = int(self.canvas.height / self.coord_scale) - if self.interactive_mode: - self._setup_misc() - self._show_toolbar_palette(0, False) - - # setup sound/sensor grab - if self.hw in [XO1, XO15]: - PALETTES[PALETTE_NAMES.index('sensor')].append('resistance') - PALETTES[PALETTE_NAMES.index('sensor')].append('voltage') - self.audio_started = False - - self.camera_available = False - v4l2src = gst.element_factory_make('v4l2src') - if v4l2src.props.device_name is not None: - PALETTES[PALETTE_NAMES.index('sensor')].append('readcamera') - PALETTES[PALETTE_NAMES.index('sensor')].append('luminance') - PALETTES[PALETTE_NAMES.index('sensor')].append('camera') - self.camera_available = True + self._icon_paths = [os.path.join(self.path, 'icons')] + self._plugins = [] + self._init_plugins() self.lc = LogoCode(self) - self.saved_pictures = [] - - self.block_operation = '' - """ - The following code will initialize a USB RFID reader. Please note that - in order to make this initialization function work, it is necessary to - set the permission for the ttyUSB device to 0666. You can do this by - adding a rule to /etc/udev/rules.d - - As root (using sudo or su), copy the following text into a new file in - /etc/udev/rules.d/94-ttyUSB-rules - - KERNEL=="ttyUSB[0-9]",MODE="0666" - - You only have to do this once. - """ - - self.rfid_connected = False - self.rfid_device = find_device() - self.rfid_idn = '' - - if self.rfid_device is not None: - _logger.info("RFID device found") - self.rfid_connected = self.rfid_device.do_connect() - if self.rfid_connected: - self.rfid_device.connect("tag-read", self._tag_read_cb) - self.rfid_device.connect("disconnected", self._disconnected_cb) - - loop = DBusGMainLoop() - bus = dbus.SystemBus(mainloop=loop) - hmgr_iface = dbus.Interface(bus.get_object(HAL_SERVICE, - HAL_MGR_PATH), HAL_MGR_IFACE) + from tabasics import Palettes + p = Palettes(self) + self._setup_plugins() - hmgr_iface.connect_to_signal('DeviceAdded', self._device_added_cb) + if self.interactive_mode: + self._setup_misc() + self.show_toolbar_palette(0, False) - PALETTES[PALETTE_NAMES.index('sensor')].append('rfid') + self.saved_pictures = [] + self.block_operation = '' - def _device_added_cb(self, path): - """ - Called from hal connection when a new device is plugged. - """ - if not self.rfid_connected: - self.rfid_device = find_device() - _logger.debug("DEVICE_ADDED: %s"%self.rfid_device) - if self.rfid_device is not None: - _logger.debug("DEVICE_ADDED: RFID device is not None!") - self.rfid_connected = self._device.do_connect() - if self.rfid_connected: - _logger.debug("DEVICE_ADDED: Connected!") - self.rfid_device.connect("tag-read", self._tag_read_cb) - self.rfid_device.connect("disconnected", self._disconnected_cb) - - def _disconnected_cb(self, device, text): - """ - Called when the device is disconnected. - """ - self.rfid_connected = False - self.rfid_device = None + def _get_plugin_home(self): + """ Look in the execution directory """ + path = os.path.join(self.path, self._PLUGIN_SUBPATH) + if os.path.exists(path): + return path + else: + return None - def _tag_read_cb(self, device, tagid): - """ - Callback for "tag-read" signal. Receives the read tag id. - """ - idbin = strhex2bin(tagid) - self.rfid_idn = strbin2dec(idbin[26:64]) - while self.rfid_idn.__len__() < 9: - self.rfid_idn = '0' + self.rfid_idn - print tagid, idbin, self.rfid_idn - - def new_buffer(self, buf): - """ Append a new buffer to the ringbuffer """ - self.lc.ringbuffer.append(buf) - return True + def _get_plugins_from_plugins_dir(self, path): + """ Look for plugin files in plugin dir. """ + plugin_files = [] + if path is not None: + candidates = os.listdir(path) + for dirname in candidates: + if os.path.exists( + os.path.join(path, dirname, dirname + '.py')): + plugin_files.append(dirname) + return plugin_files + + def _init_plugins(self): + """ Try importing plugin files from the plugin dir. """ + for plugin_dir in self._get_plugins_from_plugins_dir( + self._get_plugin_home()): + plugin_class = plugin_dir.capitalize() + f = "def f(self): from plugins.%s.%s import %s; return %s(self)" \ + % (plugin_dir, plugin_dir, plugin_class, plugin_class) + plugins = {} + try: + exec f in globals(), plugins + self._plugins.append(plugins.values()[0](self)) + # debug_output('successfully importing %s' % (plugin_class)) + except ImportError: + debug_output('failed to import %s' % (plugin_class)) + + # Add the icon dir for each plugin to the icon_theme search path + for plugin_dir in self._get_plugins_from_plugins_dir( + self._get_plugin_home()): + self._add_plugin_icon_dir(os.path.join(self._get_plugin_home(), + plugin_dir)) + + def _add_plugin_icon_dir(self, dirname): + ''' If there is an icon subdir, add it to the search path. ''' + icon_theme = gtk.icon_theme_get_default() + icon_path = os.path.join(dirname, 'icons') + if os.path.exists(icon_path): + icon_theme.append_search_path(icon_path) + self._icon_paths.append(icon_path) + + def _setup_plugins(self): + """ Initial setup -- call just once. """ + for plugin in self._plugins: + plugin.setup() + + def _start_plugins(self): + """ Start is called everytime we execute blocks. """ + for plugin in self._plugins: + plugin.start() + + def _stop_plugins(self): + """ Stop is called whenever we stop execution. """ + for plugin in self._plugins: + plugin.stop() + + def background_plugins(self): + """ Background is called when we are pushed to the background. """ + for plugin in self._plugins: + plugin.goto_background() + + def foreground_plugins(self): + """ Foreground is called when we are return from the background. """ + for plugin in self._plugins: + plugin.return_to_foreground() + + def _quit_plugins(self): + """ Quit is called upon program exit. """ + for plugin in self._plugins: + plugin.quit() def _setup_events(self): """ Register the events we listen to. """ @@ -361,41 +360,41 @@ class TurtleArtWindow(): def _setup_misc(self): """ Misc. sprites for status, overlays, etc. """ # media blocks get positioned into other blocks - for _name in MEDIA_SHAPES: - if _name[0:7] == 'journal' and not self.running_sugar: - file_name = 'file' + _name[7:] + for name in MEDIA_SHAPES: + if name[0:7] == 'journal' and not self.running_sugar: + filename = 'file' + name[7:] else: - file_name = _name - self.media_shapes[_name] = svg_str_to_pixbuf(svg_from_file( - "%s/images/%s.svg" % (self.path, file_name))) + filename = name + self.media_shapes[name] = svg_str_to_pixbuf(svg_from_file( + "%s/images/%s.svg" % (self.path, filename))) - for i, _name in enumerate(STATUS_SHAPES): - self.status_shapes[_name] = svg_str_to_pixbuf(svg_from_file( - "%s/images/%s.svg" % (self.path, _name))) + for i, name in enumerate(STATUS_SHAPES): + self.status_shapes[name] = svg_str_to_pixbuf(svg_from_file( + "%s/images/%s.svg" % (self.path, name))) self.status_spr = Sprite(self.sprite_list, 0, self.height - 200, self.status_shapes['status']) self.status_spr.hide() self.status_spr.type = 'status' - for _name in OVERLAY_SHAPES: - self.overlay_shapes[_name] = Sprite(self.sprite_list, + for name in OVERLAY_SHAPES: + self.overlay_shapes[name] = Sprite(self.sprite_list, int(self.width / 2 - 600), int(self.height / 2 - 450), svg_str_to_pixbuf( - svg_from_file("%s/images/%s.svg" % (self.path, _name)))) - self.overlay_shapes[_name].hide() - self.overlay_shapes[_name].type = 'overlay' + svg_from_file("%s/images/%s.svg" % (self.path, name)))) + self.overlay_shapes[name].hide() + self.overlay_shapes[name].type = 'overlay' if not self.running_sugar: offset = self.width - 55 * len(TOOLBAR_SHAPES) - for i, _name in enumerate(TOOLBAR_SHAPES): - self.toolbar_shapes[_name] = Sprite(self.sprite_list, - i * 55 + offset, 0, - svg_str_to_pixbuf( - svg_from_file("%s/icons/%s.svg" % (self.path, _name)))) - self.toolbar_shapes[_name].set_layer(TAB_LAYER) - self.toolbar_shapes[_name].name = _name - self.toolbar_shapes[_name].type = 'toolbar' + for i, name in enumerate(TOOLBAR_SHAPES): + self.toolbar_shapes[name] = Sprite( + self.sprite_list, i * 55 + offset, 0, + svg_str_to_pixbuf(svg_from_file(os.path.join( + self.path, 'icons', '%s.svg' % (name))))) + self.toolbar_shapes[name].set_layer(TAB_LAYER) + self.toolbar_shapes[name].name = name + self.toolbar_shapes[name].type = 'toolbar' self.toolbar_shapes['stopiton'].hide() def set_sharing(self, shared): @@ -420,35 +419,17 @@ class TurtleArtWindow(): self.lc.prim_clear() self.display_coordinates() - def _start_audiograb(self): - """ Start grabbing audio if there is an audio block in use """ - if len(self.block_list.get_similar_blocks('block', - ['volume', 'sound', 'pitch', 'resistance', 'voltage'])) > 0: - if self.audio_started: - self.audiograb.resume_grabbing() - else: - if self.hw == XO15: - self.audiograb = AudioGrab_XO15(self.new_buffer, self) - elif self.hw == XO1: - self.audiograb = AudioGrab_XO1(self.new_buffer, self) - else: - self.audiograb = AudioGrab_Unknown(self.new_buffer, self) - self.audiograb.start_grabbing() - self.audio_started = True - def run_button(self, time): """ Run turtle! """ if self.running_sugar: self.activity.recenter() - if self.interactive_mode: - self._start_audiograb() - # Look for a 'start' block for blk in self.just_blocks(): if find_start_stack(blk): self.step_time = time - _logger.debug("running stack starting from %s" % (blk.name)) + debug_output("running stack starting from %s" % (blk.name), + self.running_sugar) self._run_stack(blk) return @@ -456,15 +437,15 @@ class TurtleArtWindow(): for blk in self.just_blocks(): if find_block_to_run(blk): self.step_time = time - _logger.debug("running stack starting from %s" % (blk.name)) + debug_output("running stack starting from %s" % (blk.name), + self.running_sugar) self._run_stack(blk) return def stop_button(self): """ Stop button """ - stop_logo(self) - if self.audio_started: - self.audiograb.pause_grabbing() + self.lc.stop_logo() + self._stop_plugins() def set_userdefined(self, blk=None): """ Change icon for user-defined blocks after loading Python code. """ @@ -505,28 +486,38 @@ class TurtleArtWindow(): self.overlay_shapes['polar'].hide() self.polar = False + def set_metric(self, flag): + """ Turn on/off metric coordinates """ + if flag: + self.overlay_shapes['metric'].set_layer(OVERLAY_LAYER) + self.metric = True + else: + self.overlay_shapes['metric'].hide() + self.metric = False + def update_overlay_position(self, widget, event): """ Reposition the overlays when window size changes """ self.width = event.width self.height = event.height - for _name in OVERLAY_SHAPES: - shape = self.overlay_shapes[_name] + for name in OVERLAY_SHAPES: + shape = self.overlay_shapes[name] showing = False if shape in shape._sprites.list: shape.hide() showing = True - self.overlay_shapes[_name] = Sprite(self.sprite_list, + self.overlay_shapes[name] = Sprite(self.sprite_list, int(self.width / 2 - 600), int(self.height / 2 - 450), svg_str_to_pixbuf( - svg_from_file("%s/images/%s.svg" % (self.path, _name)))) + svg_from_file("%s/images/%s.svg" % (self.path, name)))) if showing: - self.overlay_shapes[_name].set_layer(OVERLAY_LAYER) + self.overlay_shapes[name].set_layer(OVERLAY_LAYER) else: - self.overlay_shapes[_name].hide() - self.overlay_shapes[_name].type = 'overlay' + self.overlay_shapes[name].hide() + self.overlay_shapes[name].type = 'overlay' self.cartesian = False self.polar = False + self.metric = False self.canvas.width = self.width self.canvas.height = self.height self.canvas.move_turtle() @@ -543,9 +534,9 @@ class TurtleArtWindow(): if blk.status != 'collapsed': blk.spr.set_layer(BLOCK_LAYER) self.show_palette() - if self.activity is not None and self.activity.new_sugar_system: + if self.activity is not None and self.activity.has_toolbarbox: self.activity.palette_buttons[0].set_icon( - PALETTE_NAMES[0] + 'on') + palette_names[0] + 'on') self.hide = False if self.running_sugar: self.activity.recenter() @@ -568,10 +559,10 @@ class TurtleArtWindow(): def show_palette(self, n=0): """ Show palette """ - self._show_toolbar_palette(n) + self.show_toolbar_palette(n) self.palette_button[self.orientation].set_layer(TAB_LAYER) self.palette_button[2].set_layer(TAB_LAYER) - if self.activity is None or not self.activity.new_sugar_system: + if self.activity is None or not self.activity.has_toolbarbox: self.toolbar_spr.set_layer(CATEGORY_LAYER) self.palette = True @@ -580,7 +571,7 @@ class TurtleArtWindow(): self._hide_toolbar_palette() self.palette_button[self.orientation].hide() self.palette_button[2].hide() - if self.activity is None or not self.activity.new_sugar_system: + if self.activity is None or not self.activity.has_toolbarbox: self.toolbar_spr.hide() self.palette = False @@ -591,7 +582,7 @@ class TurtleArtWindow(): self.hide = False self.hideshow_button() if self.running_sugar: - self.activity.do_hide() + self.activity.do_hide_blocks() def showblocks(self): """ Callback from 'show blocks' block """ @@ -600,7 +591,7 @@ class TurtleArtWindow(): self.hide = True self.hideshow_button() if self.running_sugar: - self.activity.do_show() + self.activity.do_show_blocks() def resize_blocks(self, blocks=None): """ Resize blocks or if blocks is None, all of the blocks """ @@ -635,65 +626,21 @@ class TurtleArtWindow(): if blk.name in BLOCKS_WITH_SKIN: self._resize_skin(blk) - def _show_toolbar_palette(self, n, init_only=False): + def show_toolbar_palette(self, n, init_only=False, regenerate=False): """ Show the toolbar palettes, creating them on init_only """ - if (self.activity is None or not self.activity.new_sugar_system) and\ + # If we are running the 0.86+ toolbar, the selectors are already + # created, as toolbar buttons. Otherwise, we need to create them. + if (self.activity is None or not self.activity.has_toolbarbox) and \ self.selectors == []: - # Create the selectors - svg = SVG() - x, y = 50, 0 - for i, name in enumerate(PALETTE_NAMES): - a = svg_str_to_pixbuf(svg_from_file("%s/icons/%soff.svg" % ( - self.path, name))) - b = svg_str_to_pixbuf(svg_from_file("%s/icons/%son.svg" % ( - self.path, name))) - self.selector_shapes.append([a, b]) - self.selectors.append(Sprite(self.sprite_list, x, y, a)) - self.selectors[i].type = 'selector' - self.selectors[i].name = name - self.selectors[i].set_layer(TAB_LAYER) - w = self.selectors[i].get_dimensions()[0] - x += int(w) - - # Create the toolbar background - self.toolbar_offset = ICON_SIZE - self.toolbar_spr = Sprite(self.sprite_list, 0, 0, - svg_str_to_pixbuf(svg.toolbar(self.width, ICON_SIZE))) - self.toolbar_spr.type = 'toolbar' - self.toolbar_spr.set_layer(CATEGORY_LAYER) + # First, create the selector buttons + self._create_the_selectors() + # Create the empty palettes that we'll then populate with prototypes. if self.palette_sprs == []: - # Create the empty palettes - if len(self.palettes) == 0: - for i in range(len(PALETTES)): - self.palettes.append([]) - - # Create empty palette backgrounds - for i in PALETTE_NAMES: - self.palette_sprs.append([None, None]) - - # Create the palette orientation button - self.palette_button.append(Sprite(self.sprite_list, 0, - self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( - "%s/images/palettehorizontal.svg" % (self.path))))) - self.palette_button.append(Sprite(self.sprite_list, 0, - self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( - "%s/images/palettevertical.svg" % (self.path))))) - self.palette_button[0].name = _('orientation') - self.palette_button[1].name = _('orientation') - self.palette_button[0].type = 'palette' - self.palette_button[1].type = 'palette' - self.palette_button[self.orientation].set_layer(TAB_LAYER) - self.palette_button[1 - self.orientation].hide() - - # Create the palette next button - self.palette_button.append(Sprite(self.sprite_list, 16, - self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( - "%s/images/palettenext.svg" % (self.path))))) - self.palette_button[2].name = _('next') - self.palette_button[2].type = 'palette' - self.palette_button[2].set_layer(TAB_LAYER) + self._create_the_empty_palettes() + # At initialization of the program, we don't actually populate + # the palettes. if init_only: return @@ -703,27 +650,121 @@ class TurtleArtWindow(): self.selected_palette = n self.previous_palette = self.selected_palette - if self.activity is None or not self.activity.new_sugar_system: + # Make sure all of the selectors are visible. (We don't need to do + # this for 0.86+ toolbars since the selectors are toolbar buttons.) + if self.activity is None or not self.activity.has_toolbarbox: self.selected_selector = self.selectors[n] - # Make sure all of the selectors are visible. self.selectors[n].set_shape(self.selector_shapes[n][1]) - for i in range(len(PALETTES)): + for i in range(len(palette_blocks)): self.selectors[i].set_layer(TAB_LAYER) # Show the palette with the current orientation. if self.palette_sprs[n][self.orientation] is not None: self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER) + # Create 'proto' blocks for each palette entry + self._create_proto_blocks(n, regenerate=regenerate) + + self._layout_palette(n, regenerate=regenerate) + for blk in self.palettes[n]: + blk.spr.set_layer(TAB_LAYER) + if n == palette_names.index('trash'): + for blk in self.trash_stack: + for gblk in find_group(blk): + if gblk.status != 'collapsed': + gblk.spr.set_layer(TAB_LAYER) + + def _create_the_selectors(self): + ''' Create the palette selector buttons. ''' + svg = SVG() + x, y = 50, 0 # positioned at the left, top + for i, name in enumerate(palette_names): + for path in self._icon_paths: + if os.path.exists(os.path.join(path, '%soff.svg' % (name))): + icon_pathname = os.path.join(path, '%soff.svg' % (name)) + break + if icon_pathname is not None: + off_shape = svg_str_to_pixbuf(svg_from_file(icon_pathname)) + else: + off_shape = svg_str_to_pixbuf(svg_from_file(os.path.join( + self._icon_paths[0], 'extrasoff.svg'))) + error_output('Unable to open %soff.svg' % (name), + self.running_sugar) + for path in self._icon_paths: + if os.path.exists(os.path.join(path, '%son.svg' % (name))): + icon_pathname = os.path.join(path, '%son.svg' % (name)) + break + if icon_pathname is not None: + on_shape = svg_str_to_pixbuf(svg_from_file(icon_pathname)) + else: + on_shape = svg_str_to_pixbuf(svg_from_file(os.path.join( + self._icon_paths[0], 'extrason.svg'))) + error_output('Unable to open %son.svg' % (name), + self.running_sugar) + + self.selector_shapes.append([off_shape, on_shape]) + self.selectors.append(Sprite(self.sprite_list, x, y, off_shape)) + self.selectors[i].type = 'selector' + self.selectors[i].name = name + self.selectors[i].set_layer(TAB_LAYER) + w = self.selectors[i].get_dimensions()[0] + x += int(w) # running from left to right + + # Create the toolbar background for the selectors + self.toolbar_offset = ICON_SIZE + self.toolbar_spr = Sprite(self.sprite_list, 0, 0, + svg_str_to_pixbuf(svg.toolbar(self.width, ICON_SIZE))) + self.toolbar_spr.type = 'toolbar' + self.toolbar_spr.set_layer(CATEGORY_LAYER) + + def _create_the_empty_palettes(self): + ''' Create the empty palettes to be populated by prototype blocks. ''' + if len(self.palettes) == 0: + for i in range(len(palette_blocks)): + self.palettes.append([]) + + # Create empty palette backgrounds + for i in palette_names: + self.palette_sprs.append([None, None]) + + # Create the palette orientation button + self.palette_button.append(Sprite(self.sprite_list, 0, + self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( + "%s/images/palettehorizontal.svg" % (self.path))))) + self.palette_button.append(Sprite(self.sprite_list, 0, + self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( + "%s/images/palettevertical.svg" % (self.path))))) + self.palette_button[0].name = _('orientation') + self.palette_button[1].name = _('orientation') + self.palette_button[0].type = 'palette' + self.palette_button[1].type = 'palette' + self.palette_button[self.orientation].set_layer(TAB_LAYER) + self.palette_button[1 - self.orientation].hide() + + # Create the palette next button + self.palette_button.append(Sprite(self.sprite_list, 16, + self.toolbar_offset, svg_str_to_pixbuf(svg_from_file( + "%s/images/palettenext.svg" % (self.path))))) + self.palette_button[2].name = _('next') + self.palette_button[2].type = 'palette' + self.palette_button[2].set_layer(TAB_LAYER) + + def _create_proto_blocks(self, n, regenerate=False): + ''' Create the protoblocks that will populate a palette. ''' + if regenerate: + for blk in self.palettes[n]: + blk.type = 'trash' + self.palettes[n] = [] + if self.palettes[n] == []: - # Create 'proto' blocks for each palette entry - for i, name in enumerate(PALETTES[n]): + for i, name in enumerate(palette_blocks[n]): self.palettes[n].append(Block(self.block_list, self.sprite_list, name, 0, 0, 'proto', [], PALETTE_SCALE)) self.palettes[n][i].spr.set_layer(TAB_LAYER) self.palettes[n][i].unhighlight() # Some proto blocks get a skin. - if name in BOX_STYLE_MEDIA: + if name in block_styles['box-style-media']: self._proto_skin(name + 'small', n, i) elif name[:8] == 'template': self._proto_skin(name[8:], n, i) @@ -732,25 +773,16 @@ class TurtleArtWindow(): elif name in PYTHON_SKIN: self._proto_skin('pythonsmall', n, i) - self._layout_palette(n) - for blk in self.palettes[n]: - blk.spr.set_layer(TAB_LAYER) - if n == self.trash_index: - for blk in self.trash_stack: - for gblk in find_group(blk): - if gblk.status != 'collapsed': - gblk.spr.set_layer(TAB_LAYER) - def _hide_toolbar_palette(self): """ Hide the toolbar palettes """ self._hide_previous_palette() - if self.activity is None or not self.activity.new_sugar_system: + if self.activity is None or not self.activity.has_toolbarbox: # Hide the selectors - for i in range(len(PALETTES)): + for i in range(len(palette_blocks)): self.selectors[i].hide() elif self.selected_palette is not None: self.activity.palette_buttons[self.selected_palette].set_icon( - PALETTE_NAMES[self.selected_palette] + 'off') + palette_names[self.selected_palette] + 'off') self.selected_palette = None self.previous_palette = None @@ -758,119 +790,117 @@ class TurtleArtWindow(): """ Hide just the previously viewed toolbar palette """ # Hide previous palette if self.previous_palette is not None: - for i in range(len(PALETTES[self.previous_palette])): - self.palettes[self.previous_palette][i].spr.hide() - self.palette_sprs[self.previous_palette][ - self.orientation].hide() - if self.activity is None or not self.activity.new_sugar_system: + for proto in self.palettes[self.previous_palette]: + proto.spr.hide() + self.palette_sprs[self.previous_palette][self.orientation].hide() + if self.activity is None or not self.activity.has_toolbarbox: self.selectors[self.previous_palette].set_shape( self.selector_shapes[self.previous_palette][0]) elif self.previous_palette is not None and \ self.previous_palette != self.selected_palette: self.activity.palette_buttons[self.previous_palette].set_icon( - PALETTE_NAMES[self.previous_palette] + 'off') - if self.previous_palette == self.trash_index: + palette_names[self.previous_palette] + 'off') + if self.previous_palette == palette_names.index('trash'): for blk in self.trash_stack: for gblk in find_group(blk): gblk.spr.hide() def _horizontal_layout(self, x, y, blocks): """ Position prototypes in a horizontal palette. """ - _max_w = 0 + max_w = 0 for blk in blocks: - _w, _h = self._width_and_height(blk) - if y + _h > PALETTE_HEIGHT + self.toolbar_offset: - x += int(_max_w + 3) + w, h = self._width_and_height(blk) + if y + h > PALETTE_HEIGHT + self.toolbar_offset: + x += int(max_w + 3) y = self.toolbar_offset + 3 - _max_w = 0 - (_bx, _by) = blk.spr.get_xy() - _dx = x - _bx - _dy = y - _by + max_w = 0 + (bx, by) = blk.spr.get_xy() + dx = x - bx + dy = y - by for g in find_group(blk): - g.spr.move_relative((int(_dx), int(_dy))) - y += int(_h + 3) - if _w > _max_w: - _max_w = _w - return x, y, _max_w + g.spr.move_relative((int(dx), int(dy))) + y += int(h + 3) + if w > max_w: + max_w = w + return x, y, max_w def _vertical_layout(self, x, y, blocks): """ Position prototypes in a vertical palette. """ - _row = [] - _row_w = 0 - _max_h = 0 - for _b in blocks: - _w, _h = self._width_and_height(_b) - if x + _w > PALETTE_WIDTH: + row = [] + row_w = 0 + max_h = 0 + for b in blocks: + w, h = self._width_and_height(b) + if x + w > PALETTE_WIDTH: # Recenter row. - _dx = int((PALETTE_WIDTH - _row_w) / 2) - for _r in _row: - for _g in find_group(_r): - _g.spr.move_relative((_dx, 0)) - _row = [] - _row_w = 0 + dx = int((PALETTE_WIDTH - row_w) / 2) + for r in row: + for g in find_group(r): + g.spr.move_relative((dx, 0)) + row = [] + row_w = 0 x = 4 - y += int(_max_h + 3) - _max_h = 0 - _row.append(_b) - _row_w += (4 + _w) - (_bx, _by) = _b.spr.get_xy() - _dx = int(x - _bx) - _dy = int(y - _by) - for _g in find_group(_b): - _g.spr.move_relative((_dx, _dy)) - x += int(_w + 4) - if _h > _max_h: - _max_h = _h + y += int(max_h + 3) + max_h = 0 + row.append(b) + row_w += (4 + w) + (bx, by) = b.spr.get_xy() + dx = int(x - bx) + dy = int(y - by) + for g in find_group(b): + g.spr.move_relative((dx, dy)) + x += int(w + 4) + if h > max_h: + max_h = h # Recenter last row. - _dx = int((PALETTE_WIDTH - _row_w) / 2) - for _r in _row: - for _g in find_group(_r): - _g.spr.move_relative((_dx, 0)) - return x, y, _max_h + dx = int((PALETTE_WIDTH - row_w) / 2) + for r in row: + for g in find_group(r): + g.spr.move_relative((dx, 0)) + return x, y, max_h - def _layout_palette(self, n): + def _layout_palette(self, n, regenerate=False): """ Layout prototypes in a palette. """ if n is not None: if self.orientation == HORIZONTAL_PALETTE: - _x, _y = 20, self.toolbar_offset + 5 - _x, _y, _max = self._horizontal_layout(_x, _y, - self.palettes[n]) - if n == self.trash_index: - _x, _y, _max = self._horizontal_layout(_x + _max, _y, - self.trash_stack) - _w = _x + _max + 25 - if self.palette_sprs[n][self.orientation] is None: - svg = SVG() - self.palette_sprs[n][self.orientation] = Sprite( - self.sprite_list, 0, self.toolbar_offset, - svg_str_to_pixbuf(svg.palette(_w, PALETTE_HEIGHT))) - self.palette_sprs[n][self.orientation].type = 'category' - if n == PALETTE_NAMES.index('trash'): - svg = SVG() - self.palette_sprs[n][self.orientation].set_shape( - svg_str_to_pixbuf(svg.palette(_w, PALETTE_HEIGHT))) - self.palette_button[2].move((_w - 20, self.toolbar_offset)) + x, y = 20, self.toolbar_offset + 5 + x, y, max_w = self._horizontal_layout(x, y, self.palettes[n]) + if n == palette_names.index('trash'): + x, y, max_w = self._horizontal_layout(x + max_w, y, + self.trash_stack) + w = x + max_w + 25 + self._make_palette_spr(n, 0, self.toolbar_offset, + w, PALETTE_HEIGHT, regenerate) + self.palette_button[2].move((w - 20, self.toolbar_offset)) else: - _x, _y = 5, self.toolbar_offset + 15 - _x, _y, _max = self._vertical_layout(_x, _y, self.palettes[n]) - if n == PALETTE_NAMES.index('trash'): - _x, _y, _max = self._vertical_layout(_x, _y + _max, - self.trash_stack) - _h = _y + _max + 25 - self.toolbar_offset - if self.palette_sprs[n][self.orientation] is None: - svg = SVG() - self.palette_sprs[n][self.orientation] = \ - Sprite(self.sprite_list, 0, self.toolbar_offset, - svg_str_to_pixbuf(svg.palette(PALETTE_WIDTH, _h))) - self.palette_sprs[n][self.orientation].type = 'category' - if n == PALETTE_NAMES.index('trash'): - svg = SVG() - self.palette_sprs[n][self.orientation].set_shape( - svg_str_to_pixbuf(svg.palette(PALETTE_WIDTH, _h))) + x, y = 5, self.toolbar_offset + 15 + x, y, max_h = self._vertical_layout(x, y, self.palettes[n]) + if n == palette_names.index('trash'): + x, y, max_h = self._vertical_layout(x, y + max_h, + self.trash_stack) + h = y + max_h + 25 - self.toolbar_offset + self._make_palette_spr(n, 0, self.toolbar_offset, + PALETTE_WIDTH, h, regenerate) self.palette_button[2].move((PALETTE_WIDTH - 20, self.toolbar_offset)) self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER) + def _make_palette_spr(self, n, x, y, w, h, regenerate=False): + ''' Make the background for the palette. ''' + if regenerate and not self.palette_sprs[n][self.orientation] is None: + self.palette_sprs[n][self.orientation].hide() + self.palette_sprs[n][self.orientation] = None + if self.palette_sprs[n][self.orientation] is None: + svg = SVG() + self.palette_sprs[n][self.orientation] = \ + Sprite(self.sprite_list, x, y, svg_str_to_pixbuf( + svg.palette(w, h))) + self.palette_sprs[n][self.orientation].type = 'category' + if n == palette_names.index('trash'): + svg = SVG() + self.palette_sprs[n][self.orientation].set_shape( + svg_str_to_pixbuf(svg.palette(w, h))) + def _buttonpress_cb(self, win, event): """ Button press """ self.window.grab_focus() @@ -916,6 +946,10 @@ class TurtleArtWindow(): self._restore_latest_from_trash() elif blk.name == 'empty': self._empty_trash() + elif blk.name == 'trashall': + for b in self.just_blocks(): + if b.type != 'trash': + self._put_in_trash(find_top_block(b)) elif blk.name in MACROS: self._new_macro(blk.name, x + 20, y + 20) else: @@ -925,7 +959,7 @@ class TurtleArtWindow(): 'block', blk.name)) > 0: self.showlabel('dupstack') return True - # You cannot miz and match sensor blocks + # You cannot mix and match sensor blocks elif blk.name in ['sound', 'volume', 'pitch']: if len(self.block_list.get_similar_blocks( 'block', ['resistance', 'voltage'])) > 0: @@ -954,6 +988,9 @@ class TurtleArtWindow(): # Next, look for a turtle t = self.turtles.spr_to_turtle(spr) if t is not None: + # If turtle is shared, ignore click + if self.remote_turtle(t.get_name()): + return True self.selected_turtle = t self.canvas.set_turtle(self.turtles.get_turtle_key(t)) self._turtle_pressed(x, y) @@ -972,18 +1009,18 @@ class TurtleArtWindow(): elif spr.type == 'palette': if spr.name == _('next'): i = self.selected_palette + 1 - if i == len(PALETTE_NAMES): + if i == len(palette_names): i = 0 if self.activity is None or \ - not self.activity.new_sugar_system: + not self.activity.has_toolbarbox: self._select_category(self.selectors[i]) else: if self.selected_palette is not None: self.activity.palette_buttons[ self.selected_palette].set_icon( - PALETTE_NAMES[self.selected_palette] + 'off') + palette_names[self.selected_palette] + 'off') self.activity.palette_buttons[i].set_icon( - PALETTE_NAMES[i] + 'on') + palette_names[i] + 'on') self.show_palette(i) else: self.orientation = 1 - self.orientation @@ -1018,7 +1055,7 @@ class TurtleArtWindow(): self.lc.trace = 0 self.run_button(0) elif spr.name == 'run-slowoff': - self.lc.trace = 0 + self.lc.trace = 1 self.run_button(3) elif spr.name == 'debugoff': self.lc.trace = 1 @@ -1060,8 +1097,8 @@ class TurtleArtWindow(): if gblk.name in BLOCKS_WITH_SKIN: self._resize_skin(gblk) - # self.show_palette(self.trash_index) - if self.selected_palette != self.trash_index: + # self.show_palette(palette_names.index('trash')) + if self.selected_palette != palette_names.index('trash'): for gblk in group: gblk.spr.hide() @@ -1113,8 +1150,8 @@ class TurtleArtWindow(): def _in_the_trash(self, x, y): """ Is x, y over the trash can? """ """ - if self.selected_palette == self.trash_index and \ - self.palette_sprs[self.trash_index][self.orientation].hit((x, y)): + if self.selected_palette == palette_names.index('trash') and \ + self.palette_sprs[palette_names.index('trash')][self.orientation].hit((x, y)): return True """ if self.selected_palette is not None and \ @@ -1146,14 +1183,18 @@ class TurtleArtWindow(): self.selected_blk.unhighlight() self.selected_blk = None - def _new_block(self, name, x, y): + def _new_block(self, name, x, y, defaults=None): """ Make a new block. """ - if name in CONTENT_BLOCKS: - newblk = Block(self.block_list, self.sprite_list, name, x - 20, - y - 20, 'block', DEFAULTS[name], self.block_scale) + x_pos = x - 20 + y_pos = y - 20 + if name in content_blocks: + if defaults == None: + defaults = default_values[name] + newblk = Block(self.block_list, self.sprite_list, name, x_pos, + y_pos, 'block', defaults, self.block_scale) else: - newblk = Block(self.block_list, self.sprite_list, name, x - 20, - y - 20, 'block', [], self.block_scale) + newblk = Block(self.block_list, self.sprite_list, name, x_pos, + y_pos, 'block', [], self.block_scale) # Add a 'skin' to some blocks if name in PYTHON_SKIN: @@ -1161,15 +1202,17 @@ class TurtleArtWindow(): self._block_skin('pythonon', newblk) else: self._block_skin('pythonoff', newblk) - elif name in BOX_STYLE_MEDIA: + elif name in block_styles['box-style-media']: self._block_skin(name + 'off', newblk) newspr = newblk.spr newspr.set_layer(TOP_LAYER) self.drag_pos = 20, 20 newblk.connections = [None] * len(newblk.docks) - if newblk.name in DEFAULTS: - for i, argvalue in enumerate(DEFAULTS[newblk.name]): + if newblk.name in default_values: + if defaults == None: + defaults = default_values[newblk.name] + for i, argvalue in enumerate(defaults): # skip the first dock position since it is always a connector dock = newblk.docks[i + 1] argname = dock[0] @@ -1186,7 +1229,7 @@ class TurtleArtWindow(): argname = argvalue (sx, sy) = newspr.get_xy() if argname is not None: - if argname in CONTENT_BLOCKS: + if argname in content_blocks: argblk = Block(self.block_list, self.sprite_list, argname, 0, 0, 'block', [argvalue], self.block_scale) @@ -1218,8 +1261,7 @@ class TurtleArtWindow(): def process_data(self, block_data, offset=0): """ Process block_data (from a macro, a file, or the clipboard). """ - if offset != 0: - _logger.debug("offset is %d" % (offset)) + # Create the blocks (or turtle). blocks = [] for blk in block_data: @@ -1238,7 +1280,8 @@ class TurtleArtWindow(): else: cons.append(blocks[c]) else: - _logger.debug("connection error %s" % (str(block_data[i]))) + debug_output("connection error %s" % (str(block_data[i])), + self.running_sugar) cons.append(None) elif blocks[i].connections == 'check': # Convert old-style boolean and arithmetic blocks @@ -1249,7 +1292,7 @@ class TurtleArtWindow(): else: cons.append(blocks[c]) # If the boolean op was connected, readjust the plumbing. - if blocks[i].name in BOOLEAN_STYLE: + if blocks[i].name in block_styles['boolean-style']: if block_data[i][4][0] is not None: c = block_data[i][4][0] cons[0] = blocks[block_data[c][4][0]] @@ -1262,7 +1305,8 @@ class TurtleArtWindow(): blocks[c].connections[3] = None else: # Connection was to a block we haven't seen yet. - _logger.debug("Warning: dock to the future") + debug_output("Warning: dock to the future", + self.running_sugar) else: if block_data[i][4][0] is not None: c = block_data[i][4][0] @@ -1276,10 +1320,12 @@ class TurtleArtWindow(): blocks[c].connections[1] = None else: # Connection was to a block we haven't seen yet. - _logger.debug("Warning: dock to the future") + debug_output("Warning: dock to the future", + self.running_sugar) else: - _logger.debug("Warning: unknown connection state %s" % \ - (str(blocks[i].connections))) + debug_output("Warning: unknown connection state %s" % \ + (str(blocks[i].connections)), + self.running_sugar) blocks[i].connections = cons[:] # Block sizes and shapes may have changed. @@ -1310,7 +1356,7 @@ class TurtleArtWindow(): return (sx, sy) = blk.spr.get_xy() for i, c in enumerate(blk.connections): - if i > 0 and c is not None: + if i > 0 and c is not None and i < len(blk.docks): bdock = blk.docks[i] for j in range(len(c.docks)): if j < len(c.connections) and c.connections[j] == blk: @@ -1322,8 +1368,8 @@ class TurtleArtWindow(): def _turtle_pressed(self, x, y): (tx, ty) = self.selected_turtle.get_xy() - w = self.active_turtle.spr.rect.width / 2 - h = self.active_turtle.spr.rect.height / 2 + w = self.selected_turtle.spr.rect.width / 2 + h = self.selected_turtle.spr.rect.height / 2 dx = x - tx - w dy = y - ty - h # if x, y is near the edge, rotate @@ -1349,17 +1395,29 @@ class TurtleArtWindow(): if self.selected_turtle is not None: dtype, dragx, dragy = self.drag_turtle (sx, sy) = self.selected_turtle.get_xy() + # self.canvas.set_turtle(self.selected_turtle.get_name()) if dtype == 'move': - dx = x - dragx - sx - dy = y - dragy - sy + dx = x - dragx - sx + self.selected_turtle.spr.rect.width / 2 + dy = y - dragy - sy + self.selected_turtle.spr.rect.width / 2 self.selected_turtle.spr.set_layer(TOP_LAYER) - self.selected_turtle.move((sx + dx, sy + dy)) + tx, ty = self.canvas.screen_to_turtle_coordinates(sx + dx, + sy + dy) + if self.canvas.pendown: + self.canvas.setpen(False) + self.canvas.setxy(tx, ty) + self.canvas.setpen(True) + else: + self.canvas.setxy(tx, ty) else: - dx = x - sx - self.active_turtle.spr.rect.width / 2 - dy = y - sy - self.active_turtle.spr.rect.height / 2 + dx = x - sx - self.selected_turtle.spr.rect.width / 2 + dy = y - sy - self.selected_turtle.spr.rect.height / 2 self.canvas.seth(int(dragx + atan2(dy, dx) / DEGTOR + 5) / \ 10 * 10) self.lc.update_label_value('heading', self.canvas.heading) + if self.sharing(): # share turtle rotation + self.send_event("r|%s" % ( + data_to_string([self.selected_turtle.get_name(), + round_int(self.canvas.heading)]))) # If we are hoving, show popup help. elif self.drag_group is None: @@ -1468,27 +1526,27 @@ class TurtleArtWindow(): def _do_show_popup(self, block_name): """ Fetch the help text and display it. """ - if block_name in SPECIAL_NAMES: - block_name_s = SPECIAL_NAMES[block_name] - elif block_name in BLOCK_NAMES: - block_name_s = BLOCK_NAMES[block_name][0] + if block_name in special_names: + special_block_name = special_names[block_name] + elif block_name in block_names: + special_block_name = block_names[block_name][0] elif block_name in TOOLBAR_SHAPES: - block_name_s = '' + special_block_name = '' else: - block_name_s = _(block_name) - if block_name in HELP_STRINGS: - if block_name_s == '': - label = HELP_STRINGS[block_name] + special_block_name = _(block_name) + if block_name in help_strings: + if special_block_name == '': + label = help_strings[block_name] else: - label = block_name_s + ": " + HELP_STRINGS[block_name] + label = special_block_name + ": " + help_strings[block_name] else: - label = block_name_s + label = special_block_name if self.running_sugar: self.activity.hover_help_label.set_text(label) self.activity.hover_help_label.show() else: if self.interactive_mode: - self.win.set_title(_("Turtle Art") + " — " + label) + self.parent.set_title(_("Turtle Art") + " — " + label) return 0 def _buttonrelease_cb(self, win, event): @@ -1514,14 +1572,15 @@ class TurtleArtWindow(): else: self.selected_turtle.hide() self.turtles.remove_from_dict(k) + self.active_turtle = None else: self._move_turtle(tx - self.canvas.width / 2 + \ self.active_turtle.spr.rect.width / 2, self.canvas.height / 2 - ty - \ self.active_turtle.spr.rect.height / 2) self.selected_turtle = None - self.active_turtle = self.turtles.get_turtle( - self.default_turtle_name) + if self.active_turtle is None: + self.canvas.set_turtle(self.default_turtle_name) return # If we don't have a group of blocks, then there is nothing to do. @@ -1557,6 +1616,30 @@ class TurtleArtWindow(): if self.block_operation == 'click': self._click_block(x, y) + def remote_turtle(self, name): + ''' Is this a remote turtle? ''' + if name == self.nick: + return False + if hasattr(self, 'remote_turtle_dictionary') and \ + name in self.remote_turtle_dictionary: + return True + return False + + def label_remote_turtle(self, name, colors=['#A0A0A0', '#C0C0C0']): + ''' Add a label to remote turtles ''' + turtle = self.turtles.get_turtle(name) + if turtle is not None: + turtle.label_block = Block(self.block_list, + self.sprite_list, 'turtle-label', 0, 0, + 'label', [], 1.0 / self.scale, + colors) + turtle.label_block.spr.set_label_attributes(6.0 / self.scale) + if len(name) > 6: + turtle.label_block.spr.set_label(name[0:4] + '…') + else: + turtle.label_block.spr.set_label(name) + turtle.show() + def _move_turtle(self, x, y): """ Move the selected turtle to (x, y). """ (cx, cy) = self.canvas.canvas.get_xy() @@ -1570,8 +1653,6 @@ class TurtleArtWindow(): self.canvas.xcor / self.coord_scale) self.lc.update_label_value('ycor', self.canvas.ycor / self.coord_scale) - if len(self.lc.value_blocks['see']) > 0: - self.lc.see() def _click_block(self, x, y): """ Click block: lots of special cases to handle... """ @@ -1584,7 +1665,9 @@ class TurtleArtWindow(): self.saved_string = blk.spr.labels[0] blk.spr.labels[0] += CURSOR - elif blk.name in BOX_STYLE_MEDIA and blk.name != 'camera': + elif blk.name in block_styles['box-style-media'] and \ + blk.name != 'camera': + # TODO: isolate reference to camera self._import_from_journal(self.selected_blk) if blk.name == 'journal' and self.running_sugar: self._load_description_block(blk) @@ -1597,7 +1680,8 @@ class TurtleArtWindow(): dx = 20 blk.expand_in_x(dx) else: - dx = 0 + self._run_stack(blk) + return for gblk in group: if gblk != blk: gblk.spr.move_relative((dx * blk.scale, 0)) @@ -1610,13 +1694,14 @@ class TurtleArtWindow(): dy = 20 blk.expand_in_y(dy) else: - dy = 0 + self._run_stack(blk) + return for gblk in group: if gblk != blk: gblk.spr.move_relative((0, dy * blk.scale)) grow_stack_arm(find_sandwich_top(blk)) - elif blk.name in EXPANDABLE_BLOCKS: + elif blk.name in expandable_blocks: # Connection may be lost during expansion, so store it... blk0 = blk.connections[0] if blk0 is not None: @@ -1628,11 +1713,10 @@ class TurtleArtWindow(): dy = 20 blk.expand_in_y(dy) else: - self._start_audiograb() self._run_stack(blk) return - if blk.name in BOOLEAN_STYLE: + if blk.name in block_styles['boolean-style']: self._expand_boolean(blk, blk.connections[1], dy) else: self._expand_expandable(blk, blk.connections[1], dy) @@ -1664,17 +1748,20 @@ class TurtleArtWindow(): dy = blk.add_arg() blk.primitive = 'userdefined2' blk.name = 'userdefined2args' + self._resize_skin(blk) elif blk.name == 'userdefined2args': dy = blk.add_arg(False) blk.primitive = 'userdefined3' blk.name = 'userdefined3args' + self._resize_skin(blk) else: dy = blk.add_arg() for gblk in group: gblk.spr.move_relative((0, dy)) blk.connections.append(blk.connections[n - 1]) argname = blk.docks[n - 1][0] - argvalue = DEFAULTS[blk.name][len(DEFAULTS[blk.name]) - 1] + argvalue = default_values[blk.name][len( + default_values[blk.name]) - 1] argblk = Block(self.block_list, self.sprite_list, argname, 0, 0, 'block', [argvalue], self.block_scale) argdock = argblk.docks[0] @@ -1685,28 +1772,26 @@ class TurtleArtWindow(): argblk.spr.set_layer(TOP_LAYER) argblk.connections = [blk, None] blk.connections[n - 1] = argblk - if blk.name in NUMBER_STYLE_VAR_ARG: + if blk.name in block_styles['number-style-var-arg']: self._cascade_expandable(blk) grow_stack_arm(find_sandwich_top(blk)) elif blk.name in PYTHON_SKIN: self._import_py() else: - self._start_audiograb() self._run_stack(blk) - elif blk.name in ['sandwichtop_no_arm_no_label', + elif blk.name in ['sandwichtop_no_arm_no_label', 'sandwichtop_no_arm']: restore_stack(blk) elif blk.name in COLLAPSIBLE: top = find_sandwich_top(blk) if collapsed(blk): - restore_stack(top) # depreciated (bottom block is invisible) + restore_stack(top) # deprecated (bottom block is invisible) elif top is not None: collapse_stack(top) else: - self._start_audiograb() self._run_stack(blk) def _expand_boolean(self, blk, blk2, dy): @@ -1726,19 +1811,27 @@ class TurtleArtWindow(): for gblk in find_group(blk): if gblk not in group: gblk.spr.move_relative((0, dy * blk.scale)) - if blk.name in COMPARE_STYLE: + if blk.name in block_styles['compare-style']: for gblk in find_group(blk): gblk.spr.move_relative((0, -dy * blk.scale)) + def _number_style(self, name): + if name in block_styles['number-style']: + return True + if name in block_styles['number-style-porch']: + return True + if name in block_styles['number-style-block']: + return True + if name in block_styles['number-style-var-arg']: + return True + return False + def _cascade_expandable(self, blk): """ If expanding/shrinking a block, cascade. """ - while blk.name in NUMBER_STYLE or \ - blk.name in NUMBER_STYLE_PORCH or \ - blk.name in NUMBER_STYLE_BLOCK or \ - blk.name in NUMBER_STYLE_VAR_ARG: + while self._number_style(blk.name): if blk.connections[0] is None: break - if blk.connections[0].name in EXPANDABLE_BLOCKS: + if blk.connections[0].name in expandable_blocks: if blk.connections[0].connections.index(blk) != 1: break blk = blk.connections[0] @@ -1755,7 +1848,7 @@ class TurtleArtWindow(): for gblk in find_group(blk): if gblk not in group: gblk.spr.move_relative((0, dy * blk.scale)) - if blk.name in COMPARE_STYLE: + if blk.name in block_styles['compare-style']: for gblk in find_group(blk): gblk.spr.move_relative((0, -dy * blk.scale)) else: @@ -1790,7 +1883,8 @@ class TurtleArtWindow(): """ Run a stack of blocks. """ if blk is None: return - self.lc.ag = None + self.lc.find_value_blocks() # Are there blocks to update? + self._start_plugins() # Let the plugins know we are running. top = find_top_block(blk) self.lc.run_blocks(top, self.just_blocks(), True) if self.interactive_mode: @@ -1848,17 +1942,18 @@ class TurtleArtWindow(): selected_block.connections[best_selected_block_dockn] = \ best_destination - if best_destination.name in BOOLEAN_STYLE: + if best_destination.name in block_styles['boolean-style']: if best_destination_dockn == 2 and \ - selected_block.name in COMPARE_STYLE: + selected_block.name in block_styles['compare-style']: dy = selected_block.ey - best_destination.ey best_destination.expand_in_y(dy) self._expand_boolean(best_destination, selected_block, dy) - elif best_destination.name in EXPANDABLE_BLOCKS and \ + elif best_destination.name in expandable_blocks and \ best_destination_dockn == 1: dy = 0 - if (selected_block.name in EXPANDABLE_BLOCKS or - selected_block.name in NUMBER_STYLE_VAR_ARG): + if (selected_block.name in expandable_blocks or + selected_block.name in block_styles[ + 'number-style-var-arg']): if selected_block.name == 'myfunc2arg': dy = 40 + selected_block.ey - best_destination.ey elif selected_block.name == 'myfunc3arg': @@ -1877,6 +1972,8 @@ class TurtleArtWindow(): def _disconnect(self, blk): """ Disconnect block from stack above it. """ + if blk is None: + return if blk.connections[0] is None: return if collapsed(blk): @@ -1886,12 +1983,12 @@ class TurtleArtWindow(): c = blk2.connections.index(blk) blk2.connections[c] = None - if blk2.name in BOOLEAN_STYLE: + if blk2.name in block_styles['boolean-style']: if c == 2 and blk2.ey > 0: dy = -blk2.ey blk2.expand_in_y(dy) self._expand_boolean(blk2, blk, dy) - elif blk2.name in EXPANDABLE_BLOCKS and c == 1: + elif blk2.name in expandable_blocks and c == 1: if blk2.ey > 0: dy = blk2.reset_y() if dy != 0: @@ -1976,7 +2073,6 @@ class TurtleArtWindow(): """ Keyboard """ keyname = gtk.gdk.keyval_name(event.keyval) keyunicode = gtk.gdk.keyval_to_unicode(event.keyval) - if event.get_state() & gtk.gdk.MOD1_MASK: alt_mask = True alt_flag = 'T' @@ -1996,9 +2092,9 @@ class TurtleArtWindow(): if keyname == "p": self.hideshow_button() elif keyname == 'q': - if self.audio_started: - self.audiograb.stop_grabbing() - stop_media(self.lc) + self._plugins_quit() + if self.gst_available: + stop_media(self.lc) exit() elif keyname == 'g': self._align_to_grid() @@ -2077,7 +2173,8 @@ class TurtleArtWindow(): oldleft, oldright = \ self.selected_blk.spr.labels[0].split(CURSOR) except ValueError: - _logger.debug("[%s]" % self.selected_blk.spr.labels[0]) + debug_output("[%s]" % self.selected_blk.spr.labels[0], + self.running_sugar) oldleft = self.selected_blk.spr.labels[0] oldright = '' else: @@ -2286,7 +2383,8 @@ class TurtleArtWindow(): f.close() id = fname except IOError: - _logger.error("Unable to read Python code from %s" % (fname)) + error_output("Unable to read Python code from %s" % (fname), + self.running_sugar) return id # if we are running Sugar, copy the file into the Journal @@ -2302,7 +2400,8 @@ class TurtleArtWindow(): datastore.write(dsobject) id = dsobject.object_id except IOError: - _logger.error("Error copying %s to the datastore" % (fname)) + error_output("Error copying %s to the datastore" % (fname), + self.running_sugar) id = None dsobject.destroy() @@ -2327,12 +2426,14 @@ class TurtleArtWindow(): """ Read the Python code from the Journal object """ self.python_code = None try: - _logger.debug("opening %s " % dsobject.file_path) + debug_output("opening %s " % dsobject.file_path, + self.running_sugar) file_handle = open(dsobject.file_path, "r") self.python_code = file_handle.read() file_handle.close() except IOError: - _logger.debug("couldn't open %s" % dsobject.file_path) + debug_output("couldn't open %s" % dsobject.file_path, + self.running_sugar) if blk is None: blk = self.selected_blk if blk is not None: @@ -2356,7 +2457,7 @@ class TurtleArtWindow(): def new_project(self): """ Start a new project """ - stop_logo(self) + self.lc.stop_logo() self._loaded_project = "" # Put current project in the trash. while len(self.just_blocks()) > 0: @@ -2377,8 +2478,9 @@ class TurtleArtWindow(): saved_project_data = f.read() f.close() except: - _logger.debug("problem loading saved project data from %s" % \ - (self._loaded_project)) + debug_output("problem loading saved project data from %s" % \ + (self._loaded_project), + self.running_sugar) saved_project_data = "" current_project_data = data_to_string(self.assemble_data_to_save()) @@ -2409,14 +2511,13 @@ class TurtleArtWindow(): if blk[1] == 'turtle': self.load_turtle(blk) return True - elif type(blk[1]) == list and blk[1][0] == 'turtle': - self.load_turtle(blk, blk[1][1]) + elif type(blk[1]) in [list, tuple] and blk[1][0] == 'turtle': + if blk[1][1] == DEFAULT_TURTLE: + if self.nick is not None and self.nick is not '': + self.load_turtle(blk, self.nick) + else: + self.load_turtle(blk, blk[1][1]) return True - elif type(blk[1]) == tuple: - _btype, _key = blk[1] - if _btype == 'turtle': - self.load_turtle(blk, _key) - return True return False def load_turtle(self, blk, key=1): @@ -2438,7 +2539,7 @@ class TurtleArtWindow(): btype, value = btype elif type(btype) == list: btype, value = btype[0], btype[1] - if btype in CONTENT_BLOCKS or btype in COLLAPSIBLE: + if btype in content_blocks or btype in COLLAPSIBLE: if btype == 'number': try: values = [round_int(value)] @@ -2467,16 +2568,17 @@ class TurtleArtWindow(): 'block', values, self.block_scale) # Some blocks get transformed. - if btype in BASIC_STYLE_VAR_ARG and value is not None: + if btype in block_styles['basic-style-var-arg'] and value is not None: # Is there code stored in this userdefined block? - if value > 0: # catch depreciated format (#2501) + if value > 0: # catch deprecated format (#2501) self.python_code = None if self.running_sugar: try: dsobject = datastore.get(value) - except: # Should be IOError, but dbus error is raised + except: # Should be IOError, but dbus error is raised dsobject = None - _logger.debug("couldn't get dsobject %s" % value) + debug_output("couldn't get dsobject %s" % value, + self.running_sugar) if dsobject is not None: self.load_python_code_from_journal(dsobject, blk) else: @@ -2493,9 +2595,9 @@ class TurtleArtWindow(): elif btype == 'start': # block size is saved in start block if value is not None: self.block_scale = value - elif btype in EXPANDABLE or btype in EXPANDABLE_BLOCKS or \ + elif btype in EXPANDABLE or btype in expandable_blocks or \ btype in EXPANDABLE_ARGS or btype == 'nop': - if btype == 'vspace' or btype in EXPANDABLE_BLOCKS: + if btype == 'vspace' or btype in expandable_blocks: if value is not None: blk.expand_in_y(value) elif btype == 'hspace' or btype == 'identity2': @@ -2514,7 +2616,8 @@ class TurtleArtWindow(): self._block_skin('pythonon', blk) else: self._block_skin('pythonoff', blk) - elif btype in BOX_STYLE_MEDIA and blk.spr is not None: + elif btype in block_styles['box-style-media'] and blk.spr is not None: + # TODO: isolate reference to camera if len(blk.values) == 0 or blk.values[0] == 'None' or \ blk.values[0] is None or btype == 'camera': self._block_skin(btype + 'off', blk) @@ -2541,8 +2644,8 @@ class TurtleArtWindow(): x, y = self._calc_image_offset('', blk.spr) blk.set_image(pixbuf, x, y) except: - _logger.debug("Couldn't open dsobject (%s)" % \ - (blk.values[0])) + debug_output("Couldn't open dsobject (%s)" % \ + (blk.values[0]), self.running_sugar) self._block_skin('journaloff', blk) else: if not movie_media_type(blk.values[0][-4:]): @@ -2605,14 +2708,15 @@ class TurtleArtWindow(): for _i, _blk in enumerate(_blks): _blk.id = _i for _blk in _blks: - if _blk.name in CONTENT_BLOCKS or _blk.name in COLLAPSIBLE: + if _blk.name in content_blocks or _blk.name in COLLAPSIBLE: if len(_blk.values) > 0: _name = (_blk.name, _blk.values[0]) else: _name = (_blk.name) - elif _blk.name in BASIC_STYLE_VAR_ARG and len(_blk.values) > 0: + elif _blk.name in block_styles['basic-style-var-arg'] and \ + len(_blk.values) > 0: _name = (_blk.name, _blk.values[0]) - elif _blk.name in EXPANDABLE or _blk.name in EXPANDABLE_BLOCKS or\ + elif _blk.name in EXPANDABLE or _blk.name in expandable_blocks or\ _blk.name in EXPANDABLE_ARGS: _ex, _ey = _blk.get_expand_x_y() if _ex > 0: @@ -2637,12 +2741,16 @@ class TurtleArtWindow(): _data.append((_blk.id, _name, _sx - self.canvas.cx, _sy - self.canvas.cy, connections)) if save_turtle: - for _turtle in iter(self.turtles.dict): - self.canvas.set_turtle(_turtle) - _data.append((-1, ['turtle', _turtle], - self.canvas.xcor, self.canvas.ycor, - self.canvas.heading, self.canvas.color, - self.canvas.shade, self.canvas.pensize)) + for turtle in iter(self.turtles.dict): + # Don't save remote turtles + if not self.remote_turtle(turtle): + # Save default turtle as 'Yertle' + if turtle == self.nick: + turtle = DEFAULT_TURTLE + _data.append((-1, ['turtle', turtle], + self.canvas.xcor, self.canvas.ycor, + self.canvas.heading, self.canvas.color, + self.canvas.shade, self.canvas.pensize)) return _data def display_coordinates(self): @@ -2655,13 +2763,13 @@ class TurtleArtWindow(): (_("xcor"), x, _("ycor"), y, _("heading"), h)) self.activity.coordinates_label.show() elif self.interactive_mode: - self.win.set_title("%s — %s: %d %s: %d %s: %d" % \ + self.parent.set_title("%s — %s: %d %s: %d %s: %d" % \ (_("Turtle Art"), _("xcor"), x, _("ycor"), y, _("heading"), h)) def showlabel(self, shp, label=''): """ Display a message on a status block """ if not self.interactive_mode: - _logger.debug(label) + debug_output(label, self.running_sugar) return if shp == 'syntaxerror' and str(label) != '': if str(label)[1:] in self.status_shapes: @@ -2683,7 +2791,7 @@ class TurtleArtWindow(): self.status_spr.move((PALETTE_WIDTH, self.height - 200)) def calc_position(self, template): - """ Relative placement of portfolio objects (depreciated) """ + """ Relative placement of portfolio objects (deprecated) """ w, h, x, y, dx, dy = TEMPLATES[template] x *= self.canvas.width y *= self.canvas.height @@ -2705,49 +2813,30 @@ class TurtleArtWindow(): def save_as_image(self, name="", svg=False, pixbuf=None): """ Grab the current canvas and save it. """ + if svg: + suffix = '.svg' + else: + suffix = '.png' if not self.interactive_mode: - save_picture(self.canvas, name[:-3] + ".png") + save_picture(self.canvas, name[:-3] + suffix) return - """ - self.color_map = self.window.get_colormap() - new_pix = pixbuf.get_from_drawable(self.window, self.color_map, - 0, 0, 0, 0, - self.width, self.height) - new_pix.save(name[:-3] + ".png", "png") - """ - if self.running_sugar: - if svg: - if len(name) == 0: - filename = "ta.svg" - else: - filename = name + ".svg" + if len(name) == 0: + filename = 'ta' + suffix else: - if len(name) == 0: - filename = "ta.png" - else: - filename = name + ".png" + filename = name + suffix datapath = get_path(self.activity, 'instance') elif len(name) == 0: - name = "ta" + name = 'ta' if self.save_folder is not None: self.load_save_folder = self.save_folder - if svg: - filename, self.load_save_folder = get_save_name('.svg', - self.load_save_folder, - name) - else: - filename, self.load_save_folder = get_save_name('.png', - self.load_save_folder, - name) + filename, self.load_save_folder = get_save_name( + suffix, self.load_save_folder, name) datapath = self.load_save_folder else: datapath = os.getcwd() - if svg: - filename = name + ".svg" - else: - filename = name + ".png" + filename = name + suffix if filename is None: return @@ -2864,3 +2953,36 @@ class TurtleArtWindow(): w, h = self._calc_w_h('descriptionoff', blk.spr) x, y = self._calc_image_offset('descriptionoff', blk.spr, w, h) blk.scale_image(x, y, w, h) + + +def dock_dx_dy(block1, dock1n, block2, dock2n): + """ Find the distance between the dock points of two blocks. """ + _dock1 = block1.docks[dock1n] + _dock2 = block2.docks[dock2n] + _d1type, _d1dir, _d1x, _d1y = _dock1[0:4] + _d2type, _d2dir, _d2x, _d2y = _dock2[0:4] + if block1 == block2: + return (100, 100) + if _d1dir == _d2dir: + return (100, 100) + if (_d2type is not 'number') or (dock2n is not 0): + if block1.connections is not None and \ + dock1n < len(block1.connections) and \ + block1.connections[dock1n] is not None: + return (100, 100) + if block2.connections is not None and \ + dock2n < len(block2.connections) and \ + block2.connections[dock2n] is not None: + return (100, 100) + if _d1type != _d2type: + if block1.name in STRING_OR_NUMBER_ARGS: + if _d2type == 'number' or _d2type == 'string': + pass + elif block1.name in CONTENT_ARGS: + if _d2type in content_blocks: + pass + else: + return (100, 100) + (_b1x, _b1y) = block1.spr.get_xy() + (_b2x, _b2y) = block2.spr.get_xy() + return ((_b1x + _d1x) - (_b2x + _d2x), (_b1y + _d1y) - (_b2y + _d2y)) diff --git a/TurtleArt/v4l2.py b/TurtleArt/v4l2.py deleted file mode 100644 index 9c052fd..0000000 --- a/TurtleArt/v4l2.py +++ /dev/null @@ -1,1914 +0,0 @@ -# Python bindings for the v4l2 userspace api - -# Copyright (C) 1999-2009 the contributors - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# Alternatively you can redistribute this file under the terms of the -# BSD license as stated below: - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# 3. The names of its contributors may not be used to endorse or promote -# products derived from this software without specific prior written -# permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -Python bindings for the v4l2 userspace api in Linux 2.6.34 -""" - -# see linux/videodev2.h - -import ctypes - - -_IOC_NRBITS = 8 -_IOC_TYPEBITS = 8 -_IOC_SIZEBITS = 14 -_IOC_DIRBITS = 2 - -_IOC_NRSHIFT = 0 -_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS -_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS -_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS - -_IOC_NONE = 0 -_IOC_WRITE = 1 -_IOC_READ = 2 - - -def _IOC(dir_, type_, nr, size): - return ( - ctypes.c_int32(dir_ << _IOC_DIRSHIFT).value | - ctypes.c_int32(ord(type_) << _IOC_TYPESHIFT).value | - ctypes.c_int32(nr << _IOC_NRSHIFT).value | - ctypes.c_int32(size << _IOC_SIZESHIFT).value) - - -def _IOC_TYPECHECK(t): - return ctypes.sizeof(t) - - -def _IO(type_, nr): - return _IOC(_IOC_NONE, type_, nr, 0) - - -def _IOW(type_, nr, size): - return _IOC(_IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) - - -def _IOR(type_, nr, size): - return _IOC(_IOC_READ, type_, nr, _IOC_TYPECHECK(size)) - - -def _IOWR(type_, nr, size): - return _IOC(_IOC_READ | _IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) - - -# -# type alias -# - -enum = ctypes.c_uint -c_int = ctypes.c_int - - -# -# time -# - -class timeval(ctypes.Structure): - _fields_ = [ - ('secs', ctypes.c_long), - ('usecs', ctypes.c_long), - ] - - -# -# v4l2 -# - - -VIDEO_MAX_FRAME = 32 - - -VID_TYPE_CAPTURE = 1 -VID_TYPE_TUNER = 2 -VID_TYPE_TELETEXT = 4 -VID_TYPE_OVERLAY = 8 -VID_TYPE_CHROMAKEY = 16 -VID_TYPE_CLIPPING = 32 -VID_TYPE_FRAMERAM = 64 -VID_TYPE_SCALES = 128 -VID_TYPE_MONOCHROME = 256 -VID_TYPE_SUBCAPTURE = 512 -VID_TYPE_MPEG_DECODER = 1024 -VID_TYPE_MPEG_ENCODER = 2048 -VID_TYPE_MJPEG_DECODER = 4096 -VID_TYPE_MJPEG_ENCODER = 8192 - - -def v4l2_fourcc(a, b, c, d): - return ord(a) | (ord(b) << 8) | (ord(c) << 16) | (ord(d) << 24) - - -v4l2_field = enum -( - V4L2_FIELD_ANY, - V4L2_FIELD_NONE, - V4L2_FIELD_TOP, - V4L2_FIELD_BOTTOM, - V4L2_FIELD_INTERLACED, - V4L2_FIELD_SEQ_TB, - V4L2_FIELD_SEQ_BT, - V4L2_FIELD_ALTERNATE, - V4L2_FIELD_INTERLACED_TB, - V4L2_FIELD_INTERLACED_BT, -) = range(10) - - -def V4L2_FIELD_HAS_TOP(field): - return ( - field == V4L2_FIELD_TOP or - field == V4L2_FIELD_INTERLACED or - field == V4L2_FIELD_INTERLACED_TB or - field == V4L2_FIELD_INTERLACED_BT or - field == V4L2_FIELD_SEQ_TB or - field == V4L2_FIELD_SEQ_BT) - - -def V4L2_FIELD_HAS_BOTTOM(field): - return ( - field == V4L2_FIELD_BOTTOM or - field == V4L2_FIELD_INTERLACED or - field == V4L2_FIELD_INTERLACED_TB or - field == V4L2_FIELD_INTERLACED_BT or - field == V4L2_FIELD_SEQ_TB or - field == V4L2_FIELD_SEQ_BT) - - -def V4L2_FIELD_HAS_BOTH(field): - return ( - field == V4L2_FIELD_INTERLACED or - field == V4L2_FIELD_INTERLACED_TB or - field == V4L2_FIELD_INTERLACED_BT or - field == V4L2_FIELD_SEQ_TB or - field == V4L2_FIELD_SEQ_BT) - - -v4l2_buf_type = enum -( - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_BUF_TYPE_VIDEO_OVERLAY, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_BUF_TYPE_VBI_OUTPUT, - V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, - V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, - V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, - V4L2_BUF_TYPE_PRIVATE, -) = range(1, 9) + [0x80] - - -v4l2_ctrl_type = enum -( - V4L2_CTRL_TYPE_INTEGER, - V4L2_CTRL_TYPE_BOOLEAN, - V4L2_CTRL_TYPE_MENU, - V4L2_CTRL_TYPE_BUTTON, - V4L2_CTRL_TYPE_INTEGER64, - V4L2_CTRL_TYPE_CTRL_CLASS, - V4L2_CTRL_TYPE_STRING, -) = range(1, 8) - - -v4l2_tuner_type = enum -( - V4L2_TUNER_RADIO, - V4L2_TUNER_ANALOG_TV, - V4L2_TUNER_DIGITAL_TV, -) = range(1, 4) - - -v4l2_memory = enum -( - V4L2_MEMORY_MMAP, - V4L2_MEMORY_USERPTR, - V4L2_MEMORY_OVERLAY, -) = range(1, 4) - - -v4l2_colorspace = enum -( - V4L2_COLORSPACE_SMPTE170M, - V4L2_COLORSPACE_SMPTE240M, - V4L2_COLORSPACE_REC709, - V4L2_COLORSPACE_BT878, - V4L2_COLORSPACE_470_SYSTEM_M, - V4L2_COLORSPACE_470_SYSTEM_BG, - V4L2_COLORSPACE_JPEG, - V4L2_COLORSPACE_SRGB, -) = range(1, 9) - - -v4l2_priority = enum -( - V4L2_PRIORITY_UNSET, - V4L2_PRIORITY_BACKGROUND, - V4L2_PRIORITY_INTERACTIVE, - V4L2_PRIORITY_RECORD, - V4L2_PRIORITY_DEFAULT, -) = range(0, 4) + [2] - - -class v4l2_rect(ctypes.Structure): - _fields_ = [ - ('left', ctypes.c_int32), - ('top', ctypes.c_int32), - ('width', ctypes.c_int32), - ('height', ctypes.c_int32), - ] - - -class v4l2_fract(ctypes.Structure): - _fields_ = [ - ('numerator', ctypes.c_uint32), - ('denominator', ctypes.c_uint32), - ] - - -# -# Driver capabilities -# - -class v4l2_capability(ctypes.Structure): - _fields_ = [ - ('driver', ctypes.c_char * 16), - ('card', ctypes.c_char * 32), - ('bus_info', ctypes.c_char * 32), - ('version', ctypes.c_uint32), - ('capabilities', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -# -# Values for 'capabilities' field -# - -V4L2_CAP_VIDEO_CAPTURE = 0x00000001 -V4L2_CAP_VIDEO_OUTPUT = 0x00000002 -V4L2_CAP_VIDEO_OVERLAY = 0x00000004 -V4L2_CAP_VBI_CAPTURE = 0x00000010 -V4L2_CAP_VBI_OUTPUT = 0x00000020 -V4L2_CAP_SLICED_VBI_CAPTURE = 0x00000040 -V4L2_CAP_SLICED_VBI_OUTPUT = 0x00000080 -V4L2_CAP_RDS_CAPTURE = 0x00000100 -V4L2_CAP_VIDEO_OUTPUT_OVERLAY = 0x00000200 -V4L2_CAP_HW_FREQ_SEEK = 0x00000400 -V4L2_CAP_RDS_OUTPUT = 0x00000800 - -V4L2_CAP_TUNER = 0x00010000 -V4L2_CAP_AUDIO = 0x00020000 -V4L2_CAP_RADIO = 0x00040000 -V4L2_CAP_MODULATOR = 0x00080000 - -V4L2_CAP_READWRITE = 0x01000000 -V4L2_CAP_ASYNCIO = 0x02000000 -V4L2_CAP_STREAMING = 0x04000000 - - -# -# Video image format -# - -class v4l2_pix_format(ctypes.Structure): - _fields_ = [ - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ('pixelformat', ctypes.c_uint32), - ('field', v4l2_field), - ('bytesperline', ctypes.c_uint32), - ('sizeimage', ctypes.c_uint32), - ('colorspace', v4l2_colorspace), - ('priv', ctypes.c_uint32), - ] - -# RGB formats -V4L2_PIX_FMT_RGB332 = v4l2_fourcc('R', 'G', 'B', '1') -V4L2_PIX_FMT_RGB444 = v4l2_fourcc('R', '4', '4', '4') -V4L2_PIX_FMT_RGB555 = v4l2_fourcc('R', 'G', 'B', 'O') -V4L2_PIX_FMT_RGB565 = v4l2_fourcc('R', 'G', 'B', 'P') -V4L2_PIX_FMT_RGB555X = v4l2_fourcc('R', 'G', 'B', 'Q') -V4L2_PIX_FMT_RGB565X = v4l2_fourcc('R', 'G', 'B', 'R') -V4L2_PIX_FMT_BGR24 = v4l2_fourcc('B', 'G', 'R', '3') -V4L2_PIX_FMT_RGB24 = v4l2_fourcc('R', 'G', 'B', '3') -V4L2_PIX_FMT_BGR32 = v4l2_fourcc('B', 'G', 'R', '4') -V4L2_PIX_FMT_RGB32 = v4l2_fourcc('R', 'G', 'B', '4') - -# Grey formats -V4L2_PIX_FMT_GREY = v4l2_fourcc('G', 'R', 'E', 'Y') -V4L2_PIX_FMT_Y10 = v4l2_fourcc('Y', '1', '0', ' ') -V4L2_PIX_FMT_Y16 = v4l2_fourcc('Y', '1', '6', ' ') - -# Palette formats -V4L2_PIX_FMT_PAL8 = v4l2_fourcc('P', 'A', 'L', '8') - -# Luminance+Chrominance formats -V4L2_PIX_FMT_YVU410 = v4l2_fourcc('Y', 'V', 'U', '9') -V4L2_PIX_FMT_YVU420 = v4l2_fourcc('Y', 'V', '1', '2') -V4L2_PIX_FMT_YUYV = v4l2_fourcc('Y', 'U', 'Y', 'V') -V4L2_PIX_FMT_YYUV = v4l2_fourcc('Y', 'Y', 'U', 'V') -V4L2_PIX_FMT_YVYU = v4l2_fourcc('Y', 'V', 'Y', 'U') -V4L2_PIX_FMT_UYVY = v4l2_fourcc('U', 'Y', 'V', 'Y') -V4L2_PIX_FMT_VYUY = v4l2_fourcc('V', 'Y', 'U', 'Y') -V4L2_PIX_FMT_YUV422P = v4l2_fourcc('4', '2', '2', 'P') -V4L2_PIX_FMT_YUV411P = v4l2_fourcc('4', '1', '1', 'P') -V4L2_PIX_FMT_Y41P = v4l2_fourcc('Y', '4', '1', 'P') -V4L2_PIX_FMT_YUV444 = v4l2_fourcc('Y', '4', '4', '4') -V4L2_PIX_FMT_YUV555 = v4l2_fourcc('Y', 'U', 'V', 'O') -V4L2_PIX_FMT_YUV565 = v4l2_fourcc('Y', 'U', 'V', 'P') -V4L2_PIX_FMT_YUV32 = v4l2_fourcc('Y', 'U', 'V', '4') -V4L2_PIX_FMT_YUV410 = v4l2_fourcc('Y', 'U', 'V', '9') -V4L2_PIX_FMT_YUV420 = v4l2_fourcc('Y', 'U', '1', '2') -V4L2_PIX_FMT_HI240 = v4l2_fourcc('H', 'I', '2', '4') -V4L2_PIX_FMT_HM12 = v4l2_fourcc('H', 'M', '1', '2') - -# two planes -- one Y, one Cr + Cb interleaved -V4L2_PIX_FMT_NV12 = v4l2_fourcc('N', 'V', '1', '2') -V4L2_PIX_FMT_NV21 = v4l2_fourcc('N', 'V', '2', '1') -V4L2_PIX_FMT_NV16 = v4l2_fourcc('N', 'V', '1', '6') -V4L2_PIX_FMT_NV61 = v4l2_fourcc('N', 'V', '6', '1') - -# Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm -V4L2_PIX_FMT_SBGGR8 = v4l2_fourcc('B', 'A', '8', '1') -V4L2_PIX_FMT_SGBRG8 = v4l2_fourcc('G', 'B', 'R', 'G') -V4L2_PIX_FMT_SGRBG8 = v4l2_fourcc('G', 'R', 'B', 'G') -V4L2_PIX_FMT_SRGGB8 = v4l2_fourcc('R', 'G', 'G', 'B') -V4L2_PIX_FMT_SBGGR10 = v4l2_fourcc('B', 'G', '1', '0') -V4L2_PIX_FMT_SGBRG10 = v4l2_fourcc('G', 'B', '1', '0') -V4L2_PIX_FMT_SGRBG10 = v4l2_fourcc('B', 'A', '1', '0') -V4L2_PIX_FMT_SRGGB10 = v4l2_fourcc('R', 'G', '1', '0') -V4L2_PIX_FMT_SGRBG10DPCM8 = v4l2_fourcc('B', 'D', '1', '0') -V4L2_PIX_FMT_SBGGR16 = v4l2_fourcc('B', 'Y', 'R', '2') - -# compressed formats -V4L2_PIX_FMT_MJPEG = v4l2_fourcc('M', 'J', 'P', 'G') -V4L2_PIX_FMT_JPEG = v4l2_fourcc('J', 'P', 'E', 'G') -V4L2_PIX_FMT_DV = v4l2_fourcc('d', 'v', 's', 'd') -V4L2_PIX_FMT_MPEG = v4l2_fourcc('M', 'P', 'E', 'G') - -# Vendor-specific formats -V4L2_PIX_FMT_CPIA1 = v4l2_fourcc('C', 'P', 'I', 'A') -V4L2_PIX_FMT_WNVA = v4l2_fourcc('W', 'N', 'V', 'A') -V4L2_PIX_FMT_SN9C10X = v4l2_fourcc('S', '9', '1', '0') -V4L2_PIX_FMT_SN9C20X_I420 = v4l2_fourcc('S', '9', '2', '0') -V4L2_PIX_FMT_PWC1 = v4l2_fourcc('P', 'W', 'C', '1') -V4L2_PIX_FMT_PWC2 = v4l2_fourcc('P', 'W', 'C', '2') -V4L2_PIX_FMT_ET61X251 = v4l2_fourcc('E', '6', '2', '5') -V4L2_PIX_FMT_SPCA501 = v4l2_fourcc('S', '5', '0', '1') -V4L2_PIX_FMT_SPCA505 = v4l2_fourcc('S', '5', '0', '5') -V4L2_PIX_FMT_SPCA508 = v4l2_fourcc('S', '5', '0', '8') -V4L2_PIX_FMT_SPCA561 = v4l2_fourcc('S', '5', '6', '1') -V4L2_PIX_FMT_PAC207 = v4l2_fourcc('P', '2', '0', '7') -V4L2_PIX_FMT_MR97310A = v4l2_fourcc('M', '3', '1', '0') -V4L2_PIX_FMT_SN9C2028 = v4l2_fourcc('S', 'O', 'N', 'X') -V4L2_PIX_FMT_SQ905C = v4l2_fourcc('9', '0', '5', 'C') -V4L2_PIX_FMT_PJPG = v4l2_fourcc('P', 'J', 'P', 'G') -V4L2_PIX_FMT_OV511 = v4l2_fourcc('O', '5', '1', '1') -V4L2_PIX_FMT_OV518 = v4l2_fourcc('O', '5', '1', '8') -V4L2_PIX_FMT_STV0680 = v4l2_fourcc('S', '6', '8', '0') - - -# -# Format enumeration -# - -class v4l2_fmtdesc(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('type', ctypes.c_int), - ('flags', ctypes.c_uint32), - ('description', ctypes.c_char * 32), - ('pixelformat', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - -V4L2_FMT_FLAG_COMPRESSED = 0x0001 -V4L2_FMT_FLAG_EMULATED = 0x0002 - - -# -# Experimental frame size and frame rate enumeration -# - -v4l2_frmsizetypes = enum -( - V4L2_FRMSIZE_TYPE_DISCRETE, - V4L2_FRMSIZE_TYPE_CONTINUOUS, - V4L2_FRMSIZE_TYPE_STEPWISE, -) = range(1, 4) - - -class v4l2_frmsize_discrete(ctypes.Structure): - _fields_ = [ - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ] - - -class v4l2_frmsize_stepwise(ctypes.Structure): - _fields_ = [ - ('min_width', ctypes.c_uint32), - ('min_height', ctypes.c_uint32), - ('step_width', ctypes.c_uint32), - ('min_height', ctypes.c_uint32), - ('max_height', ctypes.c_uint32), - ('step_height', ctypes.c_uint32), - ] - - -class v4l2_frmsizeenum(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('discrete', v4l2_frmsize_discrete), - ('stepwise', v4l2_frmsize_stepwise), - ] - - _fields_ = [ - ('index', ctypes.c_uint32), - ('pixel_format', ctypes.c_uint32), - ('type', ctypes.c_uint32), - ('_u', _u), - ('reserved', ctypes.c_uint32 * 2) - ] - - _anonymous_ = ('_u',) - - -# -# Frame rate enumeration -# - -v4l2_frmivaltypes = enum -( - V4L2_FRMIVAL_TYPE_DISCRETE, - V4L2_FRMIVAL_TYPE_CONTINUOUS, - V4L2_FRMIVAL_TYPE_STEPWISE, -) = range(1, 4) - - -class v4l2_frmival_stepwise(ctypes.Structure): - _fields_ = [ - ('min', v4l2_fract), - ('max', v4l2_fract), - ('step', v4l2_fract), - ] - - -class v4l2_frmivalenum(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('discrete', v4l2_fract), - ('stepwise', v4l2_frmival_stepwise), - ] - - _fields_ = [ - ('index', ctypes.c_uint32), - ('pixel_format', ctypes.c_uint32), - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ('type', ctypes.c_uint32), - ('_u', _u), - ('reserved', ctypes.c_uint32 * 2), - ] - - _anonymous_ = ('_u',) - - -# -# Timecode -# - -class v4l2_timecode(ctypes.Structure): - _fields_ = [ - ('type', ctypes.c_uint32), - ('flags', ctypes.c_uint32), - ('frames', ctypes.c_uint8), - ('seconds', ctypes.c_uint8), - ('minutes', ctypes.c_uint8), - ('hours', ctypes.c_uint8), - ('userbits', ctypes.c_uint8 * 4), - ] - - -V4L2_TC_TYPE_24FPS = 1 -V4L2_TC_TYPE_25FPS = 2 -V4L2_TC_TYPE_30FPS = 3 -V4L2_TC_TYPE_50FPS = 4 -V4L2_TC_TYPE_60FPS = 5 - -V4L2_TC_FLAG_DROPFRAME = 0x0001 -V4L2_TC_FLAG_COLORFRAME = 0x0002 -V4L2_TC_USERBITS_field = 0x000C -V4L2_TC_USERBITS_USERDEFINED = 0x0000 -V4L2_TC_USERBITS_8BITCHARS = 0x0008 - - -class v4l2_jpegcompression(ctypes.Structure): - _fields_ = [ - ('quality', ctypes.c_int), - ('APPn', ctypes.c_int), - ('APP_len', ctypes.c_int), - ('APP_data', ctypes.c_char * 60), - ('COM_len', ctypes.c_int), - ('COM_data', ctypes.c_char * 60), - ('jpeg_markers', ctypes.c_uint32), - ] - - -V4L2_JPEG_MARKER_DHT = 1 << 3 -V4L2_JPEG_MARKER_DQT = 1 << 4 -V4L2_JPEG_MARKER_DRI = 1 << 5 -V4L2_JPEG_MARKER_COM = 1 << 6 -V4L2_JPEG_MARKER_APP = 1 << 7 - - -# -# Memory-mapping buffers -# - -class v4l2_requestbuffers(ctypes.Structure): - _fields_ = [ - ('count', ctypes.c_uint32), - ('type', v4l2_buf_type), - ('memory', v4l2_memory), - ('reserved', ctypes.c_uint32 * 2), - ] - - -class v4l2_buffer(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('offset', ctypes.c_uint32), - ('userptr', ctypes.c_ulong), - ] - - _fields_ = [ - ('index', ctypes.c_uint32), - ('type', v4l2_buf_type), - ('bytesused', ctypes.c_uint32), - ('flags', ctypes.c_uint32), - ('field', v4l2_field), - ('timestamp', timeval), - ('timecode', v4l2_timecode), - ('sequence', ctypes.c_uint32), - ('memory', v4l2_memory), - ('m', _u), - ('length', ctypes.c_uint32), - ('input', ctypes.c_uint32), - ('reserved', ctypes.c_uint32), - ] - - -V4L2_BUF_FLAG_MAPPED = 0x0001 -V4L2_BUF_FLAG_QUEUED = 0x0002 -V4L2_BUF_FLAG_DONE = 0x0004 -V4L2_BUF_FLAG_KEYFRAME = 0x0008 -V4L2_BUF_FLAG_PFRAME = 0x0010 -V4L2_BUF_FLAG_BFRAME = 0x0020 -V4L2_BUF_FLAG_TIMECODE = 0x0100 -V4L2_BUF_FLAG_INPUT = 0x0200 - - -# -# Overlay preview -# - -class v4l2_framebuffer(ctypes.Structure): - _fields_ = [ - ('capability', ctypes.c_uint32), - ('flags', ctypes.c_uint32), - ('base', ctypes.c_void_p), - ('fmt', v4l2_pix_format), - ] - -V4L2_FBUF_CAP_EXTERNOVERLAY = 0x0001 -V4L2_FBUF_CAP_CHROMAKEY = 0x0002 -V4L2_FBUF_CAP_LIST_CLIPPING = 0x0004 -V4L2_FBUF_CAP_BITMAP_CLIPPING = 0x0008 -V4L2_FBUF_CAP_LOCAL_ALPHA = 0x0010 -V4L2_FBUF_CAP_GLOBAL_ALPHA = 0x0020 -V4L2_FBUF_CAP_LOCAL_INV_ALPHA = 0x0040 -V4L2_FBUF_CAP_SRC_CHROMAKEY = 0x0080 - -V4L2_FBUF_FLAG_PRIMARY = 0x0001 -V4L2_FBUF_FLAG_OVERLAY = 0x0002 -V4L2_FBUF_FLAG_CHROMAKEY = 0x0004 -V4L2_FBUF_FLAG_LOCAL_ALPHA = 0x0008 -V4L2_FBUF_FLAG_GLOBAL_ALPHA = 0x0010 -V4L2_FBUF_FLAG_LOCAL_INV_ALPHA = 0x0020 -V4L2_FBUF_FLAG_SRC_CHROMAKEY = 0x0040 - - -class v4l2_clip(ctypes.Structure): - pass -v4l2_clip._fields_ = [ - ('c', v4l2_rect), - ('next', ctypes.POINTER(v4l2_clip)), -] - - -class v4l2_window(ctypes.Structure): - _fields_ = [ - ('w', v4l2_rect), - ('field', v4l2_field), - ('chromakey', ctypes.c_uint32), - ('clips', ctypes.POINTER(v4l2_clip)), - ('clipcount', ctypes.c_uint32), - ('bitmap', ctypes.c_void_p), - ('global_alpha', ctypes.c_uint8), - ] - - -# -# Capture parameters -# - -class v4l2_captureparm(ctypes.Structure): - _fields_ = [ - ('capability', ctypes.c_uint32), - ('capturemode', ctypes.c_uint32), - ('timeperframe', v4l2_fract), - ('extendedmode', ctypes.c_uint32), - ('readbuffers', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -V4L2_MODE_HIGHQUALITY = 0x0001 -V4L2_CAP_TIMEPERFRAME = 0x1000 - - -class v4l2_outputparm(ctypes.Structure): - _fields_ = [ - ('capability', ctypes.c_uint32), - ('outputmode', ctypes.c_uint32), - ('timeperframe', v4l2_fract), - ('extendedmode', ctypes.c_uint32), - ('writebuffers', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -# -# Input image cropping -# - -class v4l2_cropcap(ctypes.Structure): - _fields_ = [ - ('type', v4l2_buf_type), - ('bounds', v4l2_rect), - ('defrect', v4l2_rect), - ('pixelaspect', v4l2_fract), - ] - - -class v4l2_crop(ctypes.Structure): - _fields_ = [ - ('type', ctypes.c_int), - ('c', v4l2_rect), - ] - - -# -# Analog video standard -# - -v4l2_std_id = ctypes.c_uint64 - - -V4L2_STD_PAL_B = 0x00000001 -V4L2_STD_PAL_B1 = 0x00000002 -V4L2_STD_PAL_G = 0x00000004 -V4L2_STD_PAL_H = 0x00000008 -V4L2_STD_PAL_I = 0x00000010 -V4L2_STD_PAL_D = 0x00000020 -V4L2_STD_PAL_D1 = 0x00000040 -V4L2_STD_PAL_K = 0x00000080 - -V4L2_STD_PAL_M = 0x00000100 -V4L2_STD_PAL_N = 0x00000200 -V4L2_STD_PAL_Nc = 0x00000400 -V4L2_STD_PAL_60 = 0x00000800 - -V4L2_STD_NTSC_M = 0x00001000 -V4L2_STD_NTSC_M_JP = 0x00002000 -V4L2_STD_NTSC_443 = 0x00004000 -V4L2_STD_NTSC_M_KR = 0x00008000 - -V4L2_STD_SECAM_B = 0x00010000 -V4L2_STD_SECAM_D = 0x00020000 -V4L2_STD_SECAM_G = 0x00040000 -V4L2_STD_SECAM_H = 0x00080000 -V4L2_STD_SECAM_K = 0x00100000 -V4L2_STD_SECAM_K1 = 0x00200000 -V4L2_STD_SECAM_L = 0x00400000 -V4L2_STD_SECAM_LC = 0x00800000 - -V4L2_STD_ATSC_8_VSB = 0x01000000 -V4L2_STD_ATSC_16_VSB = 0x02000000 - - -# some common needed stuff -V4L2_STD_PAL_BG = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_PAL_G) -V4L2_STD_PAL_DK = (V4L2_STD_PAL_D | V4L2_STD_PAL_D1 | V4L2_STD_PAL_K) -V4L2_STD_PAL = (V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_H | V4L2_STD_PAL_I) -V4L2_STD_NTSC = (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR) -V4L2_STD_SECAM_DK = (V4L2_STD_SECAM_D | V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1) -V4L2_STD_SECAM = (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H | V4L2_STD_SECAM_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC) - -V4L2_STD_525_60 = (V4L2_STD_PAL_M | V4L2_STD_PAL_60 | V4L2_STD_NTSC | V4L2_STD_NTSC_443) -V4L2_STD_625_50 = (V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_SECAM) -V4L2_STD_ATSC = (V4L2_STD_ATSC_8_VSB | V4L2_STD_ATSC_16_VSB) - -V4L2_STD_UNKNOWN = 0 -V4L2_STD_ALL = (V4L2_STD_525_60 | V4L2_STD_625_50) - -# some merged standards -V4L2_STD_MN = (V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_NTSC) -V4L2_STD_B = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_SECAM_B) -V4L2_STD_GH = (V4L2_STD_PAL_G | V4L2_STD_PAL_H|V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) -V4L2_STD_DK = (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK) - - -class v4l2_standard(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('id', v4l2_std_id), - ('name', ctypes.c_char * 24), - ('frameperiod', v4l2_fract), - ('framelines', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -# -# Video timings dv preset -# - -class v4l2_dv_preset(ctypes.Structure): - _fields_ = [ - ('preset', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4) - ] - - -# -# DV preset enumeration -# - -class v4l2_dv_enum_preset(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('preset', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - -# -# DV preset values -# - -V4L2_DV_INVALID = 0 -V4L2_DV_480P59_94 = 1 -V4L2_DV_576P50 = 2 -V4L2_DV_720P24 = 3 -V4L2_DV_720P25 = 4 -V4L2_DV_720P30 = 5 -V4L2_DV_720P50 = 6 -V4L2_DV_720P59_94 = 7 -V4L2_DV_720P60 = 8 -V4L2_DV_1080I29_97 = 9 -V4L2_DV_1080I30 = 10 -V4L2_DV_1080I25 = 11 -V4L2_DV_1080I50 = 12 -V4L2_DV_1080I60 = 13 -V4L2_DV_1080P24 = 14 -V4L2_DV_1080P25 = 15 -V4L2_DV_1080P30 = 16 -V4L2_DV_1080P50 = 17 -V4L2_DV_1080P60 = 18 - - -# -# DV BT timings -# - -class v4l2_bt_timings(ctypes.Structure): - _fields_ = [ - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ('interlaced', ctypes.c_uint32), - ('polarities', ctypes.c_uint32), - ('pixelclock', ctypes.c_uint64), - ('hfrontporch', ctypes.c_uint32), - ('hsync', ctypes.c_uint32), - ('hbackporch', ctypes.c_uint32), - ('vfrontporch', ctypes.c_uint32), - ('vsync', ctypes.c_uint32), - ('vbackporch', ctypes.c_uint32), - ('il_vfrontporch', ctypes.c_uint32), - ('il_vsync', ctypes.c_uint32), - ('il_vbackporch', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 16), - ] - - _pack_ = True - -# Interlaced or progressive format -V4L2_DV_PROGRESSIVE = 0 -V4L2_DV_INTERLACED = 1 - -# Polarities. If bit is not set, it is assumed to be negative polarity -V4L2_DV_VSYNC_POS_POL = 0x00000001 -V4L2_DV_HSYNC_POS_POL = 0x00000002 - - -class v4l2_dv_timings(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('bt', v4l2_bt_timings), - ('reserved', ctypes.c_uint32 * 32), - ] - - _fields_ = [ - ('type', ctypes.c_uint32), - ('_u', _u), - ] - - _anonymous_ = ('_u',) - _pack_ = True - - -# Values for the type field -V4L2_DV_BT_656_1120 = 0 - - -# -# Video inputs -# - -class v4l2_input(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('type', ctypes.c_uint32), - ('audioset', ctypes.c_uint32), - ('tuner', ctypes.c_uint32), - ('std', v4l2_std_id), - ('status', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -V4L2_INPUT_TYPE_TUNER = 1 -V4L2_INPUT_TYPE_CAMERA = 2 - -V4L2_IN_ST_NO_POWER = 0x00000001 -V4L2_IN_ST_NO_SIGNAL = 0x00000002 -V4L2_IN_ST_NO_COLOR = 0x00000004 - -V4L2_IN_ST_HFLIP = 0x00000010 -V4L2_IN_ST_VFLIP = 0x00000020 - -V4L2_IN_ST_NO_H_LOCK = 0x00000100 -V4L2_IN_ST_COLOR_KILL = 0x00000200 - -V4L2_IN_ST_NO_SYNC = 0x00010000 -V4L2_IN_ST_NO_EQU = 0x00020000 -V4L2_IN_ST_NO_CARRIER = 0x00040000 - -V4L2_IN_ST_MACROVISION = 0x01000000 -V4L2_IN_ST_NO_ACCESS = 0x02000000 -V4L2_IN_ST_VTR = 0x04000000 - -V4L2_IN_CAP_PRESETS = 0x00000001 -V4L2_IN_CAP_CUSTOM_TIMINGS = 0x00000002 -V4L2_IN_CAP_STD = 0x00000004 - -# -# Video outputs -# - -class v4l2_output(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('type', ctypes.c_uint32), - ('audioset', ctypes.c_uint32), - ('modulator', ctypes.c_uint32), - ('std', v4l2_std_id), - ('reserved', ctypes.c_uint32 * 4), - ] - - -V4L2_OUTPUT_TYPE_MODULATOR = 1 -V4L2_OUTPUT_TYPE_ANALOG = 2 -V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY = 3 - -V4L2_OUT_CAP_PRESETS = 0x00000001 -V4L2_OUT_CAP_CUSTOM_TIMINGS = 0x00000002 -V4L2_OUT_CAP_STD = 0x00000004 - -# -# Controls -# - -class v4l2_control(ctypes.Structure): - _fields_ = [ - ('id', ctypes.c_uint32), - ('value', ctypes.c_int32), - ] - - -class v4l2_ext_control(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('value', ctypes.c_int32), - ('value64', ctypes.c_int64), - ('reserved', ctypes.c_void_p), - ] - - _fields_ = [ - ('id', ctypes.c_uint32), - ('reserved2', ctypes.c_uint32 * 2), - ('_u', _u) - ] - - _anonymous_ = ('_u',) - _pack_ = True - - -class v4l2_ext_controls(ctypes.Structure): - _fields_ = [ - ('ctrl_class', ctypes.c_uint32), - ('count', ctypes.c_uint32), - ('error_idx', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ('controls', ctypes.POINTER(v4l2_ext_control)), - ] - - -V4L2_CTRL_CLASS_USER = 0x00980000 -V4L2_CTRL_CLASS_MPEG = 0x00990000 -V4L2_CTRL_CLASS_CAMERA = 0x009a0000 -V4L2_CTRL_CLASS_FM_TX = 0x009b0000 - - -def V4L2_CTRL_ID_MASK(): - return 0x0fffffff - - -def V4L2_CTRL_ID2CLASS(id_): - return id_ & 0x0fff0000 # unsigned long - - -def V4L2_CTRL_DRIVER_PRIV(id_): - return (id_ & 0xffff) >= 0x1000 - - -class v4l2_queryctrl(ctypes.Structure): - _fields_ = [ - ('id', ctypes.c_uint32), - ('type', v4l2_ctrl_type), - ('name', ctypes.c_char * 32), - ('minimum', ctypes.c_int32), - ('maximum', ctypes.c_int32), - ('step', ctypes.c_int32), - ('default', ctypes.c_int32), - ('flags', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -class v4l2_querymenu(ctypes.Structure): - _fields_ = [ - ('id', ctypes.c_uint32), - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('reserved', ctypes.c_uint32), - ] - - -V4L2_CTRL_FLAG_DISABLED = 0x0001 -V4L2_CTRL_FLAG_GRABBED = 0x0002 -V4L2_CTRL_FLAG_READ_ONLY = 0x0004 -V4L2_CTRL_FLAG_UPDATE = 0x0008 -V4L2_CTRL_FLAG_INACTIVE = 0x0010 -V4L2_CTRL_FLAG_SLIDER = 0x0020 -V4L2_CTRL_FLAG_WRITE_ONLY = 0x0040 - -V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000 - -V4L2_CID_BASE = V4L2_CTRL_CLASS_USER | 0x900 -V4L2_CID_USER_BASE = V4L2_CID_BASE -V4L2_CID_PRIVATE_BASE = 0x08000000 - -V4L2_CID_USER_CLASS = V4L2_CTRL_CLASS_USER | 1 -V4L2_CID_BRIGHTNESS = V4L2_CID_BASE + 0 -V4L2_CID_CONTRAST = V4L2_CID_BASE + 1 -V4L2_CID_SATURATION = V4L2_CID_BASE + 2 -V4L2_CID_HUE = V4L2_CID_BASE + 3 -V4L2_CID_AUDIO_VOLUME = V4L2_CID_BASE + 5 -V4L2_CID_AUDIO_BALANCE = V4L2_CID_BASE + 6 -V4L2_CID_AUDIO_BASS = V4L2_CID_BASE + 7 -V4L2_CID_AUDIO_TREBLE = V4L2_CID_BASE + 8 -V4L2_CID_AUDIO_MUTE = V4L2_CID_BASE + 9 -V4L2_CID_AUDIO_LOUDNESS = V4L2_CID_BASE + 10 -V4L2_CID_BLACK_LEVEL = V4L2_CID_BASE + 11 # Deprecated -V4L2_CID_AUTO_WHITE_BALANCE = V4L2_CID_BASE + 12 -V4L2_CID_DO_WHITE_BALANCE = V4L2_CID_BASE + 13 -V4L2_CID_RED_BALANCE = V4L2_CID_BASE + 14 -V4L2_CID_BLUE_BALANCE = V4L2_CID_BASE + 15 -V4L2_CID_GAMMA = V4L2_CID_BASE + 16 -V4L2_CID_WHITENESS = V4L2_CID_GAMMA # Deprecated -V4L2_CID_EXPOSURE = V4L2_CID_BASE + 17 -V4L2_CID_AUTOGAIN = V4L2_CID_BASE + 18 -V4L2_CID_GAIN = V4L2_CID_BASE + 19 -V4L2_CID_HFLIP = V4L2_CID_BASE + 20 -V4L2_CID_VFLIP = V4L2_CID_BASE + 21 - -# Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET -V4L2_CID_HCENTER = V4L2_CID_BASE + 22 -V4L2_CID_VCENTER = V4L2_CID_BASE + 23 - -V4L2_CID_POWER_LINE_FREQUENCY = V4L2_CID_BASE + 24 - -v4l2_power_line_frequency = enum -( - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, -) = range(3) - -V4L2_CID_HUE_AUTO = V4L2_CID_BASE + 25 -V4L2_CID_WHITE_BALANCE_TEMPERATURE = V4L2_CID_BASE + 26 -V4L2_CID_SHARPNESS = V4L2_CID_BASE + 27 -V4L2_CID_BACKLIGHT_COMPENSATION = V4L2_CID_BASE + 28 -V4L2_CID_CHROMA_AGC = V4L2_CID_BASE + 29 -V4L2_CID_COLOR_KILLER = V4L2_CID_BASE + 30 -V4L2_CID_COLORFX = V4L2_CID_BASE + 31 - -v4l2_colorfx = enum -( - V4L2_COLORFX_NONE, - V4L2_COLORFX_BW, - V4L2_COLORFX_SEPIA, -) = range(3) - -V4L2_CID_AUTOBRIGHTNESS = V4L2_CID_BASE + 32 -V4L2_CID_BAND_STOP_FILTER = V4L2_CID_BASE + 33 - -V4L2_CID_ROTATE = V4L2_CID_BASE + 34 -V4L2_CID_BG_COLOR = V4L2_CID_BASE + 35 -V4L2_CID_LASTP1 = V4L2_CID_BASE + 36 - -V4L2_CID_MPEG_BASE = V4L2_CTRL_CLASS_MPEG | 0x900 -V4L2_CID_MPEG_CLASS = V4L2_CTRL_CLASS_MPEG | 1 - -# MPEG streams -V4L2_CID_MPEG_STREAM_TYPE = V4L2_CID_MPEG_BASE + 0 - -v4l2_mpeg_stream_type = enum -( - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_TS, - V4L2_MPEG_STREAM_TYPE_MPEG1_SS, - V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, - V4L2_MPEG_STREAM_TYPE_MPEG1_VCD, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, -) = range(6) - -V4L2_CID_MPEG_STREAM_PID_PMT = V4L2_CID_MPEG_BASE + 1 -V4L2_CID_MPEG_STREAM_PID_AUDIO = V4L2_CID_MPEG_BASE + 2 -V4L2_CID_MPEG_STREAM_PID_VIDEO = V4L2_CID_MPEG_BASE + 3 -V4L2_CID_MPEG_STREAM_PID_PCR = V4L2_CID_MPEG_BASE + 4 -V4L2_CID_MPEG_STREAM_PES_ID_AUDIO = V4L2_CID_MPEG_BASE + 5 -V4L2_CID_MPEG_STREAM_PES_ID_VIDEO = V4L2_CID_MPEG_BASE + 6 -V4L2_CID_MPEG_STREAM_VBI_FMT = V4L2_CID_MPEG_BASE + 7 - -v4l2_mpeg_stream_vbi_fmt = enum -( - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, -) = range(2) - -V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ = V4L2_CID_MPEG_BASE + 100 - -v4l2_mpeg_audio_sampling_freq = enum -( - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, -) = range(3) - -V4L2_CID_MPEG_AUDIO_ENCODING = V4L2_CID_MPEG_BASE + 101 - -v4l2_mpeg_audio_encoding = enum -( - V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_3, - V4L2_MPEG_AUDIO_ENCODING_AAC, - V4L2_MPEG_AUDIO_ENCODING_AC3, -) = range(5) - -V4L2_CID_MPEG_AUDIO_L1_BITRATE = V4L2_CID_MPEG_BASE + 102 - -v4l2_mpeg_audio_l1_bitrate = enum -( - V4L2_MPEG_AUDIO_L1_BITRATE_32K, - V4L2_MPEG_AUDIO_L1_BITRATE_64K, - V4L2_MPEG_AUDIO_L1_BITRATE_96K, - V4L2_MPEG_AUDIO_L1_BITRATE_128K, - V4L2_MPEG_AUDIO_L1_BITRATE_160K, - V4L2_MPEG_AUDIO_L1_BITRATE_192K, - V4L2_MPEG_AUDIO_L1_BITRATE_224K, - V4L2_MPEG_AUDIO_L1_BITRATE_256K, - V4L2_MPEG_AUDIO_L1_BITRATE_288K, - V4L2_MPEG_AUDIO_L1_BITRATE_320K, - V4L2_MPEG_AUDIO_L1_BITRATE_352K, - V4L2_MPEG_AUDIO_L1_BITRATE_384K, - V4L2_MPEG_AUDIO_L1_BITRATE_416K, - V4L2_MPEG_AUDIO_L1_BITRATE_448K, -) = range(14) - -V4L2_CID_MPEG_AUDIO_L2_BITRATE = V4L2_CID_MPEG_BASE + 103 - -v4l2_mpeg_audio_l2_bitrate = enum -( - V4L2_MPEG_AUDIO_L2_BITRATE_32K, - V4L2_MPEG_AUDIO_L2_BITRATE_48K, - V4L2_MPEG_AUDIO_L2_BITRATE_56K, - V4L2_MPEG_AUDIO_L2_BITRATE_64K, - V4L2_MPEG_AUDIO_L2_BITRATE_80K, - V4L2_MPEG_AUDIO_L2_BITRATE_96K, - V4L2_MPEG_AUDIO_L2_BITRATE_112K, - V4L2_MPEG_AUDIO_L2_BITRATE_128K, - V4L2_MPEG_AUDIO_L2_BITRATE_160K, - V4L2_MPEG_AUDIO_L2_BITRATE_192K, - V4L2_MPEG_AUDIO_L2_BITRATE_224K, - V4L2_MPEG_AUDIO_L2_BITRATE_256K, - V4L2_MPEG_AUDIO_L2_BITRATE_320K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, -) = range(14) - -V4L2_CID_MPEG_AUDIO_L3_BITRATE = V4L2_CID_MPEG_BASE + 104 - -v4l2_mpeg_audio_l3_bitrate = enum -( - V4L2_MPEG_AUDIO_L3_BITRATE_32K, - V4L2_MPEG_AUDIO_L3_BITRATE_40K, - V4L2_MPEG_AUDIO_L3_BITRATE_48K, - V4L2_MPEG_AUDIO_L3_BITRATE_56K, - V4L2_MPEG_AUDIO_L3_BITRATE_64K, - V4L2_MPEG_AUDIO_L3_BITRATE_80K, - V4L2_MPEG_AUDIO_L3_BITRATE_96K, - V4L2_MPEG_AUDIO_L3_BITRATE_112K, - V4L2_MPEG_AUDIO_L3_BITRATE_128K, - V4L2_MPEG_AUDIO_L3_BITRATE_160K, - V4L2_MPEG_AUDIO_L3_BITRATE_192K, - V4L2_MPEG_AUDIO_L3_BITRATE_224K, - V4L2_MPEG_AUDIO_L3_BITRATE_256K, - V4L2_MPEG_AUDIO_L3_BITRATE_320K, -) = range(14) - -V4L2_CID_MPEG_AUDIO_MODE = V4L2_CID_MPEG_BASE + 105 - -v4l2_mpeg_audio_mode = enum -( - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_JOINT_STEREO, - V4L2_MPEG_AUDIO_MODE_DUAL, - V4L2_MPEG_AUDIO_MODE_MONO, -) = range(4) - -V4L2_CID_MPEG_AUDIO_MODE_EXTENSION = V4L2_CID_MPEG_BASE + 106 - -v4l2_mpeg_audio_mode_extension = enum -( - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, -) = range(4) - -V4L2_CID_MPEG_AUDIO_EMPHASIS = V4L2_CID_MPEG_BASE + 107 - -v4l2_mpeg_audio_emphasis = enum -( - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, -) = range(3) - -V4L2_CID_MPEG_AUDIO_CRC = V4L2_CID_MPEG_BASE + 108 - -v4l2_mpeg_audio_crc = enum -( - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, -) = range(2) - -V4L2_CID_MPEG_AUDIO_MUTE = V4L2_CID_MPEG_BASE + 109 -V4L2_CID_MPEG_AUDIO_AAC_BITRATE = V4L2_CID_MPEG_BASE + 110 -V4L2_CID_MPEG_AUDIO_AC3_BITRATE = V4L2_CID_MPEG_BASE + 111 - -v4l2_mpeg_audio_ac3_bitrate = enum -( - V4L2_MPEG_AUDIO_AC3_BITRATE_32K, - V4L2_MPEG_AUDIO_AC3_BITRATE_40K, - V4L2_MPEG_AUDIO_AC3_BITRATE_48K, - V4L2_MPEG_AUDIO_AC3_BITRATE_56K, - V4L2_MPEG_AUDIO_AC3_BITRATE_64K, - V4L2_MPEG_AUDIO_AC3_BITRATE_80K, - V4L2_MPEG_AUDIO_AC3_BITRATE_96K, - V4L2_MPEG_AUDIO_AC3_BITRATE_112K, - V4L2_MPEG_AUDIO_AC3_BITRATE_128K, - V4L2_MPEG_AUDIO_AC3_BITRATE_160K, - V4L2_MPEG_AUDIO_AC3_BITRATE_192K, - V4L2_MPEG_AUDIO_AC3_BITRATE_224K, - V4L2_MPEG_AUDIO_AC3_BITRATE_256K, - V4L2_MPEG_AUDIO_AC3_BITRATE_320K, - V4L2_MPEG_AUDIO_AC3_BITRATE_384K, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, - V4L2_MPEG_AUDIO_AC3_BITRATE_512K, - V4L2_MPEG_AUDIO_AC3_BITRATE_576K, - V4L2_MPEG_AUDIO_AC3_BITRATE_640K, -) = range(19) - -V4L2_CID_MPEG_VIDEO_ENCODING = V4L2_CID_MPEG_BASE + 200 - -v4l2_mpeg_video_encoding = enum -( - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, -) = range(3) - -V4L2_CID_MPEG_VIDEO_ASPECT = V4L2_CID_MPEG_BASE + 201 - -v4l2_mpeg_video_aspect = enum -( - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_4x3, - V4L2_MPEG_VIDEO_ASPECT_16x9, - V4L2_MPEG_VIDEO_ASPECT_221x100, -) = range(4) - -V4L2_CID_MPEG_VIDEO_B_FRAMES = V4L2_CID_MPEG_BASE + 202 -V4L2_CID_MPEG_VIDEO_GOP_SIZE = V4L2_CID_MPEG_BASE + 203 -V4L2_CID_MPEG_VIDEO_GOP_CLOSURE = V4L2_CID_MPEG_BASE + 204 -V4L2_CID_MPEG_VIDEO_PULLDOWN = V4L2_CID_MPEG_BASE + 205 -V4L2_CID_MPEG_VIDEO_BITRATE_MODE = V4L2_CID_MPEG_BASE + 206 - -v4l2_mpeg_video_bitrate_mode = enum -( - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, -) = range(2) - -V4L2_CID_MPEG_VIDEO_BITRATE = V4L2_CID_MPEG_BASE + 207 -V4L2_CID_MPEG_VIDEO_BITRATE_PEAK = V4L2_CID_MPEG_BASE + 208 -V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION = V4L2_CID_MPEG_BASE + 209 -V4L2_CID_MPEG_VIDEO_MUTE = V4L2_CID_MPEG_BASE + 210 -V4L2_CID_MPEG_VIDEO_MUTE_YUV = V4L2_CID_MPEG_BASE + 211 - -V4L2_CID_MPEG_CX2341X_BASE = V4L2_CTRL_CLASS_MPEG | 0x1000 -V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 0 - -v4l2_mpeg_cx2341x_video_spatial_filter_mode = enum -( - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, -) = range(2) - -V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 1 -V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 2 - -v4l2_mpeg_cx2341x_video_luma_spatial_filter_type = enum -( - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, -) = range(5) - -V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 3 - -v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type = enum -( - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, -) = range(2) - -V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 4 - -v4l2_mpeg_cx2341x_video_temporal_filter_mode = enum -( - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, -) = range(2) - -V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 5 -V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 6 - -v4l2_mpeg_cx2341x_video_median_filter_type = enum -( - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, -) = range(5) - -V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 7 -V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 8 -V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 9 -V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 10 -V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS = V4L2_CID_MPEG_CX2341X_BASE + 11 - -V4L2_CID_CAMERA_CLASS_BASE = V4L2_CTRL_CLASS_CAMERA | 0x900 -V4L2_CID_CAMERA_CLASS = V4L2_CTRL_CLASS_CAMERA | 1 - -V4L2_CID_EXPOSURE_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 1 - -v4l2_exposure_auto_type = enum -( - V4L2_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, - V4L2_EXPOSURE_SHUTTER_PRIORITY, - V4L2_EXPOSURE_APERTURE_PRIORITY, -) = range(4) - -V4L2_CID_EXPOSURE_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 2 -V4L2_CID_EXPOSURE_AUTO_PRIORITY = V4L2_CID_CAMERA_CLASS_BASE + 3 - -V4L2_CID_PAN_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 4 -V4L2_CID_TILT_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 5 -V4L2_CID_PAN_RESET = V4L2_CID_CAMERA_CLASS_BASE + 6 -V4L2_CID_TILT_RESET = V4L2_CID_CAMERA_CLASS_BASE + 7 - -V4L2_CID_PAN_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 8 -V4L2_CID_TILT_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 9 - -V4L2_CID_FOCUS_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 10 -V4L2_CID_FOCUS_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 11 -V4L2_CID_FOCUS_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 12 - -V4L2_CID_ZOOM_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 13 -V4L2_CID_ZOOM_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 14 -V4L2_CID_ZOOM_CONTINUOUS = V4L2_CID_CAMERA_CLASS_BASE + 15 - -V4L2_CID_PRIVACY = V4L2_CID_CAMERA_CLASS_BASE + 16 - -V4L2_CID_FM_TX_CLASS_BASE = V4L2_CTRL_CLASS_FM_TX | 0x900 -V4L2_CID_FM_TX_CLASS = V4L2_CTRL_CLASS_FM_TX | 1 - -V4L2_CID_RDS_TX_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 1 -V4L2_CID_RDS_TX_PI = V4L2_CID_FM_TX_CLASS_BASE + 2 -V4L2_CID_RDS_TX_PTY = V4L2_CID_FM_TX_CLASS_BASE + 3 -V4L2_CID_RDS_TX_PS_NAME = V4L2_CID_FM_TX_CLASS_BASE + 5 -V4L2_CID_RDS_TX_RADIO_TEXT = V4L2_CID_FM_TX_CLASS_BASE + 6 - -V4L2_CID_AUDIO_LIMITER_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 64 -V4L2_CID_AUDIO_LIMITER_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 65 -V4L2_CID_AUDIO_LIMITER_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 66 - -V4L2_CID_AUDIO_COMPRESSION_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 80 -V4L2_CID_AUDIO_COMPRESSION_GAIN = V4L2_CID_FM_TX_CLASS_BASE + 81 -V4L2_CID_AUDIO_COMPRESSION_THRESHOLD = V4L2_CID_FM_TX_CLASS_BASE + 82 -V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME = V4L2_CID_FM_TX_CLASS_BASE + 83 -V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 84 - -V4L2_CID_PILOT_TONE_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 96 -V4L2_CID_PILOT_TONE_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 97 -V4L2_CID_PILOT_TONE_FREQUENCY = V4L2_CID_FM_TX_CLASS_BASE + 98 - -V4L2_CID_TUNE_PREEMPHASIS = V4L2_CID_FM_TX_CLASS_BASE + 112 - -v4l2_preemphasis = enum -( - V4L2_PREEMPHASIS_DISABLED, - V4L2_PREEMPHASIS_50_uS, - V4L2_PREEMPHASIS_75_uS, -) = range(3) - -V4L2_CID_TUNE_POWER_LEVEL = V4L2_CID_FM_TX_CLASS_BASE + 113 -V4L2_CID_TUNE_ANTENNA_CAPACITOR = V4L2_CID_FM_TX_CLASS_BASE + 114 - - -# -# Tuning -# - -class v4l2_tuner(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('type', v4l2_tuner_type), - ('capability', ctypes.c_uint32), - ('rangelow', ctypes.c_uint32), - ('rangehigh', ctypes.c_uint32), - ('rxsubchans', ctypes.c_uint32), - ('audmode', ctypes.c_uint32), - ('signal', ctypes.c_int32), - ('afc', ctypes.c_int32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -class v4l2_modulator(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('capability', ctypes.c_uint32), - ('rangelow', ctypes.c_uint32), - ('rangehigh', ctypes.c_uint32), - ('txsubchans', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -V4L2_TUNER_CAP_LOW = 0x0001 -V4L2_TUNER_CAP_NORM = 0x0002 -V4L2_TUNER_CAP_STEREO = 0x0010 -V4L2_TUNER_CAP_LANG2 = 0x0020 -V4L2_TUNER_CAP_SAP = 0x0020 -V4L2_TUNER_CAP_LANG1 = 0x0040 -V4L2_TUNER_CAP_RDS = 0x0080 - -V4L2_TUNER_SUB_MONO = 0x0001 -V4L2_TUNER_SUB_STEREO = 0x0002 -V4L2_TUNER_SUB_LANG2 = 0x0004 -V4L2_TUNER_SUB_SAP = 0x0004 -V4L2_TUNER_SUB_LANG1 = 0x0008 -V4L2_TUNER_SUB_RDS = 0x0010 - -V4L2_TUNER_MODE_MONO = 0x0000 -V4L2_TUNER_MODE_STEREO = 0x0001 -V4L2_TUNER_MODE_LANG2 = 0x0002 -V4L2_TUNER_MODE_SAP = 0x0002 -V4L2_TUNER_MODE_LANG1 = 0x0003 -V4L2_TUNER_MODE_LANG1_LANG2 = 0x0004 - - -class v4l2_frequency(ctypes.Structure): - _fields_ = [ - ('tuner', ctypes.c_uint32), - ('type', v4l2_tuner_type), - ('frequency', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 8), - ] - - -class v4l2_hw_freq_seek(ctypes.Structure): - _fields_ = [ - ('tuner', ctypes.c_uint32), - ('type', v4l2_tuner_type), - ('seek_upward', ctypes.c_uint32), - ('wrap_around', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 8), - ] - - -# -# RDS -# - -class v4l2_rds_data(ctypes.Structure): - _fields_ = [ - ('lsb', ctypes.c_char), - ('msb', ctypes.c_char), - ('block', ctypes.c_char), - ] - - _pack_ = True - - -V4L2_RDS_BLOCK_MSK = 0x7 -V4L2_RDS_BLOCK_A = 0 -V4L2_RDS_BLOCK_B = 1 -V4L2_RDS_BLOCK_C = 2 -V4L2_RDS_BLOCK_D = 3 -V4L2_RDS_BLOCK_C_ALT = 4 -V4L2_RDS_BLOCK_INVALID = 7 - -V4L2_RDS_BLOCK_CORRECTED = 0x40 -V4L2_RDS_BLOCK_ERROR = 0x80 - - -# -# Audio -# - -class v4l2_audio(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('capability', ctypes.c_uint32), - ('mode', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -V4L2_AUDCAP_STEREO = 0x00001 -V4L2_AUDCAP_AVL = 0x00002 - -V4L2_AUDMODE_AVL = 0x00001 - - -class v4l2_audioout(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ('capability', ctypes.c_uint32), - ('mode', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -# -# Mpeg services (experimental) -# - -V4L2_ENC_IDX_FRAME_I = 0 -V4L2_ENC_IDX_FRAME_P = 1 -V4L2_ENC_IDX_FRAME_B = 2 -V4L2_ENC_IDX_FRAME_MASK = 0xf - - -class v4l2_enc_idx_entry(ctypes.Structure): - _fields_ = [ - ('offset', ctypes.c_uint64), - ('pts', ctypes.c_uint64), - ('length', ctypes.c_uint32), - ('flags', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -V4L2_ENC_IDX_ENTRIES = 64 - - -class v4l2_enc_idx(ctypes.Structure): - _fields_ = [ - ('entries', ctypes.c_uint32), - ('entries_cap', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ('entry', v4l2_enc_idx_entry * V4L2_ENC_IDX_ENTRIES), - ] - - -V4L2_ENC_CMD_START = 0 -V4L2_ENC_CMD_STOP = 1 -V4L2_ENC_CMD_PAUSE = 2 -V4L2_ENC_CMD_RESUME = 3 - -V4L2_ENC_CMD_STOP_AT_GOP_END = 1 << 0 - - -class v4l2_encoder_cmd(ctypes.Structure): - class _u(ctypes.Union): - class _s(ctypes.Structure): - _fields_ = [ - ('data', ctypes.c_uint32 * 8), - ] - - _fields_ = [ - ('raw', _s), - ] - - _fields_ = [ - ('cmd', ctypes.c_uint32), - ('flags', ctypes.c_uint32), - ('_u', _u), - ] - - _anonymous_ = ('_u',) - - -# -# Data services (VBI) -# - -class v4l2_vbi_format(ctypes.Structure): - _fields_ = [ - ('sampling_rate', ctypes.c_uint32), - ('offset', ctypes.c_uint32), - ('samples_per_line', ctypes.c_uint32), - ('sample_format', ctypes.c_uint32), - ('start', ctypes.c_int32 * 2), - ('count', ctypes.c_uint32 * 2), - ('flags', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -V4L2_VBI_UNSYNC = 1 << 0 -V4L2_VBI_INTERLACED = 1 << 1 - - -class v4l2_sliced_vbi_format(ctypes.Structure): - _fields_ = [ - ('service_set', ctypes.c_uint16), - ('service_lines', ctypes.c_uint16 * 2 * 24), - ('io_size', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 2), - ] - - -V4L2_SLICED_TELETEXT_B = 0x0001 -V4L2_SLICED_VPS = 0x0400 -V4L2_SLICED_CAPTION_525 = 0x1000 -V4L2_SLICED_WSS_625 = 0x4000 -V4L2_SLICED_VBI_525 = V4L2_SLICED_CAPTION_525 -V4L2_SLICED_VBI_625 = ( - V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625) - - -class v4l2_sliced_vbi_cap(ctypes.Structure): - _fields_ = [ - ('service_set', ctypes.c_uint16), - ('service_lines', ctypes.c_uint16 * 2 * 24), - ('type', v4l2_buf_type), - ('reserved', ctypes.c_uint32 * 3), - ] - - -class v4l2_sliced_vbi_data(ctypes.Structure): - _fields_ = [ - ('id', ctypes.c_uint32), - ('field', ctypes.c_uint32), - ('line', ctypes.c_uint32), - ('reserved', ctypes.c_uint32), - ('data', ctypes.c_char * 48), - ] - - -# -# Sliced VBI data inserted into MPEG Streams -# - - -V4L2_MPEG_VBI_IVTV_TELETEXT_B = 1 -V4L2_MPEG_VBI_IVTV_CAPTION_525 = 4 -V4L2_MPEG_VBI_IVTV_WSS_625 = 5 -V4L2_MPEG_VBI_IVTV_VPS = 7 - - -class v4l2_mpeg_vbi_itv0_line(ctypes.Structure): - _fields_ = [ - ('id', ctypes.c_char), - ('data', ctypes.c_char * 42), - ] - - _pack_ = True - - -class v4l2_mpeg_vbi_itv0(ctypes.Structure): - _fields_ = [ - ('linemask', ctypes.c_uint32 * 2), # how to define __le32 in ctypes? - ('line', v4l2_mpeg_vbi_itv0_line * 35), - ] - - _pack_ = True - - -class v4l2_mpeg_vbi_ITV0(ctypes.Structure): - _fields_ = [ - ('line', v4l2_mpeg_vbi_itv0_line * 36), - ] - - _pack_ = True - - -V4L2_MPEG_VBI_IVTV_MAGIC0 = "itv0" -V4L2_MPEG_VBI_IVTV_MAGIC1 = "ITV0" - - -class v4l2_mpeg_vbi_fmt_ivtv(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('itv0', v4l2_mpeg_vbi_itv0), - ('ITV0', v4l2_mpeg_vbi_ITV0), - ] - - _fields_ = [ - ('magic', ctypes.c_char * 4), - ('_u', _u) - ] - - _anonymous_ = ('_u',) - _pack_ = True - - -# -# Aggregate structures -# - -class v4l2_format(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('pix', v4l2_pix_format), - ('win', v4l2_window), - ('vbi', v4l2_vbi_format), - ('sliced', v4l2_sliced_vbi_format), - ('raw_data', ctypes.c_char * 200), - ] - - _fields_ = [ - ('type', v4l2_buf_type), - ('fmt', _u), - ] - - -class v4l2_streamparm(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('capture', v4l2_captureparm), - ('output', v4l2_outputparm), - ('raw_data', ctypes.c_char * 200), - ] - - _fields_ = [ - ('type', v4l2_buf_type), - ('parm', _u) - ] - - -# -# Advanced debugging -# - -V4L2_CHIP_MATCH_HOST = 0 -V4L2_CHIP_MATCH_I2C_DRIVER = 1 -V4L2_CHIP_MATCH_I2C_ADDR = 2 -V4L2_CHIP_MATCH_AC97 = 3 - - -class v4l2_dbg_match(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('addr', ctypes.c_uint32), - ('name', ctypes.c_char * 32), - ] - - _fields_ = [ - ('type', ctypes.c_uint32), - ('_u', _u), - ] - - _anonymous_ = ('_u',) - _pack_ = True - - -class v4l2_dbg_register(ctypes.Structure): - _fields_ = [ - ('match', v4l2_dbg_match), - ('size', ctypes.c_uint32), - ('reg', ctypes.c_uint64), - ('val', ctypes.c_uint64), - ] - - _pack_ = True - - -class v4l2_dbg_chip_ident(ctypes.Structure): - _fields_ = [ - ('match', v4l2_dbg_match), - ('ident', ctypes.c_uint32), - ('revision', ctypes.c_uint32), - ] - - _pack_ = True - - -# -# ioctl codes for video devices -# - -VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability) -VIDIOC_RESERVED = _IO('V', 1) -VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc) -VIDIOC_G_FMT = _IOWR('V', 4, v4l2_format) -VIDIOC_S_FMT = _IOWR('V', 5, v4l2_format) -VIDIOC_REQBUFS = _IOWR('V', 8, v4l2_requestbuffers) -VIDIOC_QUERYBUF = _IOWR('V', 9, v4l2_buffer) -VIDIOC_G_FBUF = _IOR('V', 10, v4l2_framebuffer) -VIDIOC_S_FBUF = _IOW('V', 11, v4l2_framebuffer) -VIDIOC_OVERLAY = _IOW('V', 14, ctypes.c_int) -VIDIOC_QBUF = _IOWR('V', 15, v4l2_buffer) -VIDIOC_DQBUF = _IOWR('V', 17, v4l2_buffer) -VIDIOC_STREAMON = _IOW('V', 18, ctypes.c_int) -VIDIOC_STREAMOFF = _IOW('V', 19, ctypes.c_int) -VIDIOC_G_PARM = _IOWR('V', 21, v4l2_streamparm) -VIDIOC_S_PARM = _IOWR('V', 22, v4l2_streamparm) -VIDIOC_G_STD = _IOR('V', 23, v4l2_std_id) -VIDIOC_S_STD = _IOW('V', 24, v4l2_std_id) -VIDIOC_ENUMSTD = _IOWR('V', 25, v4l2_standard) -VIDIOC_ENUMINPUT = _IOWR('V', 26, v4l2_input) -VIDIOC_G_CTRL = _IOWR('V', 27, v4l2_control) -VIDIOC_S_CTRL = _IOWR('V', 28, v4l2_control) -VIDIOC_G_TUNER = _IOWR('V', 29, v4l2_tuner) -VIDIOC_S_TUNER = _IOW('V', 30, v4l2_tuner) -VIDIOC_G_AUDIO = _IOR('V', 33, v4l2_audio) -VIDIOC_S_AUDIO = _IOW('V', 34, v4l2_audio) -VIDIOC_QUERYCTRL = _IOWR('V', 36, v4l2_queryctrl) -VIDIOC_QUERYMENU = _IOWR('V', 37, v4l2_querymenu) -VIDIOC_G_INPUT = _IOR('V', 38, ctypes.c_int) -VIDIOC_S_INPUT = _IOWR('V', 39, ctypes.c_int) -VIDIOC_G_OUTPUT = _IOR('V', 46, ctypes.c_int) -VIDIOC_S_OUTPUT = _IOWR('V', 47, ctypes.c_int) -VIDIOC_ENUMOUTPUT = _IOWR('V', 48, v4l2_output) -VIDIOC_G_AUDOUT = _IOR('V', 49, v4l2_audioout) -VIDIOC_S_AUDOUT = _IOW('V', 50, v4l2_audioout) -VIDIOC_G_MODULATOR = _IOWR('V', 54, v4l2_modulator) -VIDIOC_S_MODULATOR = _IOW('V', 55, v4l2_modulator) -VIDIOC_G_FREQUENCY = _IOWR('V', 56, v4l2_frequency) -VIDIOC_S_FREQUENCY = _IOW('V', 57, v4l2_frequency) -VIDIOC_CROPCAP = _IOWR('V', 58, v4l2_cropcap) -VIDIOC_G_CROP = _IOWR('V', 59, v4l2_crop) -VIDIOC_S_CROP = _IOW('V', 60, v4l2_crop) -VIDIOC_G_JPEGCOMP = _IOR('V', 61, v4l2_jpegcompression) -VIDIOC_S_JPEGCOMP = _IOW('V', 62, v4l2_jpegcompression) -VIDIOC_QUERYSTD = _IOR('V', 63, v4l2_std_id) -VIDIOC_TRY_FMT = _IOWR('V', 64, v4l2_format) -VIDIOC_ENUMAUDIO = _IOWR('V', 65, v4l2_audio) -VIDIOC_ENUMAUDOUT = _IOWR('V', 66, v4l2_audioout) -VIDIOC_G_PRIORITY = _IOR('V', 67, v4l2_priority) -VIDIOC_S_PRIORITY = _IOW('V', 68, v4l2_priority) -VIDIOC_G_SLICED_VBI_CAP = _IOWR('V', 69, v4l2_sliced_vbi_cap) -VIDIOC_LOG_STATUS = _IO('V', 70) -VIDIOC_G_EXT_CTRLS = _IOWR('V', 71, v4l2_ext_controls) -VIDIOC_S_EXT_CTRLS = _IOWR('V', 72, v4l2_ext_controls) -VIDIOC_TRY_EXT_CTRLS = _IOWR('V', 73, v4l2_ext_controls) - -VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum) -VIDIOC_ENUM_FRAMEINTERVALS = _IOWR('V', 75, v4l2_frmivalenum) -VIDIOC_G_ENC_INDEX = _IOR('V', 76, v4l2_enc_idx) -VIDIOC_ENCODER_CMD = _IOWR('V', 77, v4l2_encoder_cmd) -VIDIOC_TRY_ENCODER_CMD = _IOWR('V', 78, v4l2_encoder_cmd) - -VIDIOC_DBG_S_REGISTER = _IOW('V', 79, v4l2_dbg_register) -VIDIOC_DBG_G_REGISTER = _IOWR('V', 80, v4l2_dbg_register) - -VIDIOC_DBG_G_CHIP_IDENT = _IOWR('V', 81, v4l2_dbg_chip_ident) - -VIDIOC_S_HW_FREQ_SEEK = _IOW('V', 82, v4l2_hw_freq_seek) -VIDIOC_ENUM_DV_PRESETS = _IOWR('V', 83, v4l2_dv_enum_preset) -VIDIOC_S_DV_PRESET = _IOWR('V', 84, v4l2_dv_preset) -VIDIOC_G_DV_PRESET = _IOWR('V', 85, v4l2_dv_preset) -VIDIOC_QUERY_DV_PRESET = _IOR('V', 86, v4l2_dv_preset) -VIDIOC_S_DV_TIMINGS = _IOWR('V', 87, v4l2_dv_timings) -VIDIOC_G_DV_TIMINGS = _IOWR('V', 88, v4l2_dv_timings) - -VIDIOC_OVERLAY_OLD = _IOWR('V', 14, ctypes.c_int) -VIDIOC_S_PARM_OLD = _IOW('V', 22, v4l2_streamparm) -VIDIOC_S_CTRL_OLD = _IOW('V', 28, v4l2_control) -VIDIOC_G_AUDIO_OLD = _IOWR('V', 33, v4l2_audio) -VIDIOC_G_AUDOUT_OLD = _IOWR('V', 49, v4l2_audioout) -VIDIOC_CROPCAP_OLD = _IOR('V', 58, v4l2_cropcap) - -BASE_VIDIOC_PRIVATE = 192 |