Tuesday, December 30, 2014

EasyGUI_Qt progressing well.

A short while ago, I started working on an adaptation of EasyGUI for PyQt.  This new project is called EasyGUI_Qt.  For those that are not familiar with EasyGUI, the idea is to provide a series of graphical "widgets" that can be used in procedural programs.  For example, suppose you have a program where you query a user with Python's input:

    >>> input("Enter your reponse " )

Normally, you'd see a copy of the prompt appear, waiting for the user's response.  Using EasyGUI_Qt's  analogue of input(), this is what would show instead of a prompt:




There is also a demo launcher which can be used to see all of the available widgets:




A complete description of the existing widgets is available.  EasyGUI_Qt will not have all the same widgets available as the original EasyGUI.  While the current version number is 0.4, it is, in fact, nearly complete.  I have written a roadmap indicating what's left to be done until the 1.0 release. Anyone should feel free to make comments about the proposed roadmap including making suggestions for additional widgets (with justification provided).

EasyGUI_Qt targets Python 3.3 (and PyQt4) but should support (except for some pesky unicode issues) Python 2.7 - following some requests by users.


Saturday, December 20, 2014

Review of IPython Notebook Essentials

Disclaimer: following a post on Google+ by a Packt representative, I volunteered to do a review of IPython Notebook Essentials and got a free copy as an ebook (I used the pdf format).

The verdict: Not recommended in its current state.

IPython Notebook Essentials starts very well.  It suggests to use the Anaconda 3.4 distribution (which I have on my computer - more on that later) or connecting online using Wakari.  Both methods worked fine for simple examples.

In chapter 1, we are given a quick tour of what the IPython notebook can do using the modeling of a cup of coffee as an example. The idea, on its own works well.  [As a scientist, I found the use of degree Farenheit a bit odd and reminded me of books I read that were written 50 or more years ago.]  However, while the authors used variables that follow the normal Python convention, separating names by underscore as in temp_cream, the code formatting is at times atrocious as variable names are sometimes split up after an underscore character as in the following two lines of code:

initial_temp_mix_at_shop = temp_mixture(temp_coffee, vol_coffee, temp_
cream, vol_cream)

which are, ironically enough, in bold in the text as the author wants us to focus our attention on their meaning (but no mention of the error in formatting).

While I usually prefer holding a paper book over reading an ebook on my screen, I must admit that being able to copy-paste code samples beats having to retype everything.  So, it was possible to quickly reproduce the examples by copy-pasting and fixing the typos to run the examples.

However, a much better way exists which is often used by Packt books: making code samples available for download.  The IPython notebooks have been designed for easy sharing.  Here, the author has chosen not to make use of this.  This, in my opinion, is something that is a major flaw in this case.


Chapter 2 covers in more details the notebook interface. This is undoubtably the most important chapter of the book for anyone who wishes to learn about the IPython notebook.  It covers a lot of grounds.

However, I encountered many problems, some more serious than others. The first, which is perhaps more an annoyance than a real problem, is that one example intended to show code completion using the tab key is given as follows:

print am

Python programmers will immediately recognize this as being a Python 2 example.  However, as far as I could tell (using ctrl-f "python 2") there isn't a single mention anywhere that Python 2 is used.
I happened to have the Anaconda 3.4 distribution installed (as recommended) but with Python 3.4 and not Python 2.  Python 3 is now 6 years old and there is no excuse to focus on an old, and soon to be obsolete, version of Python without at least mentioning why that choice was made. Minor syntax difference, like adding parentheses for print statements, are easily fixed; some more subtle ones are not.   Furthermore, while I had the Anaconda distribution installed, I was still using the online Wakari site to work through the examples, so that was not a major problem at that point.

While still in chapter 2,  we are invited to replace "%pylab inline" by "%pylab" and run an example again to see a plot appear in a window (I first assumed a separate browser window) instead of being shown in the document.    This does not work using the online Wakari site: the window is actually a Qt window, not something that works in a browser.  So, to try this example, I had to start my local version and recopy the code, which worked correctly.

Shortly thereafter, we are introduced to more "magic" methods and invited to try running a cython example, loading from a local file. This did not work.  The recommended "%%cython" magic method is no longer working in the latest IPython version included with the Python 3.4 Anaconda 3.4 distribution.  After a while, I found the proper way to run cython code with the latest version BUT the example provided raised a (numpy-related?) syntax error.  I copy-pasted the code from my browser to the Wakari online version and it worked correctly, confirming that I had not made an error in reproducing the code given by the author. However, I was not able to figure out the source of the error using the local version.

After finishing Chapter 2, I stopped trying to run every single examples and simply skimmed the book.

Chapter 3 focuses on producing plots with matplotlib, including animations. While not specific to the IPython notebook, this topic felt like an appropriate one to cover.

In Chapter 4, we learn about the pandas library which has very little to do with the IPython notebook.  The situation is similar with Chapter 5 which covers SciPy, Numba and NumbaPro, again three libraries that have very little to do with the notebook as such.  The choice of NumbaPro struck me as a bit odd since it is a commercial product.  True enough, it can be tried for free - however, it is not something that I would consider to be an "essential" for the IPython notebook.

I know very little more about the IPython notebook than what I have learned from this book. However, I do know that it is possible to write extensions for the IPython notebook which is something that I would have thought should be included in any book titled  "IPython Notebook Essentials", well before discussing specialized libraries such as Pandas, SciPy, Numba and NumbaPro.

There might very well be other topics more notebook specific that should be included, but I have no way to know from this book.

The book includes three appendices: a brief IPython notebook reference card, a brief review of Python, and an appendix on Numpy arrays.   Both the Reference card and the Numpy arrays appendices seem like worthwhile additions.  However, the brief review of Python seems a bit out of place.  By including code like:

def make_lorenz(sigma, r, b):
   def func(statevec, t):
      x, y, z = statevec
      return [ sigma * (y - x),
              r * x - y - x * z,
              x * y - b * z ]
   return func


in Chapter 2, the author seems to assume, and rightly so in my opinion, that the reader will be familiar with Python.  However, the appendix only covers the standard Python construct that one may find in a beginner's book intended for programmers that are familiar with other languages.  As such, the Python review appendix seems just like a filler, increasing the page count while adding nothing of value to the book. Thankfully, it is relegated to an appendix instead of being inserted in an early chapter.

In summary, about half of the book contains information of value for someone who wants to learn about the IPython notebook itself; the choice of Python 2 over Python 3 is odd, and almost inexcusable given that it is not even mentioned anywhere; the lack of downloable code samples" (mostly IPython notebooks in this case) greatly reduces the value of this book and is something that could be remedied by the author.  In fact, given the typos mentioned (where variable names are split over two lines), downloadable copies of notebooks should be made available.

As I write this review, Packt is having a sale during which ebooks are available for $5.  At that price, I would say that IPython Notebook Essentials is worth it if one wants to learn about the IPython Notebook; however, based on a quick scan of other Packt books covering the IPython notebook, I  believe that better choices exist from the same editor.

