Thursday 5 July 2012

Defining a game

The way Rye displays a game layout is defined in a text file. Each block or object is defined in the file by a single character. When Rye loads it tries to open a file called default.rye. A rye file can have lots of pages in it. All of the file is read at once, parsed into pages which get stored until a page is displayed. At that point the page is expanded into a specialised object that handles all of the interaction and movement. Rye pages can be different sizes, the original Kye game that inspired Rye had fixed size pages. Having variable sized pages makes the games much more flexible.

I designed a simple start up page. You can see a few different objects on the page but the whole thing gets created with simple lines in a file.  The layout for this page is

Beginner,10,10
A small start
5555555555
5p G  5p 5
5555e55 55
5T 5 15b55
5be5     5
5    s   5
5        5
5   45v555
5   e e p5
5555555555


The first line has three parts, Beginner is the name of the page, 10,10 are the width and height of the page. The second line is a hint or message for the page. The size of the remaining section is defined by the width and height. This has 10 lines (height = 10) and each line has 10 characters (width = 10). Each character in this section defines a single block. It is usual to surround the whole page with solid walls, usually the '5' character which is a wall block with square edges. Walls with rounded edges are created with numbers laid out like this:

789
456
123

So, the fourth line has a rounded corner defined by '1' and the eighth line has a rounded wall end which is defined by '4' . Every page must have a 'G' which is Rye and at least one 'p', a prize. You would expect to see one or more beasts, 'T', to increase the jeopardy.

Next we need to load the file and display pages.

Monday 2 July 2012

Menus for Rye

Rye is coming along. We need to add a little more to the layout of the program then we can concentrate on making it function well. As the program gets longer and longer it will be tedious to keep reproducing all of it here, so I'm going to highlight additions here from previous versions and, as before, provide the whole code to download.

We will need a menu bar to allow some interaction outside of the game, such as loading a game layout or restarting the game. Menus are made up of parts and we need to add the parts in the right way to make it work. In the GUI initialisation part of the program we need to define the menus:

# create a menu
menubar=Menu(self) 1
filemenu=Menu(menubar, tearoff=0) 2

filemenu.add_command(label='Open game',command=self.mnuOpen) 3
filemenu.add_command(label='Exit', command=self.quit)
menubar.add_cascade(label='File', menu=filemenu) 4
   
lvlmenu=Menu(menubar,tearoff=0)
lvlmenu.add_command(label='Restart level', command=self.mnuRestart)
lvlmenu.add_command(label='Goto level', command=self.mnuGoLevel)
menubar.add_cascade(label='Level', menu=lvlmenu)
   
helpmenu=Menu(menubar, tearoff=0)
helpmenu.add_command(label='About', command=self.mnuAbout)
menubar.add_cascade(label='Help', menu=helpmenu)
   
self.config(menu=menubar) 5


This creates a menubar with three menus on it, File, Level and Help. Line 1 creates an empty menubar. Each of the menu columns is created as in line 2. The tearoff=0 prevents the menu being able to be dragged for later use elsewhere, which I wish was the default state. Each of the items on the dropdown column are added using the example in line 3. This provides a label and points to a function to execute if the option id clicked. This is an event handler. Just like we have seen before, the event handler is called when the mouse is clicked on the menu, not by calling it in code from somewhere. This means we don't need to monitor mouse or keyboard actions, Tkinter does that for us and just calls our code at the right time. We now add the whole column of the menu with line 4, giving it a label of File. This process gets repeated for the Level and Help menu columns. Now the menu structure is created we use config, in line 5, to add it to the window.

We now add the event handlers used above. For now, some will not do much, but it will show the benefit of running these GUI programs from a terminal window, because we can write a message to the terminal to show the event handler has run.  Add these event handlers:

def mnuOpen(self): 6
    print 'Open' 7
    filename = tkFileDialog.askopenfilename(filetypes=[('Rye games','*.rye')]) 8
    print filename 9

def mnuRestart(self):
    print 'Restart level'

def mnuGoLevel(self):
    print 'Goto level'

def mnuAbout(self):
    print 'About'
    tkMessageBox.showinfo(parent=self,message='Rye version 0.3',detail='A game to build for yourself',icon='info',title='About')


Each of these functions gets called by name from the menu item if it is clicked. Line 6 defines the function that will be called if the 'Open game' item, defined in line 3, gets clicked. the function uses print 'Open' one line 7 to display the word Open on the terminal, not on the GUI window. It then displays a dialog box to open a file in line 8 and if a file is chosen the name gets printed on the terminal in line 9. The dialog box is a standard part of Tkinter and of GUIs in general. You do need to include an extra import line near the top of your program to make this work:

