Ejemplo n.º 1
0
class PyInterpreter(Singleton):
  isInit = False
  bExitOnTouchPrev = True
  outputWidth = W
  prevMode = ""
  currentMode = "" # console, tutorial, editor
  emptyWidget = Widget(size_hint=(None,None), size=(0,0))
  
  def init(self):
    if self.isInit:
      return

    # add screens
    self.screen = Screen(name=szConsole)
    self.oldScreen = None
    self.oldTouchPrev = None
    self.editorLayout = EditorLayout(self)
    self.tutorialLayout = TutorialLayout(self)
    self.fileBrowser = FileBrowser.instance()
    
    self.repl = REPL(self)
    self.history = ["",]
    self.historyIndex = -1
    self.historyCount = 100
    self.lastIndentSpace = ""
    self.bIndentMode = False
    self.reFocusInputText = False
    self.oldTouchPrev = None
    self.textInputWidth = W * (4.0/5.0)
    
    # console menu layout
    self.consoleMenuLayout = BoxLayout(orientation="horizontal", size_hint=(1, None), height="35dp")
    btn_clear = Button(text="Clear", background_color=darkGray)
    btn_clear.bind(on_release = lambda inst:self.clearOutput())
    btn_prev = Button(text="<<", background_color=darkGray)
    btn_next = Button(text=">>", background_color=darkGray)
    self.consoleMenuLayout.add_widget(btn_clear)
    self.consoleMenuLayout.add_widget(btn_prev)
    self.consoleMenuLayout.add_widget(btn_next)
    self.screen.add_widget(self.consoleMenuLayout)
    
    # screen menu layout
    self.screenMenuLayout = BoxLayout(orientation="horizontal", size_hint=(1, None), height="35dp", pos=(0,0))
    btn_console = Button(text="Console", background_color=[1.5,0.8,0.8,2]) 
    btn_editor = Button(text="Code Editor", background_color=[0.8,1.5,0.8,2])
    btn_tutorial = Button(text="Python Tutotial", background_color=[0.8,0.8,1.5,2])
    btn_editor.bind(on_release=lambda inst:self.setMode(szEditor))
    btn_tutorial.bind(on_release=lambda inst:self.setMode(szTutorial))
    self.screenMenuLayout.add_widget(btn_console)
    self.screenMenuLayout.add_widget(btn_editor)
    self.screenMenuLayout.add_widget(btn_tutorial)
    self.screen.add_widget(self.screenMenuLayout)
    
    # text input
    self.consoleInput = TextInput(text = "text", multiline=False, size_hint=(None, None), auto_indent = True, font_name=defaultFont, 
      background_color=(.1, .1, .1, 1), foreground_color=(1,1,1,1), text_size=(0,0), font_size="14dp", padding_x="20dp", padding_y="15dp")  
    self.consoleInput.size = (W, self.consoleInput.minimum_height)
    self.consoleInput.text = ""
  
    self.consoleInput.bind(on_text_validate = lambda inst:self.onConsoleInput(inst, False, False))
    self.consoleInput.bind(focus = self.inputBoxFocus)
    
    # textinput scroll view
    self.textInputSV = ScrollView(size_hint=(None, None), size = (W, self.consoleInput.minimum_height))
    self.textInputSV.scroll_y = 0
    self.textInputSV.add_widget(self.consoleInput)
    
    # add input widget
    self.screen.add_widget(self.textInputSV)
    
    # run button
    self.btn_run = Button(text="Run", size_hint=(None,None), size =(W-self.consoleInput.size[0], self.consoleInput.size[1]),\
      background_color=(1.3,1.3,2,2))
    self.btn_run.bind(on_release = lambda inst:self.onConsoleInput(self.consoleInput, True, False))
    self.btn_run.pos = (W - self.btn_run.size[0], 0)
    self.screen.add_widget(self.btn_run)
      
    # output
    self.outputSV = ScrollView(size_hint=(None, None))
    self.screen.add_widget(self.outputSV)
    
    self.outputLayout = BoxLayout(orientation="vertical", size_hint=(None,None))
    self.outputSV.add_widget(self.outputLayout)
    
    def func_prev(inst):         
      if len(self.history) > 0:
        self.historyIndex -= 1
        if self.historyIndex < 0:
          self.historyIndex = len(self.history) - 1
        text = self.history[self.historyIndex]
        if text.find("\n") > -1:
          self.bIndentMode = True
        self.setInputText(text)
    btn_prev.bind(on_release = func_prev)
    
    def func_next(inst):
      if len(self.history) > 0:
        self.historyIndex += 1
        if self.historyIndex >= len(self.history):
          self.historyIndex = 0
        text = self.history[self.historyIndex]
        if text.find("\n") > -1:
          self.bIndentMode = True
        self.setInputText(text)
    btn_next.bind(on_release = func_next)    

    # show output layout
    self.displayText("Python " + sys.version.strip(), 0)
    
    self.repl.run_script('input=InputPopup.instance().show', False)
    
    self.isInit = True
    
  def setMode(self, mode):
    if mode == self.currentMode:
      return
      
    self.prevMode = self.currentMode
    self.currentMode = mode
    
    # restore..
    if self.prevMode == szConsole:
      self.reFocusInputText = False
      self.inputBoxForceFocus(False)
    
     # set mode layout  
    if mode == szConsole:
      gMyRoot.current_screen(self.screen)
      self.refreshLayout()
    elif mode == szEditor:
      gMyRoot.current_screen(self.editorLayout.screen)
    elif mode == szTutorial:
      gMyRoot.current_screen(self.tutorialLayout.screen)
    elif mode == szFileBrowserOpen:
      self.fileBrowser.showOpenLayout(self)
    elif mode == szFileBrowserSaveAs:
      self.fileBrowser.showSaveAsLayout(self)
      
  def callback_open_file(self, filename):
    self.editorLayout.open_file(filename)
    
  def callback_save_as(self, filename):
    self.editorLayout.save_as(filename)
    
  def callback_filebrowser_closed(self):
    self.setMode(szEditor)
    
  def setExitOnTouchPrev(self, bValue):
    self.bExitOnTouchPrev = bValue
  
  def start(self):
    self.show()
    
  def update(self, dt):
    pass
  
  def clearOutput(self):
      self.outputLayout.clear_widgets()
      self.outputLayout.size = (W, 0)
      
  def outputLayout_add_widget(self, widget):
    self.outputLayout.add_widget(widget)
    self.outputLayout.height += widget.height + widget.padding[1] + widget.padding[3]

  def displayText(self, text, scroll_y, background_color=(1,1,1,0)):
    if type(text) != str:
      text = str(text)
       
    output = TextInput(markup = True, text="", halign='left', valign='top', readonly=True, font_size="12dp", font_name = defaultFont,
      multiline=True, background_color=background_color, foreground_color=(1,1,1,1), size_hint=(None,None), size = (self.outputWidth, 0))
    output.text = text
    output.size = (self.outputWidth, output.minimum_height)
    
    self.outputLayout.add_widget(output)
    self.outputLayout.size = (self.outputWidth, self.outputLayout.size[1] + output.size[1])
    self.outputSV.scroll_x = 0
    self.outputSV.scroll_y = scroll_y
    
  def onConsoleInput(self, inst, bForceRun, resetGlobals):
    self.reFocusInputText = True
    if inst.text.strip():
      bRunCode = len(inst.text) == self.consoleInput.cursor_index()
      lastLine_nonStrip = inst.text.split("\n")[-1]
      lastLine = lastLine_nonStrip.strip()
      # indent mode - continue input but not run
      if not bForceRun and lastLine and (lastLine[-1] in ("\\", ":") or self.bIndentMode or not bRunCode):
        self.bIndentMode = True
        # get indent space
        self.lastIndentSpace = ""
        if self.bIndentMode:
          for i in lastLine_nonStrip:
            if i in [" ", "\t"]:
              self.lastIndentSpace += i
            else:
              break
        inst.insert_text("\n" + self.lastIndentSpace)
        self.consoleInput.size = (W, self.consoleInput.minimum_height)
        return
      # check indent mode - run code
      self.bIndentMode = False
      # pop input text from history
      if len(self.history) > 0 and self.historyIndex > -1 \
        and self.historyIndex < len(self.history) and self.history[self.historyIndex] == inst.text:
          self.history.pop(self.historyIndex)
      # append item to history
      if self.currentMode == szConsole:
        self.history.append(inst.text.strip())
      # check history count
      if len(self.history) > self.historyCount:
        self.history = self.history[:self.historyCount]
      self.historyIndex = len(self.history)
      # display input text to output widget
      if self.currentMode == szConsole:
        lines = inst.text.split("\n")
        result = []
        for i, line in enumerate(lines):
          line = (">>> " if i == 0 else "... ") + line
          result.append(line)
        result = "\n".join(result)
        self.displayText(result, 0)
      elif self.currentMode == szEditor:
        currentDocument = ">>> Run Untitled.py"
        self.displayText("\n".join(["-" * len(currentDocument), currentDocument + " - " + time.ctime()]), 0)
      # run code
      self.repl.onConsoleInput(inst.text, resetGlobals)
      # end message
      if self.currentMode == szEditor:
        self.displayText("Done.", 0)
        
      # clear text input
      if self.currentMode == szConsole:
        self.setInputText("")
      
  def refreshLayout(self):
    if self.currentMode == szConsole:  
      keyboardHeight = gMyRoot.getKeyboardHeight() if self.consoleInput.focus else 0
      limitHeight = (H - topMargin - keyboardHeight) * 0.5
      if self.consoleInput.minimum_height > limitHeight:
        height = limitHeight
      else:
        height = self.consoleInput.minimum_height
      self.screenMenuLayout.pos = (0, keyboardHeight)
      self.textInputSV.pos = (0, self.screenMenuLayout.top)
      self.textInputSV.size = (self.textInputWidth, height)
      self.consoleInput.size = (self.textInputWidth, self.consoleInput.minimum_height)
      self.btn_run.pos = (self.textInputSV.size[0], self.textInputSV.pos[1])
      self.btn_run.size = (W - self.textInputSV.size[0], self.textInputSV.size[1])
      self.consoleMenuLayout.pos = (0, self.textInputSV.top)
      self.outputSV.pos = (0, self.consoleMenuLayout.top)
      self.outputSV.size = (W, H - self.outputSV.pos[1] - topMargin)
      
  def setInputText(self, text):
    self.consoleInput.text = text
    if self.consoleInput.size[1] != self.consoleInput.minimum_height:
      self.refreshLayout()
  
  def insertInputText(self, text):
    self.consoleInput.insert_text(text)
    if self.consoleInput.size[1] != self.consoleInput.minimum_height:
      self.refreshLayout()
  
  def inputBoxForceFocus(self, bFocus):
    if bFocus != self.consoleInput.focus:
      self.consoleInput.focus = bFocus 
      
  def inputBoxFocus(self, inst, bFocus):
    bAlwaysPreserveFocus = False
    if not bFocus:
      if self.reFocusInputText:
        self.reFocusInputText = bAlwaysPreserveFocus
        inst.focus = True
    self.reFocusInputText = bAlwaysPreserveFocus
    self.refreshLayout()
    
  def update(self, dt):
    pass
    
  def touchPrev(self):
    if self.currentMode == szEditor:
      self.editorLayout.touchPrev()
    elif self.currentMode in (szFileBrowserOpen, szFileBrowserSaveAs):
      self.fileBrowser.touchPrev()
    elif self.currentMode == szTutorial:
      self.tutorialLayout.touchPrev()
    elif self.consoleInput.focus:
      self.reFocusInputText = False
      self.inputBoxForceFocus(False)
    else:
      self.exit()
      
  def toggle(self):
    if not self.isInit:
      self.init()
      
    if self.screen.name == gMyRoot.get_current_screen():
      self.close()
    else:
      self.show()
      
  def show(self):
    if not self.isInit:
      self.init()
    
    if self.oldScreen == None:
      self.oldScreen = gMyRoot.get_current_screen()
      self.oldTouchPrev = gMyRoot.getTouchPrev()
      
    gMyRoot.add_screen(self.screen)
    gMyRoot.add_screen(self.editorLayout.screen)
    gMyRoot.add_screen(self.tutorialLayout.screen)
    gMyRoot.current_screen(self.screen)
    gMyRoot.setTouchPrev(self.touchPrev)
    self.setMode(szConsole)
  
  def close(self):
    gMyRoot.setTouchPrev(self.oldTouchPrev)
    gMyRoot.remove(self)
    gMyRoot.remove_screen(self.screen)
    gMyRoot.remove_screen(self.editorLayout.screen)
    gMyRoot.remove_screen(self.tutorialLayout.screen)
    self.editorLayout.exit()
    if self.bExitOnTouchPrev:
      gMyRoot.exit()
    elif self.oldScreen:
      gMyRoot.current_screen(self.oldScreen)
    self.oldScreen = None
        
  def exit(self, *args):
    self.reFocusInputText = False
    self.inputBoxForceFocus(False)
    if self.bExitOnTouchPrev:
      gMyRoot.popup("Exit Python?", "", self.close, None)
    else:
      self.close()