Tuesday, December 09, 2014

Reloadable user-defined library with Brython/Python


[Note: this post is a more detailed explanation of something that is briefly described in a previous post on supporting multiple human languages for Reeborg's World.]

In Reeborg's World, I want to have programmers (read: Python beginners who have never programmed before) to be able to learn about using libraries in Python.  As usual, I was looking at the simplest way to introduce the idea of libraries.  Since almost all of the programs that programmers write make use of their own definition for turning right:

def turn_right():
    turn_left()
    turn_left()
    turn_left()

it makes sense to have this "reusable" function be put in a library. So, instead of a single code editor, the code editor has two tabs: one for the basic program and one for the library. Initially, I only had a Javascript version of Reeborg's World, but I still wanted to introduce the concept of using libraries. So, when I first introduced support for using a library with Javascript, I cheated.  And I continued to cheat when I added Python support.  If the programmer wanted to use the functions (or anything else) defined in their library, I required them to insert the following statement in their program.

from my_lib import *

Before running the program using Brython's exec(), I would scan the code in the editor's program tab. If I found this general import statement, I would replace the line by the entire content of the editor's library tab and execute this modified source instead.   [Note: I have since removed the idea of using a library in the Javascript version since there is no natural syntax for importing a library using Javascript.]

However, this approach had two problems:
  1. It did not support the other ways to use an import statement in Python (see below for an example).
  2. It encouraged a bad practice of including everything, polluting the program's namespace.  I already had shown the idiom "from some_lib import *" when explaining how to have Reeborg understand instruction using other human languages (such as from reeborg_fr import * for the French version, or from reeborg_es import * for the Spanish version; other translations welcome! ;-)
I wanted to encourage good programming practice, such as using

from my_lib import turn_right

One problem I had is that Brython's import mechanism is based on retrieving files on the server using ajax calls.  This by itself might not be a problem ... except that I do not want to store anything created by users on the server: Reeborg's World is meant to be run entirely inside the browser, with no login required. (The content of the editor and library tabs are saved in the browser local storage so that they are available when the user comes back to the site using the same browser with local storage enabled.)

Another problem I had is that, once a module is imported, future import statements for that module make use of the cached version.  If the programmer modifies the code in their library (tab), the corresponding module needs to be reloaded.  I need for this to be done automatically, without the programmer having to do anything special.

One solution to these problems might have been to create a special importer class that could import code directly from the library tab and add it to sys.meta_path.  Then, after a program has been run, remove all traces of the imported module (user's library) so that the next time it is executed, the import takes place all over again.

I decided instead on a different approach.  I created a simple module, called my_lib.py  (and another one, biblio.py, for the French version) and put it in Brython's site package directory.  The content of that module is simply:

from reeborg_en import *

which ensures that all normal robot commands can be used in that module.  When Reeborg's World is first loaded, I import that module so that it is cached.  Then, whenever the programmer's code needs to be executed, instead of simply having exec(src) called, the following is called instead:

def generic_translate_python(src, lib, lang_import, highlight):
    ''' Translate Python code into Javascript and execute

        src: source code in editor
        lib: language specific lib (e.g. my_lib in English, biblio in French)
             already imported in html file
        lang_import: something like "from reeborg_en import *"
    '''
    # save initial state of lib
    initial_lib_dict = {}
    for key in lib.__dict__:
        initial_lib_dict[key] = lib.__dict__[key]

    exec(library.getValue(), lib.__dict__)
    exec(lang_import)
    if highlight:
        src = insert_highlight_info(src)
    exec(src)

    # remove added definitions
    new_keys = []
    for key in lib.__dict__:
        if key not in initial_lib_dict:
            new_keys.append(key)
        else:
            lib.__dict__[key] = initial_lib_dict[key]

    for key in new_keys:
        del lib.__dict__[key]


In the above, highlight refers to some pre-processing of the code which allow to show which line of the code is executed as illustrated in two previous blog posts.  library.getValue() is a method that returns the content of the library tab.

Friday, December 05, 2014

Still baffled by the Python 2/3 discussions

I'm ... baffled...

For the past few years, I've been focused mostly on doing my own things, and not really following what was happening in the "core" Python community.   Reading this post today by Brett Cannon about the "consensus" that has apparently emerged  by the language summit at PyCon 2014 about writing code compatible for both Python 2 and 3, I was reminded about the release of version 1.0 of Crunchy 

Crunchy 1.0 is compatible with Python 2.4, 2.5, 2.6 ... and 3.1. It is also compatible with Jython 2.5 modulo some bugs when trying to work with examples containing unicode strings.

That was in 2009.   At 2.1 MB (zipped), Crunchy was not exactly a small script...

Why has it taken so long for this to become the norm?....

Due to a lack of interest in Crunchy, I have essentially not developed it much further past that point, and it is almost certainly not compatible with newer versions of Python... 

Sunday, November 30, 2014

Step by step execution ... and reverse steps

A few years ago, Greg Wilson mentioned that, a useful feature when teaching students would be the ability of recording programs and have the possibility of playing them back one step at a time either in the forward or backward direction.   Actually, I am paraphrasing what Greg said or wrote, as I don't exactly remember the context: it could have been in a web post, a tweet, or the one time we met and when I gave him a brief demo of what Crunchy was capable of doing at the time.   While I can not say for sure what Greg said/wrote and when he did it, the idea stuck in my head as something that I should implement at some point in the future.

This idea is something that the Online Python Tutor, by Philip Guo, makes possible.

It is now possible to do this with Reeborg's World as well. :-)


Thursday, November 27, 2014

Practical Python and OpenCV: conclusion of the review

I own a lot of programming books and ebooks; with the exception of the Python Cookbook (1st and 2nd editions) and Code Complete, I don't think that I've come close to reading an entire book from cover to cover.  I write programs for fun, not for a living, and I almost never write reviews on my blog.  So why did I write one this time?

A while ago, I entered my email address to receive 10 emails meant as a crash course on OpenCV, using Python (of course), provided by Adrian Rosebrock.  The content of the emails and various posts they linked intrigued me. So, I decided to fork out some money and get the premium bundle which included an introductory book (reviewed in part 1), a Case Studies (partly reviewed in part 3), both of which include code samples and (as part of that package) free updates to future versions.  Included in the bundle was also a Ubuntu VirtualBox (reviewed in part 2) and a commitment by the author to respond quickly to emails - commitment that I have severely tested with no complaints.

As I mentioned, I program for fun, and I had fun going through the material covered in Practical Python and OpenCV.  I've also read through most of both books and tried a majority of the examples - something that is really rare for me.  On that basis alone, I thought it deserved a review.

Am I 100% satisfied with the Premium bundle I got with no idea about how it could be improved upon?  If you read the 3 previous parts, you know that the answer is no.  I have some slightly idiosynchratic tastes and tend to be blunt (I usually prefer to say"honest") in my assessments.

If I were 30 years younger, I might seriously consider getting into computer programming as a career and learn as much as I could about Machine Learning, Computer Vision and other such topics.  As a starting point, I would recommend to my younger self to go through the material covered in Practical Python and OpenCV, read the many interesting posts on Adrian Rosebrock's blog, as well as the Python tutorials on the OpenCV site itself.  I would probably recommend to my younger self to get just the Case Studies bundle (not including the Ubuntu VirtualBox): my younger self would have been too stubborn/self-reliant to feel like asking questions to the author and would have liked to install things on his computer in his own way.

My old self still feels the same way sometimes ...

Tuesday, November 25, 2014

Practical Python and OpenCV: a review (part 3)

In part 1, I did a brief review of the "Practical Python and OpenCV" ebook which I will refer to as Book 1.  As part of the bundle I purchased, there was another ebook entitled "Case Studies" (hereafter referred to as Book 2) covering such topics as Face Detection, Web Cam Detection, Object Tracking in Videos, Eye Tracking, Handwriting Recognition, Plant Classification and Building an Amazon.com Cover Search.

Each topic in Book 2 is presented as part of a story with different characters (Jeremy, a student; Laura, a bank software programmer; etc.).   I have often read about how framing topics within a story is a good way to keep the interest of a reader.  However, personally I tend to prefer getting right down to the topic at hand (show me the executive summary! ...I gather from discussion that this is a trait shared by others that had a job like my previous one) and so, these stories do not really really appeal to me, but I do recognize their creativity and the work that went into creating them.

As a test of what I have learned to do while reading these books, I thought I should combine various topics of both books into a single experiment which constitutes the bulk of this post.  All the image processing (other than the screen captures) was done using OpenCV.

I decided to start with a photo, taken a few years ago with my daughter while visiting Montreal.
This photo was too large to fit entirely on my computer screen; as a test, I used a slightly modified version of the resize.py script included in Book 1 so that I could see it entirely on my computer screen, as shown below on the left.

 Then, I combined samples from a few scripts from Book 1 (load_display_save.py, drawing.py, cropy.py) together with ideas from this OpenCV tutorial covering mouse control and callbacks: The idea was to take the (resized) full image, show a rectangular selection (with a blue rectangle) and the corresponding cropped image on a separate window as shown below.


As the mouse moves, the selection changes.  The code to do so is listed below. Note that, when the selection is saved, the rectangular outline on the original image is changed to a green colour (not shown here) so as to give feedback that the operation was completed.


'''Displays an image and a cropped selection {WIDTH} x {HEIGHT} in a second
   window. Use ESC to quit the program; press "s" to save the image.

   Note: This program is only meant to be used from the command line and
   not as an imported module.
'''

import argparse
import cv2
import copy

WIDTH = 640
HEIGHT = 480
SELECT_COLOUR = (255, 0, 0)  # blue
SAVE_COLOUR = (0, 255, 0)    # green
_drawing = False
_x = _y = 0
_original = _cropped = None
default_ouput = "cropped.jpg"


def init():
    '''Initializes display windows, images and paths
       init() is meant to be used only with script invoked with
       command line arguments'''
    ap = argparse.ArgumentParser(
                    description=__doc__.format(WIDTH=WIDTH, HEIGHT=HEIGHT))
    ap.add_argument("-i", "--image", required=True,
                    help="Path to the original image")
    ap.add_argument("-o", "--output", default=default_ouput,
                    help="Path to saved image (default: %s)"%default_ouput)
    args = vars(ap.parse_args())

    original = cv2.imread(args["image"])
    cv2.namedWindow('Original image')
    cv2.imshow('Original image', original)

    cropped = original[0:HEIGHT, 0:WIDTH]  # [y, x] instead of the usual [x, y]
    cv2.namedWindow('Cropped')
    cv2.imshow("Cropped", cropped)
    return args, original, cropped


def update(x, y, colour=SELECT_COLOUR):
    '''Displays original image with coloured rectangle indicating cropping area
       and updates the displayed cropped image'''
    global _x, _y, _original, _cropped
    _x, _y = x, y
    _cropped = _original[y:y+HEIGHT, x:x+WIDTH]

    cv2.imshow("Cropped", _cropped)
    img = copy.copy(_original)
    cv2.rectangle(img, (x, y), (x+WIDTH, y+HEIGHT), colour, 3)
    cv2.imshow('Original image', img)


def show_cropped(event, x, y, flags, param):
    '''Mouse callback function - updates position of mouse and determines
       if image display should be updated.'''
    global _drawing

    if event == cv2.EVENT_LBUTTONDOWN:
        _drawing = True
    elif event == cv2.EVENT_LBUTTONUP:
        _drawing = False

    if _drawing:
        update(x, y)


def main():
    '''Entry point'''
    global _original, _cropped
    args, _original, _cropped = init()

    cv2.setMouseCallback('Original image', show_cropped)

    while True:
        key = cv2.waitKey(1) & 0xFF  # 0xFF is for 64 bit computer
        if key == 27:  # escape
            break
        elif key == ord("s"):
            cv2.imwrite(args["output"], _cropped)
            update(_x, _y, colour=SAVE_COLOUR)

    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

Using this script, I was able to select and save a cropped version of the original image.

With the cropped image in hand, I was ready to do some further experimentation including face and eye detection as well as blurring faces.  I decided to combine all these features into a single program listed below.  While the code provided with Book 2 worked perfectly fine for feature detection [using the appropriate version of OpenCV...] and gave me the original idea, I decided instead to adapt the code from the OpenCV face detection tutorial as I found it simpler to use as a starting point for my purpose.  I also used what I had learned from Book 1 about blurring.

The following code, was put together quickly and uses hard-coded paths. Since incorrect paths given to classifiers generate no mistakes/exceptions, I included some assert statements to insure that I was using the correct files for reasons that you can probably guess...

import cv2
import os
import copy

face_classifiers = 'cascades/haarcascade_frontalface_default.xml'
eye_classifiers = 'cascades/haarcascade_eye.xml'

cwd = os.getcwd() + '/'
assert os.path.isfile(cwd + face_classifiers)
assert os.path.isfile(cwd + eye_classifiers)

face_cascade = cv2.CascadeClassifier(face_classifiers)
eye_cascade = cv2.CascadeClassifier(eye_classifiers)

original = cv2.imread('images/cropped.jpg')
cv2.namedWindow('Image')
cv2.imshow('Image', original)

gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
blue = (255, 0, 0)
green = (0, 255, 0)

faces = face_cascade.detectMultiScale(gray, 1.3, 5)

def blur_faces(img):
    for (x, y, w, h) in faces:
        cropped = img[y:y+h, x:x+w]
        cropped = cv2.blur(cropped, (11, 11))
        img[y:y+h, x:x+w] = cropped
    cv2.imshow('Image', img)
def show_features(img, factor=1.1):
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(roi_gray, scaleFactor=factor)
        for (ex, ey, ew, eh) in eyes:
            cv2.circle(roi_color, (ex+ew/2, ey+eh/2), (eh+eh)/4, (0, 255, 0), 1)
        cv2.imshow('Image', img)

while True:
    key = cv2.waitKey(1) & 0xFF  # 0xFF is for 64 bit computer
    if key == 27 or key == ord("q"):
        break
    elif key == ord("o"):
        cv2.imshow('Image', original)
    elif key == ord("f"):
        show_features(copy.copy(original))
    elif key == ord("b"):
        blur_faces(copy.copy(original))
    elif key == ord("5"):
        show_features(copy.copy(original), factor=1.5)

cv2.destroyAllWindows()

The results are shown below; first the original (reduced, cropped) image:

This is followed by the automated face and eye detection.  Note that the eye detection routine could not detect my eyes; my original thought was that this could be due to my glasses.  I did look for, and found some other training samples from the OpenCV sources ... but the few additional ones I tried did not succeed in detecting my eyes.  



The author mentioned in Book 2 that the "scaleFactor" parameter could be adjusted resulting sometimes in improved detection (or reduced false positives).  However, no matter what parameter I chose for the scale factor (or other possible parameters listed in Book 2), it did not detect my eyes ... but found that my daughter had apparently four eyes:




Finally, using a simple blur method adapted from Book 1, I could also blur the faces as shown below:




One important point to note though: I had initially downloaded and installed the latest version of OpenCV (3.0 Beta) and found that the face detection script included in Book 2 did not work -- nor (but for a different reason) did the one provided in the tutorial found on the OpenCV website.  So, in the end, and after corresponding with Adrian Rosebrock, the author of Books 1 and 2,   (who has been very patient in answering all my questions, always doing so with very little delay), I downloaded the previous stable version of OpenCV (2.49) and everything worked fine.

As an aside, while I found the experience of using a Virtual Box a bit frustrating, as mentioned in part 2 of this review, I must recognize that all the scripts provided worked within the Virtual Box.
However, the Virtual Box cannot capture the web camera.  Having OpenCV installed on my computer, I was able to run the scripts provided by the author together with my webcam ... and found that the face tracking using the web cam works very well; the eye tracking was a bit quirky (even without my glasses) until I realised that my eyes are rarely fully open: if I do open them wide, the eye tracking works essentially flawlessly.

Stay tuned for part 4, the conclusion.



Sunday, November 23, 2014

Practical Python and OpenCV: a review (part 2)

In part 1, I mentioned that I intended to review the "Case Studies" of the bundle I got from Practical Python and OpenCV  and that I would discuss using the included Ubuntu VirtualBox later.  However, after finishing the blog post on Part 1, I started looking at the "Case Studies" and encountered some "new" problems using the VirtualBox that I will mention near the end of this post.  So, I decided to forego using it altogether and install OpenCV directly.

Note: If you have experience using VirtualBoxes, then it might perhaps be useful to get the premium bundle that includes them; for me it was not.  Including a Ubuntu VirtualBox already set up with all the dependencies and the code samples from the two books is a very good idea and one  that may work very well for some people.

If you need to use VirtualBoxes on Windows for other reasons, perhaps you will find the following useful.

Setting up the VirtualBox

Running Windows 8.1, I encountered an error about vt-x not being enabled.   My OS is in French and googling French error messages is ... hopeless.  So, I used my best guess as to what were the relevant English pages.

From http://superuser.com/questions/785672/linux-64bit-on-virtual-box-with-window-7-profession-64-bit  I understood that I needed to access the BIOS to change the settings so that I could enable virtualization mode.

Unfortunately, I (no longer) was seeing an option to access the bios at boot time.     There are *many* messages about how to re-enable bios access at boot time, most of which simply did not work for me.  The simpler method I found to do so was following (at least initially) the explanation given at http://www.7tutorials.com/how-boot-uefi-bios-any-windows-81-tablet-or-device.

(However, I found out afterwards, that the bios not being accessible is possibly/likely simply because I had a fast startup option checked in power settings.)

Once I got access to the bios, I changed my settings to enable virtualization; there were two such settings ... I enabled them both, not knowing which one was relevant.  I do not recall exactly which settings (I've done this one month ago and did not take notes of that step)... but navigating through the options, it was very easy to identify them.

This made it possible to start the virtual box,  but when I tried for the first few times, I had to use the option to run as Administrator for this to work. 

The first time I tried to start the image (as an administrator), it got stuck at 20%.  I killed the process.  (I might have repeated this twice.)   Eventually, it started just fine and I got to the same stage as demonstrated in the demonstration video included with the bundle.   Started the terminal - the file structure is slightly different from what what is shown in the video but easy enough to figure out.

Using the VirtualBox

I've used the VirtualBox a few times since setting it up.  For some reason, it runs just fine as a normal user, without needing to use the option run as an Administrator anymore.  

My 50+ years old eyes not being as good as they once were, I found it easier to read the ebook on my regular computer while running the programs inside the VirtualBox.  Running the included programs, and doing some small modifications was easy to do and made me appreciate the possibility of using VirtualBoxes as a good way to either learn to use another OS or simply use a "package" already set up without having to worry about downloading and installing anything else. 

As I set up to start the "Case Studies" samples, I thought it would be a good opportunity to do my own examples.  And this is where I ran into another problem - which may very well be due to my lack of experience with Virtual Boxes.

I wanted to use my own images.  However, I did not manage to find a way to set things up so that I could see a folder on my own computer.  There is an option to take control of a USB device ... but, while activating the USB device on the VirtualBox was clearly deactivating it under Windows (and deactivating it was enabling it again on Windows indicating that something was done correctly), I simply was not able to figure out how to see any files on the usb key from the Ubuntu VirtualBox.  (Problem between keyboard and chair perhaps?)

I did find a workaround: starting Firefox on the Ubuntu VirtualBox, I logged in my Google account and got access to my Google Drive.  I used it to transfer one image, ran a quick program to modify it using OpenCV.  To save the resulting image (and/or modified script) onto my Windows computer, I would have had to copy the files to my Google Drive ...

However, as I thought of the experiments I wanted to do, I decided that this "back-and-forth" copying (and lack of my usual environment and editor) was not a very efficient nor very pleasant way to do things.

So, I gave up on using the VirtualBox, used Anaconda to install Python 2.7, Numpy, Matplotlib (and many other packages not required for OpenCV), installed OpenCV (3.0 Beta), ran a quick test using the first program included with Practical Python and OpenCV ... (loading, viewing and saving an image)  which just worked.

Take away

If you have some experience running VirtualBoxes successfully, including being able to copy easily files between the VirtualBox and your regular OS, then you are in a better position than I am to figure out if getting the premium bundle that includes a VirtualBox might be worth your while.

If you have no experience using and setting up VirtualBoxes, unless you wanted to use this opportunity to learn about them, my advice would be to not consider this option.

Now that I have all the required tools (OpenCV, Numpy, Matplotlib, ...) already installed on my computer, I look forward to spending some time exploring the various Case Studies.

---
My evaluation so far:  Getting the "Practical Python and OpenCV" ebook with code and image samples was definitely worth it for me.   Getting the Ubuntu VirtualBox and setting it up was ... a learning experience, but not one that I would recommend as being very useful for people with my own level of expertise or lack thereof.

My evaluation of the "Case Studies" will likely take a while to appear - after all, it took me a month between purchasing the Premium bundle and writing the first two blog post.  (Going through the first book could easily be done in one day.)  

I do intend to do some exploration with my own images and I plan to include them with my next review.

Practical Python and OpenCV: a review (part 1)

A few weeks ago, I purchased the premium bundle of Practical Python and OpenCV consisting of a pdf book (Practical Python and OpenCV) and short Python programs explained in the book, a Case Studies bundle also consisting of a pdf book and Python programs, and a Ubuntu VirtualBox virtual machine with all the computer vision and image processing libraries pre-installed.

In this post, I will give my first impression going through the Practical Python and OpenCV part of the bundle.  My intention at this point is to cover the Case Studies part in a future post, and conclude with a review of the Ubuntu VirtualBox, including some annoying Windows specific problems I encountered when I attempted to install use it and, more importantly, the solutions to these problems. (In short: my Windows 8 laptop came with BIOS setttings that prevented such VirtualBoxes from working - something that may be quite common.)

The Practical Python and OpenCV pdf book (hereafter designated simply by "this book") consists of 12 chapters.  Chapter 1 is a brief introduction motivating the reader to learn more about computer vision.  Chapter 2 explains how to install NumPy, SciPy, MatplotLib, OpenCV and Mahotas.  Since I used the virtual Ubuntu machine, I skipped that part.  If I had to install these (which I may in the future), I would probably install the Anaconda Python distribution (which I have used on another computer before) as it already include the first three packages mentioned above in addition to many other useful Python packages not included in the standard distribution.

Chapter 3 is a short chapter that explains how to load, display and save images using OpenCV and friends.  After reading the first 3 chapters, which numerically represent one quarter of the book, I was far from impressed by the amount of useful material covered.  This view was reinforced by the fourth chapter (Image Basics, explaining what a pixel is, how to access and manipulate pixels and the RGB color notation) and by the fifth chapter explaining how to draw simple shapes (lines, rectangles and circles).  However, and this is important to point out, Chapter 5 ends at page 36 ... which is only one-quarter of the book.  In my experience, most books produced "professionally" tend to have chapters of similar length (except for the introduction) so that one gets a subconscious impression of the amount of material covered in an entire book by reading a few chapters.  By contrast here, the author seems to have focused on defining a chapter as a set of closely related topics, with little regards to the amount of material (number of pages) included in a given chapter.  After reading the entire book, this decision makes a lot of sense to me here - even though it initially gave me a negative impression (Is that all there is? Am I wasting my time?) as I was reading the first few chapters.  So, if you purchased this book as well, and stopped reading before going through Chapter 6, I encourage you to resume your reading...

Chapter 6, Image Processing, is the first substantial chapter.  It covers topics such as Image transformations (translation, rotation, resizing, flipping, cropping), image arithmetic, bitwise operation, masking, splitting and mergin channels and conclude with a short section on color spaces which I would probably have put in an appendix.  As everywhere else in the book, each topic is illustrated by a simple program.

Chapter 7 introduces color histograms explaining what they are, and how to manipulate them to change the appearance of an image.

Chapter 8, Smoothing and Blurring, explains four simple methods (simple averaging, gaussian, median and bilateral) used to perform smoothing and blurring of images.

Chapter 9 Thresholding, covers three methods (simple, adaptive, and Otsu and Riddler-Calvard) to do thresholding.  What is thresholding?... it is a way to separate pixels into two categories (think black or white) in a given image.  This can be used as a preliminary to identify individual objects in an image and focus on them.

Chapter 10 deals with Gradients and Edge Detection.  Two methods are introduced (Laplacian and Sobel, and Canny Edge Detector).  This is a prelude to Chapter 11 which uses these techniques to count the number of objects in an image.

Chapter 12 is a short conclusion.

After going (quickly) through the book, I found that every individual topic was well illustrated by at least one simple example (program) showing the original image and the expected output.  Since the source code and images used are included with the book, it was really easy to reproduce the examples and do further exploration either using the same images or using my own images.   Note that I have not (yet) tried all the examples but all those I tried ran exactly as expected and are explained in sufficient details that they are very straightforward to modify for further exploration.

For the advanced topic, you will not find some theoretical derivation (read: math) for the various techniques: this is a book designed for people having at least some basic knowledge of Python and who want to write programs to do image manipulation; it is not aimed at researchers or graduate students in computer vision.

At first glance, one may think that asking $22 for a short (143 pages) ebook with code samples and images is a bit on the high side as compared with other programming ebooks and taking into account how much free material is already available on Internet. For example, I have not read (yet) any of the available tutorials on the OpenCV site  ... However, I found that the very good organization of the material in the book, the smooth progression of topics introduced and the number of useful pointers (e.g. Numpy gives nb columns X nb of rows unlike the traditional rows X cols in linear algebra; OpenCV store images in order Blue Green Red, as opposed to the traditional Red Green Blue, etc.) makes it very worthwhile for anyone that would like to learn about image processing using OpenCV.
I should also point out that books on advanced topics (such as computer vision) tend to be much pricier than the average programming book.  So the asking price seems more than fair to me.

If you are interested in learning about image processing using OpenCV (and Python, of course!), I would tentatively recommend this book.  I wrote tentatively as I have not yet read the Case Studies book: it could well turn out that my recommendation would be to purchase both as a bundle.  So, stay tuned if you are interested in this topic.

Wednesday, November 19, 2014

Translating a programming environment

tl;dr: volunteers always welcome! ;-)

Over the past few years, there has been an explosion in the number of web sites dedicated to teaching programming.  A few, like Codecademy, are making an effort to offer multilingual content.  While there seems to exist a prejudice shared by many programmers that "real programmers code in English and therefore everyone should learn to program using English", there is no doubt that for most non-English speakers, having to learn programming concepts and new English vocabulary at the same time can make the learning experience more challenging. So, for beginners, the best learning environment is one that is available in their native tongue.  However, to do so can require a massive amount of work and a team of people.  Freely available programming environments rely on the help of volunteers to provide translation.

Viewed from the outside, the work required to translate a programming environment like that of codecademy appears to be an all or nothing kind of effort, as everything (UI, lessons and feedback messages) is tightly integrated.  A given tutorial is broken up in a series of lessons, each of which can be translated independently.  For many of these lessons, the range of acceptable input by users is fairly limited, and so are the possible feedback messages.  Still, the amount of work required for providing a complete translation of a given tutorial is enormous.

When I started the work required to create Reeborg's World, I knew that I would be largely on my own.  Still, I wanted to make sure that the final "product" would be available in both English and French, and could be adapted relatively easily for other languages.  The approach I have used for Reeborg's World is different than that of codecademy, and has been greatly influenced by my past experiences with RUR-PLE and Crunchy, as well as during the development of Reeborg's World itself.

1. I have completed separated the tutorials from the programming environment itself.  Thus, one can find a (slightly outdated) tutorial for complete beginners in English as well as  a French version of the same tutorial, separately from the programming environment itself.  This separation of the tutorial and the programming environment makes it easily possible for others to create their own tutorial (in whatever language) like this beautiful tutorial site created by a teacher in California that makes use of Reeborg's World, but calls the robot Karel as a hat tip to the original one created by Richard Pattis.

2. The programming environment is a single web page, with a mimimum amount of visible text. Currently, there are two versions: one in English, and one in French.

3. Programming languages available are standard ones (currently Python, Javascript and CoffeeScript). Whereas I have seen translated "real" programming language (like ChinesePython or Perunis, both of which are Python with keywords and builtins translated in other languages) or translated mini-programming language, like that used by Guido van Robot, I do not believe that the slight additional work required to memorize the meaning of keywords and builtins is a significant hurdle, especially when taking into account the possibilities of using the programming language outside of the somewhat artificial learning environment.

4. Possible feedback given to the user are currently available in both French and English.  The approach I have used is to create a simple Javascript function

RUR.translate = function (s) {
    if (RUR.translation[s] !== undefined) {
        return RUR.translation[s];
    } else {
        return s;
    }
};

Here, RUR is a namespace that contains the vast majority of functions and other objects hidden from the end user.   A given html page (English or French) will load the corresponding javascript file containing some translations, such as

RUR.translation["Python Code"] = "Code Python";

The total number of such strings that need to be translated is currently slightly less than 100.

5. Functions (and a class) specific to Reeborg's World are available in both English and French, as well as in Spanish (for the Python version).  By default, the English functions are loaded on the English page and the French ones are loaded on the French page.  An example of a definition of a Python command in English could be:

move = RUR._move_

class UsedRobot(object):
    def move(self):
        RUR.control.move(self.body)

whereas the French equivalent would be

avance = RUR._move_

class RobotUsage(object):
    def avance(self):
        RUR.control.move(self.body)

with similar definitions done for Javascript.

6. When using Python, one can use commands in any human language by using a simple import statement.  For example, one can use the French version of the commands on the English page as follows:

from reeborg_fr import *
avance()     #   equivalent to move()
move()       #   still works by default

If anyone is interested in contributing, this would likely be the most important part (and relatively easy, as shown above) to translate in other languages.

7. As part of the programming environment, a help button can be clicked so that a window shows the available commands (e.g. move, turn_left, etc.).  When importing a set of commands using Python in a given human language as above, there is also a provision to automatically update the help available on that page based on the content of the command file. (While the basic commands are available in Spanish, the corresponding help content has not been translated yet.)

8. In order to teach the concept of using a library, two editor tabs are available on the page of the programming environment, one of which represents the library.  In the tutorial I wrote, I encourage beginners to put the functions that they define and reuse in many programs, such as turn_right or turn_around, in their library so that they don't have to redefine them every single time.  The idea of having a user library is one that was requested in the past by teachers using RUR-PLE.  The idea of using a second editor on the second page is one that I first saw on this html canvas tutorial by Bill Mill.  When programming in Javascript (or CoffeeScript), for which the concept of a library is not native like it is in Python, if the user calls the function

import_lib();

the content of the library will be evaluated at that point.

For Python, I ensure that the traditional way works.   Brython (which I use as the Python in the browser implementation) can import Python files found on the server.  For the English version, the library is called my_lib.py and contains the following single line:

from reeborg_en import *

(The french version is called biblio.py and contains a similar statement.)

When a user runs their Python program, the following code is executed

def translate_python(src):
    import my_lib
    #setup code to save the current state of my_lib

    exec(library.getValue(), my_lib.__dict__)
    exec("from reeborg_en import *\n" + src)

    # cleanup to start from a clean slate next time

The content of the editor (user program) is passed as the string src.  
library.getValue()returns the content of the user library as a string.  Even if my_lib only imported once by Brython, its content effectively gets updated each time a program is run by a user.

9. One thing which I did not translate, but that we had done for Crunchy (!), are the Python traceback statements.  However, they are typically simplified from the usual full tracebacks provided.

In summary, to provide a complete translation of Reeborg's World itself in a different language, the following are needed:
1. a translated single page html file;
2. a javascript file containing approximately 100 strings used for messages;
3. a Python file containing the defintion of the robot functions as well as a corresponding "help" section;
4. a javascript file containing the definition of the robot functions. If the Python file containing translation is available, it is trivial for me to create this corresponding javascript file.

Saturday, November 08, 2014

Tracking users

A while ago, after reading about how much excessive tracking of users is done on the web, I decided that I was not going to track users of Reeborg's World.  I had initially tracked users with piwik, found that some minor spikes occurred when I posted about it (I even found out that one person had copied the entire site) but the information that I got did not seem to be worth getting.

So, I stopped tracking, and mentioned on the site and elsewhere that I was not tracking users ... and have no idea how much it is used (it's free to use and does not require any login), and if the various changes I made to the site which are intended to be improvements are seen as such by the silent users.

A few people have contacted me with suggestions for improvements and with mentions of how useful the tutorial and the programming environment were in helping to learn Python.  I am very grateful to those that contacted me.  Still, I am curious about how much the site is used.

So, I am going to start tracking users and thought I should mention it in the interest of openness.  After reading about various options, I am thinking of using Clicky instead of Piwik ... but I am curious to find out if anyone has better suggestions.

Thursday, November 06, 2014

Line highlighting working - development version

Reeborg's World is better than RUR-PLE ever was in all aspects ... except for one thing: line of codes being executed were simultaneously highlighted in RUR-PLE something which, up until now, had not been possible to do on the web version as it relied upon some features specific to CPython.

Well... I may have just found an alternative method. :-)




Tuesday, November 04, 2014

Partial fun

My daughter suggested to me that

To sort in descending order in python, you should be able to do
detros() instead of sorted( ... reverse = True)

This is what I suggested to her:

>>> from functools import partial
>>> detros = partial(sorted, reverse=True)
>>> detros([1, 3, 4, 2])
[4, 3, 2, 1]

I love Python. :-)

Sunday, November 02, 2014

Tasks with random initial values

Quick Reeborg update:

Reeborg's World now allows for tasks with randomly selected initial values within a certain range.  For example, this world (and solution) is such that Reeborg can start at any of three initial positions, in any of the four basic orientations.   Furthermore, there is an initially unspecified number of tokens at grid position (5, 4).    This type of random selection opens the possibility to create more interesting programming challenges.

Friday, June 20, 2014

Reeborg in real life

One of the first users of rur-ple is a high school teacher in New Jersey who gave me some great feedback while I was developing the program.  He reall seems to have found a way to make students enjoy learning.  A while ago, he sent me this picture of one of his students who decided to make a Reeborg costume for Hallowe'en.


And, today, for the last day of school, another student gave him this as a token of appreciation:

Knowing that the little robot, whose image I drew pixel by pixel with my limited art skills, seem to have been appreciated by students as they learned programming using Python makes my day! :-)