import tkFileDialog

The dialog box is an askopenfilename type, which means it displays a list of files for you to choose from and returns the name of the selected file or an empty string ('') if you cancel. The dialog does not open the file for you, just helps you choose the file to open. We have added an option for filetypes. This is a list of file types to display - others will not be displayed. In our we want to see any file that has an extension of '.rye', so we use *.rye and name these Rye Games.

The event handlers for Restart Level and  Goto Level simply print a message so we know the handler is working. Later we will extend these. The handler for About displays a messagebox. It also needs an import line

import tkMessageBox

This is a rather crude way of displaying an about box, but it works for now.

The remaining parts of the Rye screen are boxes to show how many lives are remaining, the current level of play, how many prizes are still remaining and a hint on how the level might be finished. They are added to the grid for the window to position them. I have included the canvas for the game-play because it is also included in the grid.

        # create the canvas for the game
        self.cnvs = Canvas(self, height=300, width=300)
        self.cnvs.grid(column=0, row=0, columnspan=4) 10

        # show the remaining lives
        self.lives = Canvas(self, height=22, width=62)
        self.lives.grid(column=0, row=1, sticky='W') 11
       
        # show the level number
        self.lbllevel = Label(self, text='Level:', width=6)
        self.lbllevel.grid(column=1, row=1, sticky='W') 12
       
        # show how many prizes remain
        self.lblprizes = Label(self, text='Prizes:',width=6)
        self.lblprizes.grid(column=2,row=1, sticky='W') 13
       
        # show the hint for the page
        self.lblhint = Label(self, text='hint')
        self.lblhint.grid(column=3,row=1, sticky='E') 14
       
        # stretch column 3 on resize
        self.columnconfigure(3,weight=1) 15
       
        # window is not resizable
        self.resizable(False,False) 16

In line 10, the canvas is positioned in column 0, row 0. Below the game play there will be 4 items in a single row. We want the game play to be the same width as the total width of the four columns below it, so we add the option columnspan=4, meaning it will span from column 0 to column3 - a total of 4 columns. You can make things span rows too, but we don't need to. We add a canvas to draw the remaining lives on in row 1, column 0. Adding option sticky='W' on line 11 means it will stick to the west end of the box usually known as being left-justified in other programs. The Labels in lines 12 and 13 are where text will be written and both are also stuck west. The label in line 14 has the option sticky='E', to force it to be right-justified.  The size of the game play canvas may change with various game layouts, so it would be good to have the hint stretch to fill the space, the other labels have a fixed size (option width=). To do that we make column 3 have weight 1 - others default to 0. This means that when the window is being resized anything in column 3 will get resized because its weight is greater than 0. You can create elaborate weighting to define what proportion of the resize falls into which column, but we don't need to.  Line 15 is all we need.

The remaining job is make sure the window cannot be resized with the mouse and line 16 does this. The two options are for vertical and horizontal resizing, both of which are prevented.

Your copy of menuRye is available for download here.

=========================
Update:
I forgot to point out a simple menu option between line 4 & 5. The File>Exit menu option uses the command command=self.quit. This is how to end the program by closing the top-most window. This is not always the way you might want to close a GUI program, you might want to offer the change to save a file before closing, so the Exit menu option might want to use an event handler with code just like any others, but this quick way out suits out game.

Tuesday 26 June 2012

The classy way

One of the issues with GUI programs is that it is event-driven. The code in the event handlers is not called directly by your program it is called in response to something happening. This really is a great way to manage GUI programs, but it takes a bit of thinking about. When your event handler is triggered it will want to interact with other parts of your program; read or change variables, call other functions and so on. The event handler is not called directly by your code, that is the point. It responds to an event whenever the event happens. This means that you cannot pass variables to the event handler as parameters. We used global variables to overcome this but this is not a great way to do it. A better way is to use a class.

I'm not going to describe the ins and outs of classes and object-oriented programming - Google it. I will say that a class is the definition of how an object works and an object is what a program creates from the class. Objects run in code, classes are the definition of objects. The class can define data which can be used throughout the object. The class can also define functions to work within the object, using the object's data.

Data variables defined in a class are known as fields and when they are used outside of the object they can be known as properties. Functions defined in a class that get used from  outside the object the class defines are known as methods.

