#!/usr/bin/env python
# -*- coding: latin-1 -*-
""" RUR-PLE: Roberge's Used Robot - a Python Learning Environment
rur_start.py - "Main" file for RUR-PLE.
Version 1.0
Author: Andr Roberge Copyright 2006
andre.roberge@gmail.com
"""
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# todo: consider adding support for translation of commands e.g.
# todo: ... francais() initialises commands equivalent like avance(), etc.
# todo: create easy install for windows (exe file)
# todo: create easy install for linux (setup.py...)
# todo: consider adding new page in notebook for turtle graphics.
import os
import sys
import wx
import wx.lib.buttons
import rur_py.misc as misc # a few global variables
if __name__ == "__main__": # ensures that all wxPython stuff is loaded properly
App = wx.PySimpleApp(0) # (1) redirects print output to window;
# (0) to terminal
cur_path = os.path.dirname(sys.argv[0])
if cur_path != '':
os.chdir(cur_path)
misc.HOME = os.getcwd() # sets up path for later use
#
os.chdir("rur_images")
misc.IMAGE_DIR = os.getcwd()
os.chdir(misc.HOME)
#
os.chdir("lessons")
from rur_py.translation import language_code
os.chdir(language_code)
misc.HTML_DIR = os.getcwd()
os.chdir(misc.HOME)
#
os.chdir("rur_programs")
misc.PRGM_DIR = os.getcwd()
os.chdir(misc.HOME)
#
os.chdir("world_files")
misc.WORLD_DIR = os.getcwd()
os.chdir(misc.HOME)
#
os.chdir("python_files")
misc.PYTHON_DIR = os.getcwd()
os.chdir(misc.HOME)
#--- see end of file for completion of the start of the application
import wx.py as py # For the interpreter
import rur_py.images as images # load all images
import rur_py.dialogs as rD # contains various dialogs and exception classes
from rur_py.translation import _
from rur_py.sash import MySashWindow
from rur_py.lightning import EditorSashWindow
import rur_py.parser as parser
from rur_py.bouton import pythonChoiceWindow
code = "" # global variable defined for convenience; contains user program
from rur_py.cpu import rur_program
import rur_py.browser as browser
import rur_py.event_manager as event_manager
from rur_py.status_bar import rurStatusBar
class RURnotebook(wx.Notebook):
def __init__(self, parent):
wx.Notebook.__init__(self, parent, -1)
self.parent = parent
wx.EVT_NOTEBOOK_PAGE_CHANGED(self, -1, self.OnPageChanged)
def OnPageChanged(self, event):
arg = self.parent.status_bar.notebook_new_page, event.GetSelection()
event_manager.SendCustomEvent(self.parent, arg)
event.Skip()
class RURApp(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
_("RUR: a Python Learning Environment"),
size = (800, 660),
style=wx.DEFAULT_FRAME_STYLE)
self.raw_code = ""
self.filename = ""
self.world_filename = ""
self.isPaused = False
self.isRunning = False
self.isStepped = False
self.status_bar = rurStatusBar(self)
self.SetStatusBar(self.status_bar)
# icon on top left of window
icon = wx.EmptyIcon()
icon.CopyFromBitmap(images.ICON)
self.SetIcon(icon)
#
self.Show(True)
self.window = RURnotebook(self)
#
win = browser.TestHtmlPanel(self.window, self)
self.window.AddPage(win, _(" RUR: Read and Learn "))
#
self.sash = MySashWindow(self.window, self)
self.outputWindow = self.sash.output_window
self.window.AddPage(self.sash, _("Robot: Code and Learn"))
# See MySashWindow for the following 'shortcut' notation
self.world = self.WorldDisplay.world
# create a backup copy that will be used to "reset" the world
self.backup_dict = {}
self.backup_dict['avenues'] = self.world.av
self.backup_dict['streets'] = self.world.st
self.backup_dict['beepers'] = {}
self.backup_dict['walls'] = []
# add a robot to start
self.world.addOneRobot(name='robot')
self.backup_dict['robot'] = self.world.robot_dict[
'robot']._getInfoTuple()
# create user_program object as a two-step process: create Singleton
self.user_program = rur_program()
# binds attributes explicitly
self.user_program.myInit(self, self.world, self.WorldDisplay,
self.ProgramEditor, self.world.robot_dict)
# We will then be able to "link" to the one rur_program() without
# having to worry about changing attributes
# recreate the image so that the robot shows up.
self.world.DoDrawing()
self.WorldDisplay.drawImage()
self.WorldDisplay.Refresh()
#
win = py.shell.Shell(self.window, -1,
introText = "")
self.window.AddPage(win, _("Python: Code and Learn"))
self.sash2 = EditorSashWindow(self.window, grand_parent=self,
controller=pythonChoiceWindow,
top_control=True, top_control_height=40)
# 40 = bouton "." btn_size[1] + 8
self.window.AddPage(self.sash2, _("Python: simple editor"))
self.SetSize((900, 660))
self.window.SetFocus()
self.SendSizeEvent() # added to attempt to solve problem on MacOS
wx.EVT_CLOSE(self, self.OnClose)
def OnClose(self, event):
if self.ProgramEditor.GetModify():
rD.rurMessageDialog(
_("Robot program has changed.\nYou may save it before quitting."),
_("Program has been changed"))
self.SaveProgramFile(event)
# in case the user does not save the program, make it appear
# as though it has been saved so that the next attempt to
# quit will work.
self.ProgramEditor.SetSavePoint()
else:
self.Destroy()
#---- World file methods
def OpenWorldFile(self, dummy):
if self.isRunning:
return
wildcard = _("World files (*.wld)|*.wld| All files (*.*)|*.*")
dlg = wx.FileDialog(self, _("Choose a file"), misc.WORLD_DIR, "",
wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
self.world_filename = dlg.GetPath()
self.ReadWorldFile()
self.UpdateWorld()
self.user_program.clear_trace()
misc.WORLD_DIR = os.path.dirname(self.world_filename)
arg = self.status_bar.world_field, \
os.path.basename(self.world_filename)
event_manager.SendCustomEvent(self, arg)
dlg.Destroy()
def ReadWorldFile(self):
if self.isRunning:
return
txt = open(self.world_filename, 'r').read()
txt = parser.FixLineEnding(txt)
flag = parser.ParseWorld(txt)
if flag:
self.backup_dict = {} # used to 'reset' the world
exec txt in self.backup_dict # extracts avenues, streets, robot,
# walls and beepers
def ResetWorld(self, dummy):
if self.isRunning:
return
self.UpdateWorld()
def UpdateWorld(self):
try:
av = self.backup_dict['avenues']
except:
rD.rurMessageDialog(
_("Problem with %s\nPlease recreate world file.")%
_("avenues"),
_("Invalid world file format"))
return
try:
st = self.backup_dict['streets']
except:
rD.rurMessageDialog(
_("Problem with %s\nPlease recreate world file.")%
_("streets"),
_("Invalid world file format"))
return
self.world.resetDimensions(av, st)
try:
if 'robot' in self.backup_dict:
x, y, key, beep = self.backup_dict['robot']
arg = self.status_bar.beeper_field, beep
event_manager.SendCustomEvent(self, arg)
except:
rD.rurMessageDialog(
_("Problem with %s\nPlease recreate world file.")%
_("robot"),
_("Invalid world file format"))
return
# We might be reloading the world, to which robots may have been added
# Remove all robots that have been added, if any;
# recall that "named" robots are added through a user-defined program,
# not in a world file.
self.world.robot_dict = {}
# Recreate the robot that was in the original file
if 'robot' in self.backup_dict:
self.world.addOneRobot(x, y, key, beep, name='robot')
# world characteristics
# note: we make shallow copies of rurApp.backup_dict as we
# may need it if we call ResetWorld().
try:
for corner in self.world.beepers_dict:
del self.world.beepers_dict[corner] # empty, but keep reference
for corner in self.backup_dict['beepers']:
self.world.beepers_dict[corner] = self.backup_dict[
'beepers'][corner]
except:
rD.rurMessageDialog(
_("Problem with %s\nPlease recreate world file.")%
_("beepers"),
_("Invalid world file format"))
return
# We need to keep one reference only to walls_list
try:
for col, row in self.world.walls_list:
self.world.walls_list.remove((col, row)) # empty, but keep ref.
for col, row in self.backup_dict['walls']:
self.world.walls_list.append((col, row))
except:
rD.rurMessageDialog(
_("Problem with %s\nPlease recreate world file.")%
_("walls"),
_("Invalid world file format"))
return
# prepare to recreate the background images
self.world.background_images_created = False
self.world.AdjustWorldSize()
self.world.InitTileSizes()
self.WorldDisplay.InitialiseVariables()
self.world.DoDrawing()
# create a new bitmap image
self.WorldDisplay.buffer = wx.EmptyBitmap(self.world.maxWidth,
self.world.maxHeight)
self.WorldDisplay.drawImage()
def SaveWorldFile(self, dummy):
if self.isRunning:
return
wildcard = _("World files (*.wld)|*.wld| All files (*.*)|*.*")
fname = os.path.basename(self.filename)
dlg = wx.FileDialog(self, _("Save new world as"), misc.WORLD_DIR,
fname, wildcard, wx.SAVE| wx.CHANGE_DIR )
if dlg.ShowModal() == wx.ID_OK:
self.world_filename = dlg.GetPath()
f = open(self.world_filename, 'w')
txt = self.WorldDisplay.UpdateEditor()
f.write(txt)
f.close()
arg = self.status_bar.world_field, \
os.path.basename(self.world_filename)
event_manager.SendCustomEvent(self, arg)
misc.WORLD_DIR = os.path.dirname(self.world_filename)
# save a backup copy to 'reset world'
self.backup_dict = {}
exec txt in self.backup_dict
dlg.Destroy()
#----- Program files methods
def OpenProgramFile(self, dummy):
if self.isRunning:
return
wildcard = _("Program files (*.rur)|*.rur| All files (*.*)|*.*")
dlg = wx.FileDialog(self, _("Choose a file"), misc.PRGM_DIR, "",
wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
global code
self.filename = dlg.GetPath()
arg = self.status_bar.program_field, \
os.path.basename(self.filename)
event_manager.SendCustomEvent(self, arg)
code = open(self.filename, 'r').read()
code = parser.FixLineEnding(code)
self.ProgramEditor.SetText(code)
no_error, mesg = parser.ParseProgram(code)
misc.PRGM_DIR = os.path.dirname(self.filename)
if no_error:
self.raw_code = code
self.ProgramEditor.SetSavePoint()
else:
code = ""
rD.rurMessageDialog(mesg, ("Program will not be used."))
dlg.Destroy()
def SaveProgramFile(self, dummy):
if self.isRunning:
return
global code
code = self.ProgramEditor.GetText()
no_error, mesg = parser.ParseProgram(code)
if no_error:
wildcard = _("Program files (*.rur)|*.rur| All files (*.*)|*.*")
fname = os.path.basename(self.filename)
dlg = wx.FileDialog(self, _("Save new program as"), misc.PRGM_DIR,
fname, wildcard, wx.SAVE| wx.CHANGE_DIR )
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetPath()
f = open(self.filename, 'w')
f.write(code)
f.close()
arg = self.status_bar.program_field, \
os.path.basename(self.filename)
event_manager.SendCustomEvent(self, arg)
code = parser.FixLineEnding(code)
misc.PRGM_DIR = os.path.dirname(self.filename)
dlg.Destroy()
self.ProgramEditor.SetSavePoint()
else:
code = ""
rD.rurMessageDialog(mesg, _("Program will not be saved."))
#--- Program controls
def RunProgram(self, dummy):
if self.user_program.isRunning:
if self.user_program.isPaused or self.user_program.isStepped:
self.user_program.isStepped = False
self.Pause(None)
return
self.user_program.isRunning = True
self.user_program.restart(self.world.robot_dict)
def Pause(self, dummy):
if not (self.user_program.isRunning or self.user_program.isStepped):
return
if self.user_program.isPaused:
self.user_program.isPaused = False
arg = self.status_bar.running_field, _("Program is running")
event_manager.SendCustomEvent(self, arg)
else:
self.user_program.isPaused = True
arg = self.status_bar.running_field, _("Program paused")
event_manager.SendCustomEvent(self, arg)
def Step(self, dummy):
self.user_program.isStepped = True
if not self.user_program.isRunning:
self.RunProgram(None)
else:
self.Pause(None)
def StopProgram(self, dummy):
self.user_program.StopProgram()
arg = self.status_bar.running_field, _("Program not running")
event_manager.SendCustomEvent(self, arg)
self.user_program.stopped_by_user = True
#--- World controls
def EditWalls(self, event):
if self.user_program.isRunning:
return
self.user_program.clear_trace()
if self.world.editWalls:
self.world.editWalls = False
self.world.DoDrawing()
else:
self.world.editWalls = True
self.world.DoDrawing()
self.WorldDisplay.drawImage()
self.WorldDisplay.Refresh()
def BeepersToRobot(self, dummy):
if self.user_program.isRunning:
return
self.user_program.clear_trace()
try:
dummy = self.backup_dict['robot']
except KeyError:
msg = _("No robot in world to give beepers to.")
rD.rurMessageDialog(msg, 'error')
return
rD.RobotBeeperDialog(self, -1, _("Beepers!"))
def BeepersUpdateStatusBar(self):
arg = self.status_bar.beeper_field, \
self.world.robot_dict['robot']._beeper_bag
event_manager.SendCustomEvent(self, arg)
# update the world window text at the same time
self.rightWindow.DestroyChildren() # removes the old wx.StaticText
wx.StaticText(self.rightWindow, -1,
self.WorldDisplay.UpdateEditor(), (10, 10))
def ResizeWorld(self, dummy):
if self.user_program.isRunning:
return
self.user_program.clear_trace()
rD.ResizeWorldDialog(self, -1, _("World size!"))
def ToggleHighlight(self, dummy):
if self.user_program.isRunning:
return
global code
if misc.line_number_flag:
misc.line_number_flag = False
code = self.raw_code
else:
misc.line_number_flag = True
code = parser.add_line_number_info(code)
def ToggleWorldWindow(self, dummy):
if self.user_program.isRunning:
return
if self.rightWindow.isVisible:
self.rightWindow.SetDefaultSize(wx.Size(0, 600))
self.rightWindow.isVisible = False
else:
self.rightWindow.SetDefaultSize(wx.Size(200, 600))
self.rightWindow.isVisible = True
wx.LayoutAlgorithm().LayoutWindow(self.sash, self.WorldDisplay)
self.rightWindow.DestroyChildren() # removes the old wx.StaticText
wx.StaticText(self.rightWindow, -1,
self.WorldDisplay.UpdateEditor(), (10, 10))
def AddRemoveRobot(self, dummy):
if self.user_program.isRunning:
return
if self.world.robot_dict:
# remove all robots from non-empty dict
self.world.robot_dict = {}
arg = self.status_bar.beeper_field, self.status_bar.no_robot
else:
self.world.robot_dict = {}
self.world.addOneRobot(name='robot')
self.backup_dict['robot'] = self.world.robot_dict[
'robot']._getInfoTuple()
arg = self.status_bar.beeper_field, \
self.world.robot_dict['robot']._beeper_bag
event_manager.SendCustomEvent(self, arg)
self.world.DoDrawing()
self.WorldDisplay.drawImage()
self.WorldDisplay.Refresh()
def load_images(self, event):
wildcard = "All files (*.*)|*.*"
dlg = wx.FileDialog(self, "Choose an image: robot facing South",
os.getcwd(), "", wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
self.fileSouth = dlg.GetPath()
dlg.Destroy()
dlg = wx.FileDialog(self, "Choose an image: robot facing North",
os.getcwd(), "", wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
self.fileNorth = dlg.GetPath()
dlg.Destroy()
dlg = wx.FileDialog(self, "Choose an image: robot facing East",
os.getcwd(), "", wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
self.fileEast = dlg.GetPath()
dlg.Destroy()
dlg = wx.FileDialog(self, "Choose an image: robot facing West",
os.getcwd(), "", wildcard, wx.OPEN | wx.CHANGE_DIR)
if dlg.ShowModal() == wx.ID_OK:
self.fileWest = dlg.GetPath()
dlg.Destroy()
image_south = images.GREY_ROBOT_S
image_north = images.GREY_ROBOT_N
image_east = images.GREY_ROBOT_E
image_west = images.GREY_ROBOT_W
try:
image_south = wx.Image(self.fileSouth).ConvertToBitmap()
image_north = wx.Image(self.fileNorth).ConvertToBitmap()
image_east = wx.Image(self.fileEast).ConvertToBitmap()
image_west = wx.Image(self.fileWest).ConvertToBitmap()
except Exception, info:
print "Conversion or loading problems: can not use new images."
print "info = %", info
images.GREY_ROBOT_S = image_south
images.GREY_ROBOT_N = image_north
images.GREY_ROBOT_E = image_east
images.GREY_ROBOT_W = image_west
self.AddRemoveRobot(event) # remove robot with old image
self.AddRemoveRobot(event) # and add new one
class MySplashScreen(wx.SplashScreen):
def __init__(self):
wx.SplashScreen.__init__(self, images.SPLASH_SCREEN,
wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 100, None, -1,
style = wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP)
wx.EVT_CLOSE(self, self.OnClose)
def OnClose(self, evt):
dummy = RURApp()
evt.Skip()
if __name__ == "__main__":
Splash = MySplashScreen()
Splash.Show()
App.MainLoop()
|