Wednesday, May 21, 2014

Reeborg programming challenges - Challenge #1

Can you have Reeborg do multiplication without using numerical variables?

Reeborg must show that it can multiply two numbers by taking a number N tokens located at x=1 and  y=Y, and leave a single token at row y=1 and x = N*Y.  For example, here's a starting position (image taken from RUR-PLE, so that it looks a bit different from the web version)
the final position must be
where a single token must be deposited.

So, without using numerical variables, and using only a single instruction per line (so no use of semi-colon, or having a colon followed by a statement), how short can a solution to this problem be?  Here's an example of a solution that is NOT allowed under the above rules:
think(0)
select_challenge("mul5x1")
y = 1      # numerical variables not allowed
tokens = 0;turn_left()  # use of ; is not allowed
def turn_around():
    turn_left()
    turn_left()
while not token_here():  
    move()
    y += 1  # not allowed
while token_here():
    take()
    tokens += 1  # not allowed
turn_around()

while front_is_clear():  move()  # not allowed, statement after colon

turn_left()
repeat(move, y*tokens-1)  # not allowed, multiplication
put()

The solution must work for 5 different challenges (mul5x1, mul1x5, mul5x5, mul4x3, mul3x2) - or any other such challenges for which I could create a world. Excluding the line with think(0), which makes Reeborg move as quickly as possible, and the line select_challenge(...), can you write a solution shorter than 29 lines?  Solutions can be attempted at Reeborg's World. You may want to click on the help button to see a brief summary of all known instructions.