If we define a class in which we include all of the data fields we need to make our game of Rye work and define the event handlers there too, the event handlers can access any of the data they need. The global variable disappear with a touch of class.

Python has good support for classes. If you want to know more, take a look at the documentation.

I have converted the previous program to a version that uses a class, without adding any extra functionality.

#!/usr/bin/env python

from Tkinter import *

import ryepics

class ryeapp(Tk): 1
   
    def __init__(self,parent): 2
        Tk.__init__(self,parent) 3
        self.parent=parent
      
        # create the shared variables 4
        self.imageList=ryepics.createImages()
        self.ryeId=0
        self.ryeX=0
        self.ryeY=0
      
        # GUI initialisation
        self.title('Raspberry Rye')
        self.grid()
      
        # create the canvas for the game
        self.cnvs = Canvas(self, height=300, width=300)
        self.cnvs.grid(column=0, row=0, columnspan=4)
      
        # draw Rye at position 2,2
        self.ryeX=2
        self.ryeY=2
        sx=self.ryeX*20+10
        sy=self.ryeY*20+10
        self.ryeId=self.cnvs.create_image(sx,sy,image=self.imageList['G'])
      
        self.cnvs.bind("<Key-Down>", self.ryeDown)
        self.cnvs.bind("<Key-Up>", self.ryeUp)
        self.cnvs.bind("<Key-Left>", self.ryeLeft)
        self.cnvs.bind("<Key-Right>", self.ryeRight)
   
        # set the canvas to receive the focus, so keypresses are captured
        self.cnvs.focus_set()
   
    def ryeDown(self,event): 5
        self.cnvs.move(self.ryeId, 0, 20)
   
    def ryeUp(self,event):
        self.cnvs.move(self.ryeId, 0, -20)

    def ryeLeft(self,event):
        self.cnvs.move(self.ryeId, -20, 0)

    def ryeRight(self,event):
        self.cnvs.move(self.ryeId, 20, 0)

if __name__ == "__main__":
    app = ryeapp(None) 6
    app.mainloop()


This version is very similar to the last one, except that almost all of the program is now wrapped up in a class call ryeapp 1. The class is based on the Tk class, so our class extends the Tk class. Extending a class is known as inheritance and a fundamental part of object-oriented programming. All of the functions of Tk are now part of our class and we can add as much extra stuff as we want. This is not the only way to use a class, when you know more you might do it another way.