Le monde de Reeborg: finalement en français!

J'ai finalement complété la traduction de la nouvelle version du monde de Reeborg en français. Les tutoriels pour débutant, que ce soit pour Python ou pour Javascript ne sont cependant pas encore traduits.

Si la version française vous intéresse, svp contactez-moi par courriel (ou laissez un commentaire sur ce billet) pour me le laisser savoir et que je puisse mieux juger du besoin de traduction des tutoriels.

Thursday, May 15, 2014

Eating your own dog food

When I created rur-ple, my intention was to use it to teach my pre-teens computer programming.  When it was working reasonably well, and after having written a few lessons, I showed it to my daughter who quickly went through the material I had prepared and concluded that it was too easy/boring.  She essentially decided then that programming was not for her.

Fast forward 10 years.  She had to do some programming as part of her university program and found out that she really enjoyed it.  She is currently more than 1000 km away, working in a lab where she has to program in Python, which she is essentially just learning.  She had a question for me, emailed me some code when I got the idea of using Reeborg's world's editor and the embedded TogetherJS from Mozilla so that we could share a screen, while talking using Skype.  

It worked very well. :-)   However, I found that, while the fixed-size editor was big enough for the tutorials I wrote, it was too limiting when trying to work collaboratively on "real life" code.  The same could be said for the output area ("Reeborg's Diary"). Nothing like "eating your own dog food" to find its limitations.    So, after a couple of hours of tinkering with javascript/jquery/css, I finally got a reasonably working setup for remote collaboration/help on Python 3 code (using Brython) or Javascript or CoffeeScript or .... (more languages to come eventually).


The only limitation that we found is having the TogetherJS chat window in a fixed position. If it could easily be moved (on a per-user basis), it would make it even more useful.

Tuesday, May 13, 2014

Python tutorial for beginners.

The first draft of my Python tutorial for beginners is now live.  The previous link is for the tutorials; the actual code needs to be entered in Reeborg's World.  There is also a Javascript version of the tutorial.

Tuesday, April 29, 2014

Collaborate on Reeborg's World using Mozilla's TogetherJS

If you go to http://reeborg.ca/world.html and click on "Start TogetherJS" at the top, you can get a URL to share with others.  This hopefully will be found useful by teachers who can help their students remotely.

Note that the exact location of the Start TogetherJS button may change in the near future.  Thanks to Ian Bicking and others at Mozilla for creating this amazing tool.

Feedback is welcome! ;-)


Reeborg's world: more options to share

RUR-PLE's primary goal was to create a very easy path for people that wanted to learn programming.  The original version still had a significant hurdle to jump over for absolute beginners.  They had to 1) download and install Python; 2) download and install wxPython; 3) download and unzip the files for RUR-PLE's distribution.  This is probably considered to be trivial by the reader of this blog ... but it was not so for the users (some of whom were young children ... or their parents!)  So, it really required to have someone nearby with some computer savyy and, since it required to install programs, some teachers could not use it without jumping through additional hoops to have it installed on the school computers.