Classes can define a special function that is always called __init__ (that's two underscores before and after the init). Whenever the class is used to create an object this function will be called, so this is a great way to initialise our GUI. We define it on line 2 which is slightly different from the def statements we used before. Firstly it is indented within the class level. This means it is part of the class. Secondly the first parameter is self. This is a special reference to the rest of the class. This is how all the parts of the class can see each other. We'll use self whenever we want to refer to other parts of the class.

We have based our class on Tk. Tk has its own __init__ function, so the first thing we must do is call it, as in line 3. All of the rest of the function is similar to what we did in the previous program and it is worth comparing them. All of the variables we created as global variables before we can now create as fields in the class, starting at line 4.Everywhere we want to refer to a field we must use self. as the prefix to use the one in the class and not some other variable.

The event handlers are now defined as part of the class, starting on line 5. They can refer to the self.cnvs and self.ryeId safely.

Lastly when we have defined the class we need to use it to create the object that actually does something. Line 6 does this, creating the object app from the class ryeapp, passing None as the parent name, since this is the top level window it has no parent. The creation of the object automatically called our __init__ function which here does most of the work. When that is complete the mainloop() can start which waits for the events which get processed as we have seen before.

You can download a copy of classRye.py here.

Sunday 24 June 2012

Make Rye move

The next stage is to draw something on the canvas and make it move. There are various ways to create an image on the canvas. One simple way would be to draw the objects you want to display in a drawing package and store each one as some kind of picture file like a .gif or .png file. There will be lots of images needed eventually, so there would be lots of separate pictures to manage. Another way would be to draw the objects in the program so there are no external pictures. This is the way I chose, partly as an example of how to draw these objects and use them. We need to use Python Imaging Library module to draw the pictures and a module to make these work with the Tkinter module. At the command prompt, with the R-Pi connected to the internet, type

sudo apt-get install python-imaging python-imaging-tk

This will install the two modules in one command.

Lets start with creating a simple object to draw. There will be lots of drawings later on, so lets start a new module of our own to keep this separate from the main code. I have chosen to call it ryepics.py. Create a new file in the rye folder called ryepics.py and open the empty file in Geany. The code is below, and you can download it from here.

#!/usr/bin/env python

from PIL import Image, ImageDraw, ImageTk 1

def createImages(): 2
    #create a dict for the images to return
    imageList = {} 3
   
    #start with rye
    iRye=Image.new('RGBA',(20,20)) 4
    draw = ImageDraw.Draw(iRye) 5
    draw.ellipse((1,1,19,19), fill=(0,255,0), outline=(0,40,0)) 6
    draw.point((7,7),fill=(255,255,200)) 7
    draw.point((7,8),fill=(255,255,200))
    draw.point((8,7),fill=(255,255,200))
    draw.point((8,8),fill=(255,255,200))
   
    drye=ImageTk.PhotoImage(iRye) 8
    imageList['G']=drye 9
   
    return imageList 10


The import line, 1, uses the new modules we have just installed on R-Pi. Modules can use functions, so we define ours on line 2, called createImages(). Next we introduce one of the superb built-in parts of Python, a dictionary 3. More about that below. We now use the Image object from the imported Image module. Line 4 creates an empty image that is 20x20 pixels. It is an RGBA type, that is it has the full range of colours and transparency. Because we didn't define a coloured background it will be transparent, which is really great for our needs. Line 5 creates a drawing object we use to add to the image. Line 6 draws an ellipse in a rectangle. All references to the location on the images and the canvas are based on the normal computer layout that top left is (0,0). The x axis increases to the right and the y axis increases down the screen. The rectangle is defined by the four numbers top left is (1,1) and bottom left is (19,19). This draws a circle, because the rectangle is square. It is filled with green  - the colours are defines as red-green-blue in the range 0-255. The outline is dark green. To make the blob look a bit smarter I added 4 bright pixels from line 7.

The image now needs to be converted to something that a canvas understands which is what line 8 does. At this point we don't want to use the image, we want to store it for later use. We add the image to the dictionary we created earlier in line 9. In later stages we will add other images to the dictionary, but at this stage we just return the imageList dictionary from the function in 10. Functions can always return something, but don't have to.

So what is this dictionary? It is one of the built-in types of collection that Python offers. There are four very commonly used collections: string, tuple, list and dictionary. Programming languages usually have some kind of string to hold pieces of text and they don't usually get thought of as a collection, but in Python it is worth thinking of it in that way, because the same way of breaking up a collection apply to strings too. A tuple is a group of objects and so is a list. Tuples (and strings) are immutable - once created they can't be changed, whereas lists can be changed, sorted appended to etc. Individual elements of lists, tuples and strings can be accessed by their numeric index. Dictionaries are like lists; they are mutable (can be changed) but their index can be any key you like, each key in a dictionary must be unique. There is a lot that you can do with these collections and you might like to read the Python online manual to find out more.

In our imageList dictionary in line 9 we used a key of 'G' to store the image. We can refer to the green blob we have drawn by the key 'G' when we want it later.

Now we have an function that will create an image list that we can use. We need to amend the original start.py program to add some code to use it. I copied the original start.py to moveRye.py which you can download from here

#!/usr/bin/env python

from Tkinter import *

import ryepics 1

def RyeSetup():
    global cnvs 2
    global ryeId 3
    # GUI initialisation
    ryewin.title('Raspberry Rye')
    ryewin.grid()
    # create the canvas for the game
    cnvs = Canvas(ryewin, height=300, width=300)
    cnvs.grid(column=0, row=0, columnspan=4)
   
    # draw Rye at position 2,2 4
    sx=2*20+10
    sy=2*20+10
    ryeId=cnvs.create_image(sx,sy,image=imageList['G']) 5
   
    cnvs.bind("<Key-Down>", ryeDown) 6
    cnvs.bind("<Key-Up>", ryeUp)
    cnvs.bind("<Key-Left>", ryeLeft)
    cnvs.bind("<Key-Right>", ryeRight)
   
    # set the canvas to receive the focus, so keypresses are captured
    cnvs.focus_set() 7

def ryeDown(event): 8
    cnvs.move(ryeId, 0, 20) 9

def ryeUp(event):
    cnvs.move(ryeId, 0, -20)

def ryeLeft(event):
    cnvs.move(ryeId, -20, 0)

def ryeRight(event):
    cnvs.move(ryeId, 20, 0)

if __name__ == "__main__":
    ryewin=Tk()
    imageList=ryepics.createImages() 10
    ryeId=0 11
    cnvs=0 12
    RyeSetup()
    ryewin.mainloop()


We need to import the ryepics module we have just created. Python will find the module if it is in the same folder as the importing program. We import it in line 1. Lines 2 and 3 are to overcome the issue that is we want to share data across functions we need to make the variables global. This is a horrible fudge which we will address later, but for now it keeps things simple.

To display the image on the canvas we created last time we need to decide where we want it. I made the blob an object 20x20 pixels, and everything will be based on this scaling, so to use 'Rye coordinates', based on 20x20 objects I will need to multiply any position by 20. Images are also placed by their centre by default. To define where we want to place the Rye blob 4 we need to multiply the x & y position by 20 and add 10 (half of 20) to get the centre. We then use the canvas create_image method to place the blob in place. We use the imageList with the key 'G' to display the blob 5.  When we create an object on the canvas it returns an id which we can use later to refer to that object. We call it ryeId and because we want to use it elsewhere it is one of the variables we declared as global (that fudge again).

Last time we said that GUI programming is event-driven. So let's use respond to some events. The four lines at 6 declare some events to respond to. We are binding a keyboard press on the canvas to a function. So when the down arrow key is pressed the function called ryeDown will be called and similarly for the other three arrow keys. To make sure that the canvas has the focus to receive any keypresses we force the canvas to have the focus on line 7.

To respond to the events we define four functions; these are sometimes called event handlers. These functions need a parameter (we call it event) which has extra information in it. We ignore it, but we must declare it, as in line 8. Line 9 shows we can move a canvas object when we know its id. We move it down 20 pixels and in the other event handlers up, left or right.

So now we come to how the imageList we created in the separate module is made available here. In line 10 we call the createImage() function in the external module and store the dictionary it returns as imageList.

Lines 11 & 12 are there just to create the fudge of global variables that can be shared by the functions.

If you run the program ./moveRye.py in a terminal, as before, you should see a window with a green blob that moves in response to the arrow keys.

Saturday 23 June 2012

Making a start (2)

The last post created a small piece of code, which you can download from here. It displays a simple window, but it needs explaining. Here's the code again, but with some more explanation:

#!/usr/bin/env python 1

from Tkinter import * 2

def RyeSetup(): 3

    # GUI initialisation 4
    ryewin.title('Raspberry Rye') 5
    ryewin.grid() 6

    # create the canvas for the game 7
    cnvs = Canvas(ryewin, height=300, width=300) 8
    cnvs.grid(column=0, row=0) 9


if __name__ == "__main__": 10
    ryewin=Tk() 11
    RyeSetup() 12
    ryewin.mainloop() 13

The first line 1 is something that often appears in Linux script. It's know as a shebang. Linux uses it to find the interpreter that runs the script, in this case Python. All of your Python scripts should begin with this.

Line 2 is how Python knows about extensions to the scripting language. The import statement looks for external modules of code and makes them available to the program. An external module (Tkinter in this case) can have many functions in it and you may choose to only import some of them by name or, as in this case, import them all with '*'. We'll use some this later.

Line 3 defines the beginning of a function called RyeSetup. The function has no parameters but we still need the parentheses () to show this. The function is marked with a colon to show where the function starts. Everything in the function must be indented by the same amount to group the statements together. Lines 4 to 8 are in the function called RyeSetup(). Nothing in the function will get run yet, it is just being defined.

Line 4 is a comment. Everything after a # on a line is ignored as a comment. Line 7 is a comment too.

Line 5 sets the title of the window and line 6 initialises a grid in the window, but what window? Well it is created elsewhere, remember this code is being defined, not run yet. The grid is a very important part of the way the things on the window look and behave, but more of that later.

Line 8 creates a Canvas. A canvas is something the program can draw on, which is where we will draw the elements of the game in later stages. It has a single parameter we must pass  and two optional parameters. The required parameter is the window that it belongs to - your program could display multiple windows. We pass ryewin - this mysterious window we haven't yet defines. We also pass height and width, both are in pixels and define the size of the canvas on the screen. We call the Canvas cnvs. Canvas is one of the things that we can use because with imported the Tkinter module.

Next, in line 9, we add the canvas to the grid for the window in row 0, column 0. Later when we define a some more things on the window we will position them using this grid.

That was the end of the ryewin() function. Now we get to the program that gets run when the script is started. Line 10 is a horrible looking line that is fairly common in Python programs. It is used to find out if a piece of code is being run or loaded as a module. Basically it is an if statement and if it is True (and it will be) the indented code that follows the colon will be run. I could have missed this line out - the program would generally work, but you will often see it so I left it in. The indents are just like the ones needed for the function - they

Line 11 creates the window we are going to use. It looks simple, and indeed it is simple to use, but it does a lot of work behind the scenes. It is another function that is included in the Tkinter module. We create an object called ryewin which is returned from the function Tk(). This we will use as the reference to our window. We have created it outside of any functions so it is a global variable and therefore accessible from anywhere in the program. Creating and using global variables is often frowned on, but a small number of key ones works well.

Now we have created our window we need to set up its looks and the things in it, so we call the function we defined earlier, Ryesetup(), in line 12 which we now know will add a title and a grid and add a canvas to the grid.

Lastly in line 13 we call a function mainloop(). Strictly this is a method of the ryewin object, but what does it do? Well it processes events for the window. The way GUI programs work is called event-driven. The mainloop() method displays the window and loops around waiting for any events to happen. We haven't defined any yet, so only a few built-in ones will work. We can move the window around the screen, minimise it, change its size and finally close it. All of these create events that have default responses. We can add our own events to respond to mouse events and keyboard events and other things too. We'll make use of some of these another time.

Getting going with GUI programming is a little slow, but the bulk of the work is done for you with Tkinter and now progress can be a bit more visible, as we'll see next time.

Thursday 21 June 2012

Making a start

We want to run some code using Tkinter, this is not installed on R-Pi by default, but it is easy to add if your R-Pi is connected to the internet. Login as normal and at the command prompt type

sudo apt-get install python-tk

This will install the extension to python.

To start creating a program that runs on R-Pi in a GUI way, we first have to enter that GUI environment. When you power up the R-Pi you get prompted to login and then get the Linux command prompt. If you type startx at that prompt the GUI environment of X-Windows will start.

We need to create the code of a program and working in the GUI environment means we can edit it and test it easily. There are various editors available and one that is available in Debian is Geany. What we are going to write is a Python program and Geany helps a little by understanding the way Python should look. To get started we need to create an folder to store our program in. If you click the File Manager on the bar near the bottom left. Hover the mouse over the icons for a hint about what they are. It will open up in the home folder for the user you are logged in as. Right-click in the window and create a new folder called python. Double click on that to open it and create another folder called rye. Notice that I'm using lower-case folder names. You can use mixed or upper-case names, but file and folder names in Linux are case sensitive, so folder Python is different from folder python. Open the folder rye, double-click and this time create a blank file called start.py.

If you right-click this blank file you can look at its properties from the pop-up menu. There is something we need to change. On the Permissions tab you need to check the setting 'Make the file executable'. This will allow us to run our typed code as a program.

If you right-click the file and select Geany from the menu, Geany should start with the empty file in it. type the following code and save it.

#!/usr/bin/env python

from Tkinter import *

def RyeSetup():

    # GUI initialisation
    ryewin.title('Raspberry Rye')
    ryewin.grid()

    # create the canvas for the game 
    cnvs = Canvas(ryewin, height=300, width=300)
    cnvs.grid(column=0, row=0, columnspan=4)


if __name__ == "__main__":
    ryewin=Tk()
    RyeSetup()
    ryewin.mainloop()

This is a simple GUI program in Python that doesn't do much, but does give us a start. The indenting is vital with python. Use the tab key to create the indents carefully as shown. The case and spelling of everything here is important too. Every part must be exactly as you see it above.

To run this program, go back to the File Manager window and from the Tools menu select 'Open current folder in terminal'. We are going to run the program from a terminal session, because any errors will display on the terminal. Later we can add debugging text to display on the terminal too. To run the program type ./start.py . You need to put the ./ in front of the filename because Linux only looks in its PATH to find programs and the folder we created will not be on the PATH. You can redo a previous command in the terminal by pressing up arrow. You can edit the line by inserting or deleting characters before pressing return. If you have typed everything as shown you should see a grey window appear with the title bar at the top displaying Raspberry Rye. You should be able to move the window, resize it and close it, which is not bad for a few lines of code.

That is enough for now. In the next post I'll explain what the code does and we will start to display something more useful. If you use this or have any problems please add a comment.

Full steam ahead

I've been doing some experiments with creating the Kye game look-alike Rye. I wondered how the performance of R-Pi would stand up to running a simple game like Rye in Python. My conclusion is that R-Pi hardly notices as lots of blocks zoom around the screen. Any worries about performance are behind me, now I just need to finish the workings of the game.

Thursday 7 June 2012

Rye for R-Pi

I've been thinking about the sort of programming that might be interesting on Raspberry Pi. Anyone who uses Windows just expects a GUI interface and so anyone coming to R-Pi from Windows will expect this too. The scripting language being pushed forward is Python, something I'm happy about, it really is a pleasant language to use. Python has more than one way to create a GUI program - I chose Tkinter.

GUI programming is a bit harder than just a terminal text-based program, but not too hard. I've been working on a program that will be a bit of fun to use. It is based on a program from about twenty years ago called Kye. It is a puzzle game to move Kye, a blob, around the level, staying away from the beasts and gather the prizes. You can see more about the original Kye here.

I'll hopefully create a series of posts describing the stages to get something displayed and more and more stuff working towards a fun game. We'll see how far and how fast I get on. I hope this will be interesting to anyone wanting to write Python GUI code. As a spin off I'll get a version of Kye that doesn't need Windows. I've decided to call my version Rye.

Tuesday 29 May 2012

Pi but not on TV

Raspberry Pi has an HDMI socket and a composite socket, both can be used to connect up to a TV, but what if your TV is being used by the rest of the family? Well if you connect R-Pi to Ethernet you can use another computer to take control of your R-Pi.
When you first start R-Pi running Debian it presents you with a login prompt and a terminal session. You can start a similar remote session using Secure Shell, ssh. You need to run the ssh server on R-Pi and then use a ssh client on the monitoring computer.
On the SD card the boot partition has a file called boot_enable_ssh.rc. If you rename the file to boot.rc when the R-Pi boots it will start the ssh server automatically. You now need to know what the IP address is of the R-Pi. By default R-Pi connects to a network by getting its IP address from DHCP. The Linux command nmap will show you devices within a range on my home network I use nmap -sP 192.168.0.0/24 which pings addresses within the range and reports devices it finds. This should help you find the address of your R-Pi. To connect to R-Pi from a Linux terminal use ssh pi@192.168.0.2 (substitute the address of your R-Pi) and you should see a prompt for the password for the user pi. You can now run any command just as normal. To connect from Windows (or Linux) you could use PuTTY as your ssh client, which does work well.
The next issue is to see the actual desktop of R-Pi. This would let you use the graphical interface remotely. The usual way is to use VNC. To run a VNC server on the R-Pi you need to install it, using the command sudo apt-get install tightvncserver. This will fetch the tightvncserver software from a repository and install it.
The command to start the VNC server is
vncserver :1 -geometry 1024x768 -depth 16 -pixelformat rgb565
The first time you run this you will be prompted to set access passwords. Once it is running you can now connect from a PC to see the GUI interface. The :1 above sets the port to use, the screen size, colour depth and pixel format are all set, you can change these to suit your needs and your monitor size. To access the screen you need to run a VNC client, I just use the Remote Desktop Viewer and use its VNC protocol. Supply the IP address and port (e.g. 192.168.0.2:1) and enter the password you chose above. You should see the R-Pi GUI screen without hogging the TV.

Making a disk image

In my last post I glossed over the process of creating a disk image. This is not too hard, but it is not what most computer users might expect. In the world of desktop or laptop PCs if you want to install the operating system from scratch you probably now start with a CD, DVD or a USB stick, boot from that media and it will prompt you to create the disk image on the hard disk that the computer normally boots from. Raspberry Pi is different.

I normally use Linux - usually Ubuntu which is a derivative of Debian Linux. Debian Linux is available for R-Pi, so that was my first choice. R-Pi loads from an SD card, so the operating system needs to be installed on an SD card. This needs to be 2GB at least, but that would not leave much free space for any files that need to be added, so I use a 8GB SD card. I decided to use the command line tools in Linux which give a step by step view of the process.

The disk images for a few different set ups have been created and they can be found on the downloads page of the Raspberry Pi web site. From there I downloaded the Debian "squeeze" image which was debian6-19-04-2012.zip when I looked. Also published there is the SHA-1 hash for the .zip file. To check that the file you have downloaded is intact run

sha1sum <filename>

and compare the resulting hash against the hash on the web site. If they match the downloaded file should be fine.

Windows gives every logical disk drive a drive letter, Linux doesn't. When a removable drive is inserted it gets mounted as part of the directory structure, in Ubuntu's case as a folder in the /media folder. The name is logical, but not always obvious. The command df displays the file systems available, run it without the SD card inserted and then with the SD card inserted to see the name of the device. If there are multiple partitions already on the SD card df shows them with a number at the end. In my case the SD card was device /media/sdb (ignoring the numbers).

I then took the .img file from out of the zip file that was downloaded, placed it into the raspi folder under my home and knowing what the SD device is called I could copy it to the SD card with the command:

sudo dd bs=1M if=~/raspi/debian6-19-04-2012.img of=/dev/sdb

It is important that I got the device name right. If I had copied the image file over an existing disk partition, it would have been wiped out of course. If you do this, be careful.

After a few minutes the command finished and I had partitions on the SD card, but if I use that card in R-Pi it would only see a 2GB disk, and ignore the remaining 6GB. So now I needed to extend one partition to fill the remaining space on  the card. I started by using parted to see what the partitions on the card looked like. You need to have the card inserted in the card reader (or SD slot) but not mounted. I ran

sudo parted /dev/sdb

which starts parted. I use the unit chs (cylinders, heads, sectors) view and then print which showed three partitions, a FAT boot partition, which I left alone, the main ext4 partition which was nearly 2GB and a swap partition. I wanted to extend the main ext4 partition so first I needed to move the swap partition to the end of the space to make room. The exact numbers I used would vary on different cards as the size of storage varies from card type to card type. The command I had to issue was move 3 118223,0,0. This moved the third  partition (the swap) to the end of the available space. Now the ext4 partition needed to be resized, so two commands to remove and recreate the partition are needed. This does not delete the data, just alters some pointers that a later step could use. The commands were rm 2 followed by mkpart primary 1232,0,0 118222,3,31 which used the whole space for the ext4 partition. The start point was the same as before but the end point was just before the newly moved swap partition. I quit parted. To check the partition was sound I ran

sudo e2fsck -f /dev/sdb2

This prompted me to make lost+found and I accepted that.  The last step was to actually extend the partition with

sudo resize2fs /dev/sdb2

When this had run I had an SD card with a 7.7GB ext4 partition with over 6GB free space to save any data on.

When I inserted a card made like this into R-Pi the first time it booted it did some one-time changes and had to be restarted but then it worked well.

Making all of these changes on the command line will seem alien to Windows users who have been coaxed away from typing commands, but it makes more sense the more you do it. Describing, repeating and supporting command line style working is easier and often opens up more options. I do think the visualisation of a GUI interface would be easier to use for the moving of the SD card partitions, and of course they are available.

Monday 28 May 2012

The wait is over

I ordered a Raspberry Pi shortly after they became available and it has arrived less than three months later.

If you don't know what I'm talking about then you might like to take a look at the Raspberry Pi FAQ page, but Raspberry Pi is a small, self-contained computer that runs an ARM processor and costs less than £30 delivered. When I opened the envelope there was a tee shirt (which I'd forgotten about) and a small box about the size of a smart phone which contained a circuit board wrapped in anti-static plastic.

It has sockets and connectors all over it, around the edges are a mini-usb socket to power it, two USB sockets for USB things like a keyboard, a socket for an SD card, an ethernet RJ45 socket, an HDMI socket to connect it to a TV or monitor, a composite socket which would be an alternative way to use an older TV as a display and a 3.5mm jack for sound. On the top are general purpose IO (GPIO) pins. These are interesting, but not just yet.

I had created a boot disk by downloading a disk image from the Raspberry Pi web site. I had plenty of time while I was waiting. The disk image gets written to an SD card which is how the Pi boots. Once it has booted it is possible to use extra drives connected to USB, which can be extended with a USB hub. The disk image is a Debian distribution of Linux, specifically created and compiled to run on the ARM processor.

I plugged in the SD card, a USB keyboard, an HDMI cable to the TV and then plugged a phone charger for power. The TV leapt into life and a series of text scrolled by. Instead of the expected login prompt I got a Kernel Panic. After a second go with the same result I unplugged the keyboard and the Pi booted, but I couldn't login without a keyboard. When I plugged it in the Pi crashed. Mike McLennan pointed me, via twitter, to the list of keyboards that work and don't and sure enough mine was in the problem list.

I bought a cheap keyboard from Tesco today, and now all is well. Now I need to look at what is there and what I can do with it.