As more users joined in, some volunteered to create "one-click" install files (.exe, .dmg, etc) for various systems.  These were made available on Google code; however, since new downloads can not be added (due, I understand, to how this service was abused by some other "projects"), this means that yet a new home (after the original sourceforge site) would have to be found for updates.  Furthermore, this does not solve the issue of installing software on school's computers.

These various facts were the main impetus behind my desire to create a web version as something with the absolute lowest barrier to entry.  To my mind, this means no creation of user accounts.   With no user accounts, there is nothing saved on the server.  Still, it is useful for people learning to program to be able to save their results.  So, I implemented:

  • An automatic save of a program state when it's run successfully.  This includes the code in the "library".  It also saves the "world" that was selected, as well as the programming language (Python, Javascript of CoffeeScript). This way, when a user returns to the page, it restarts from where it was left off.   However, this uses localStorage which is appropriate only when everything is done from a single browser.
  • For students that may want to work on the school computer and at home, I implemented a way to save to files (hello USB keys...); this can be either the world selected (which the student can edit) or the program or the content of the library, each saved separately.
I thought this was going to be enough until I got an email from the Samsung folks behind the Junior Software Academy initiative.  As I mentioned in a previous blog, they created a book based on rur-ple.  They now would like students to take part in a mini programming contest and wanted to be able to have the students show their results.  This works fine if rur-ple is installed on the computer they have ... but a web based solution was thought to be more interesting. So, I implemented a permalink utility which enables one to save the complete state (programming language, world definition, code in editor and library) and share this as a url.  Here is a silly example.  

One more thing I would like to do is to implement a collaborative mode using Mozilla's TogetherJS.  However, this will require quite a bit more coding, based on what I saw in an earlier attempt, just after TogetherJS was announced. 

Monday, April 14, 2014

Reeborg knows multiple programming languages

I wish I were in Montreal to visit my daughter, eat some delicious Saint-Sauveur bagels for breakfast, a good La Banquise poutine and some Montreal Smoked Meat for lunch... and, of course, attend Pycon.  Alas....

In the meantime, a quick update: Reeborg now knows Python, Javascript and CoffeeScript.  The old tutorials are gone as Reeborg's World has seen too many changes.  I now am in the process of writing the following tutorials, all using Reeborg's world as the test environment

  1. A quick introduction to Python (for people that know programming in another language)
  2. A quick introduction to Javascript (same as above)
  3. A quick introduction to CoffeeScript (same as above)
  4. An introduction to programming using Python, for absolute beginners
  5. An introduction to programming using Javascript, for absolute beginners
  6. An introduction to Object-Oriented Programming concepts using Python
  7. An introduction to Object-Oriented Programming concepts using Javascript
Note that I have two "versions" of Javascript, one that uses JSHint to enforce good programming practices (and runs the code with "use strict"; option) and one that is the normal permissive Javascript.

If anyone knows of any other transpilers written in Javascript that can convert code client-side from language X into Javascript (like Brython does for Python, or CoffeeScript does naturally), I would be interested in adding them as additional options.

Thursday, March 13, 2014

Reeborg news

People reading this blog may be familiar with rur-ple (https://code.google.com/p/rur-ple/), a Karel-the-robot clone using Python that I wrote.  The robot's name in my version is Reeborg, hence the title of this post.

Since the last version was produced in 2009, rur-ple had been downloaded more than 11,000 times.  Since people that download it do not need to contact me to do so, it is only through serependity that I find out where it is used.  By doing some searches on the web, including videos on youtube, I found out that it has been used by elementary school children in Austria, high school children in the U.S. and by university students in the U.S. and Latin America as a tool to introduce Python.

Recently, Samsung contacted me and asked my permission (!) to produce a book based on rur-ple. They distributed free copies of this book yesterday to approximately 1000 Korean students.  

So what you say?....

I am happy to announce that a test-version of "Reeborg's world" is now available online as a tool to learn Python.  Like the desktop version (rur-ple) I wrote, it is free to use and does not require registration.

The first version of Reeborg's world was produced to teach Javascript; it is the default version available from http://reeborg.ca.    It includes some 98 lessons (available in both English and French); the English version can be found directly at http://reeborg.ca/learn_js.html

I'm working on an "improved" version which can be found at http://reeborg.ca/learn_js_dev.html 
Following some comments by early adopters, the UI of this version is improved slightly and more changes are planned, including a graphical world builder, new images for the robot, the option to import from file and save to file  programs and worlds, etc.  I am also thinking of adding a collaborative option using https://togetherjs.com/.

This time, I will probably include a page on the site where I will ask teachers that use it to communicate with me to let me know in what context they use it, and keep track of it on a "wall of fame".

The proof-of-concept Python version, which is probably of greater interest for readers of this blog, can be found at http://reeborg.ca/learn_py_test.html It is based on the "improved" version and uses Brython  to translate the Python code into Javascript, which can then be executed using eval(), like the Javascript version does.  I do not plan to do more work on it (including adapting the lessons to teach Python instead of Javascript) until I have nailed down the "improved" Javascript version.

If you want to quickly try the Python version to see what it can do, after dismissing the "contents window", I suggest you select (from the drop down menu showing "Alone" by default) the world "Tokens 1" and run the following program:

move()
take()
move()
put()
move()

I welcome any comments & suggestions about Reeborg's world; please feel free to email me directly. 


Thursday, February 13, 2014

Importing modules in RUR-PLE

When I first started working on RUR-PLE some 10 years ago, I wanted to create a "safe" environment so that beginners using it could not be "tricked" by others into running malicious programs.  So, before running a user program, I would parse it and prevent the use of keywords such as "chr", "exec", "eval", "input" and "import".  However, I still wanted to teach the idea of "importing a module", so I created a "fake" module allowed the user to have the line

import useful

or

from useful import turn_right

into their program, and this would import various "useful" definitions so that they would not have to retype them.  I added a note to teachers  at the bottom of the relevant lesson, telling them that they could get in touch with me if they wanted to remove the "import" restriction, and I would tell them how to modify the code.

Somewhere along the way, prompted by a discussion with a user, discussion whose details I have long forgotten, I removed the so-called useful module and enabled the import statement to be used. However, the lesson about the "import useful" module remained (and has been translated into other languages) as I just noticed, much to my embarassment.

Today, five years after I last modified the code, I was contacted by a teacher had read what I wrote and who wanted their students to be able to save their own functions and import them.  Here's a way to do it. Note however it involves a bit of "magic" (at least from the point of view of the student).

First, just to demonstrate that import works, one can type the following:

import sys
print sys.path   # python 2 syntax
turn_off()

Here is the output from the window below the robot world (I used the version installed by the .exe file)

['C:\\Program Files (x86)\\RUR-PLE', 'C:\\Program Files (x86)\\RUR-PLE\\library.zip']

I used a usb key (recognized as drive d: on my computer) to save the file my_def.py and containing the following:

from rur_py.cpu import rur_program
robot = rur_program()

def turn_around():
robot.turn_left()
robot.turn_left()

The first two lines contained the required "magic" so that our user-defined module can use all the same robot instruction that a "normal" program can.  However, these instructions, such as turn_left(), must be preceded by "robot."  [or any other name given to rur_program()].   

Note that I could have used just about any name instead of "my_def".

Once we have saved this module, we can now use it.  First, we need to make rur-ple aware of where our definitions exist.  To this end, we first run the following program once within RUR-PLE:

import sys
sys.path.append("D:\\")
turn_off()

Next, if we want to use our definitions found in module my_def.py, we do as follows:

import my_def
my_def.turn_around()
turn_off()

Of course, one could use the  "from my_def import turn_around" syntax instead so as not to have to write
"my_def." in front of every instruction I define.

Note that, if my_def is modified, RUR-PLE will have to be restarted and the entire procedure above will have to be repeated. 

===
Note added after the original post.  I wrote RUR-PLE when I was just relearning computer programming and I did not really understand a lot of things very well.  rur_program() referred to above is not a function, but a class (I was learning!...).  And, having learned a bit of Java some years before and having come accross some sample code about singletons in Python, I wrote rur_program() to be a singleton, thinking I was never going to need more than one user program at a time.  I don't think most Python programmers would have done that.  Yet, through sheer luck, this is needed for the above to work, otherwise "turn_left" in the imported module, defined as a method of rur_program, would have referred to a different instance than that used in the user's program, and it would not have been possible to import user definitions as done above. 
===
Edit #2 
I know that some teachers that use rur-ple are not familiar with advanced Python features; so here's another equivalent way (sent to me by a reader) to import and use modules with rur-ple; the path uses the syntax one may have on a Mac.  (For people familiar with Python, I do realize that both ways will look essentially the same - but they do not for beginners.)

from rur_py.cpu import rur_program
robot = rur_program()

def left():
    robot.turn_left()

def turn_around():
    left()
    left()

def right():
    turn_around()
    left()


======== and within rur-ple

import sys
my_py_dir = "/Users/andre/rurple_files"
if my_py_dir no in sys.path:
     sys.path.append(my_py_dir)       

from my_module import *

# main program

right()
turn_off()