Example #1
0
    def __init__(self,  parent, global_vars=None):
        super(PyInterp,  self).__init__(parent)

        self.stdout_bak = sys.stdout
        self.stderr_bak = sys.stderr

        sys.stdout = self
        sys.stderr = self
        self.refreshMarker = False  # to change back to >>> from ...
        self.multiLine = False  # code spans more than one line
        self.command = ''    # command to be ran
        self.printBanner()              # print sys info
        self.marker()                   # make the >>> or ... marker
        self.history = []    # list of commands entered
        self.historyIndex = -1
        self.interpreterLocals = {}

        # self.last_cursor=None

        # setting the color for bg and text
        palette = QtGui.QPalette()
        text_color = QtGui.QColor(179, 179, 179)
        # highlight_color=self.palette().highlight().color()
        bg_color=QtGui.QColor(46, 46, 46)
        palette.setColor(QtGui.QPalette.Base, bg_color)
        palette.setColor(QtGui.QPalette.Text,text_color )

        # palette.setColor(QtGui.QPalette.Highlight, highlight_color)
        # palette.setColor(QtGui.QPalette.HighlightedText, bg_color)
        # set highlight color

        self.setPalette(palette)
        self.setFont(QtGui.QFont('DejaVu Sans Mono', 10))

        # save default format
        self.default_char_format = self.textCursor().charFormat()

        # save selected \
        self.selected_range=None

        # last cursor position
        self.last_cursor_pos=self.textCursor().position()

        # initilize interpreter with self locals
        
        if global_vars is None:
            global_vars = inspect.getouterframes(inspect.currentframe())[-1][0].f_globals
        self.initInterpreter(global_vars)

        from rlcompleter2 import Completer

        self.completer = Completer()

        # extend default menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.textChanged.connect(self.check_multiline)

        # set cursor shape
        # self.viewport().setCursor(Qt.PointingHandCursor)
        # self.setCursorWidth(10)
        self.cursorPositionChanged.connect(self.update_readonly)

        # disable rich text pasting
        self.setAcceptRichText(False)

        # clear undo
        self.document().clearUndoRedoStacks()
Example #2
0
    def __init__(self, parent, global_vars=None):
        super(PyInterp, self).__init__(parent)

        self.stdout_bak = sys.stdout
        self.stderr_bak = sys.stderr

        sys.stdout = self
        sys.stderr = self
        self.refreshMarker = False  # to change back to >>> from ...
        self.multiLine = False  # code spans more than one line
        self.command = ''  # command to be ran
        self.printBanner()  # print sys info
        self.marker()  # make the >>> or ... marker
        self.history = []  # list of commands entered
        self.historyIndex = -1
        self.interpreterLocals = {}

        # self.last_cursor=None

        # setting the color for bg and text
        palette = QtGui.QPalette()
        text_color = QtGui.QColor(179, 179, 179)
        # highlight_color=self.palette().highlight().color()
        bg_color = QtGui.QColor(46, 46, 46)
        palette.setColor(QtGui.QPalette.Base, bg_color)
        palette.setColor(QtGui.QPalette.Text, text_color)

        # palette.setColor(QtGui.QPalette.Highlight, highlight_color)
        # palette.setColor(QtGui.QPalette.HighlightedText, bg_color)
        # set highlight color

        self.setPalette(palette)
        self.setFont(QtGui.QFont('DejaVu Sans Mono', 10))

        # save default format
        self.default_char_format = self.textCursor().charFormat()

        # save selected \
        self.selected_range = None

        # last cursor position
        self.last_cursor_pos = self.textCursor().position()

        # initilize interpreter with self locals

        if global_vars is None:
            global_vars = inspect.getouterframes(
                inspect.currentframe())[-1][0].f_globals
        self.initInterpreter(global_vars)

        from rlcompleter2 import Completer

        self.completer = Completer()

        # extend default menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.textChanged.connect(self.check_multiline)

        # set cursor shape
        # self.viewport().setCursor(Qt.PointingHandCursor)
        # self.setCursorWidth(10)
        self.cursorPositionChanged.connect(self.update_readonly)

        # disable rich text pasting
        self.setAcceptRichText(False)

        # clear undo
        self.document().clearUndoRedoStacks()
Example #3
0
class PyInterp(QtGui.QTextEdit):

    class InteractiveInterpreter(code.InteractiveInterpreter):

        def __init__(self, locals):
            code.InteractiveInterpreter.__init__(self, locals)

        def runIt(self, command):
            code.InteractiveInterpreter.runsource(self, command)

    def __init__(self,  parent, global_vars=None):
        super(PyInterp,  self).__init__(parent)

        self.stdout_bak = sys.stdout
        self.stderr_bak = sys.stderr

        sys.stdout = self
        sys.stderr = self
        self.refreshMarker = False  # to change back to >>> from ...
        self.multiLine = False  # code spans more than one line
        self.command = ''    # command to be ran
        self.printBanner()              # print sys info
        self.marker()                   # make the >>> or ... marker
        self.history = []    # list of commands entered
        self.historyIndex = -1
        self.interpreterLocals = {}

        # self.last_cursor=None

        # setting the color for bg and text
        palette = QtGui.QPalette()
        text_color = QtGui.QColor(179, 179, 179)
        # highlight_color=self.palette().highlight().color()
        bg_color=QtGui.QColor(46, 46, 46)
        palette.setColor(QtGui.QPalette.Base, bg_color)
        palette.setColor(QtGui.QPalette.Text,text_color )

        # palette.setColor(QtGui.QPalette.Highlight, highlight_color)
        # palette.setColor(QtGui.QPalette.HighlightedText, bg_color)
        # set highlight color

        self.setPalette(palette)
        self.setFont(QtGui.QFont('DejaVu Sans Mono', 10))

        # save default format
        self.default_char_format = self.textCursor().charFormat()

        # save selected \
        self.selected_range=None

        # last cursor position
        self.last_cursor_pos=self.textCursor().position()

        # initilize interpreter with self locals
        
        if global_vars is None:
            global_vars = inspect.getouterframes(inspect.currentframe())[-1][0].f_globals
        self.initInterpreter(global_vars)

        from rlcompleter2 import Completer

        self.completer = Completer()

        # extend default menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.textChanged.connect(self.check_multiline)

        # set cursor shape
        # self.viewport().setCursor(Qt.PointingHandCursor)
        # self.setCursorWidth(10)
        self.cursorPositionChanged.connect(self.update_readonly)

        # disable rich text pasting
        self.setAcceptRichText(False)

        # clear undo
        self.document().clearUndoRedoStacks()

        # self.setAcceptDrops(False)

        # event.ignore()
    def showContextMenu(self,pos):
        menu=self.createStandardContextMenu()
        menu.addSeparator()
        
        resetAction=menu.addAction('Reset')
        testAction=menu.addAction('Debug')
        action=menu.exec_(self.mapToGlobal(pos))
        # delete menu
        if action== testAction:
            # print len(locals()),len(globals()),len(vars(self.interpreter)),len(self.interpreter.locals)
            # print len(self.interpreter.locals)

            # print self.interpreter.locals
            self.textCursor().setPosition(self.last_cursor_pos)
        if action== resetAction:
            self.clear()
            self.marker()
            # update last_cursor_pos
            self.last_cursor_pos=self.textCursor().position()


    def check_multiline(self):
        self.blockSignals(True)
        # print 'check'
        with open('/tmp/test','a') as f:
            f.write('changed')
        self.blockSignals(False)

    def printBanner(self):
        self.write(sys.version)
        self.write(' on ' + sys.platform + '\n')
        self.write('PyQt4 ' + QtCore.PYQT_VERSION_STR + '\n')
        # msg = 'Type !hist for a history view and !hist(n) history index recall'
        # self.write(msg + '\n')
    # def get_last_line(self):
    #     for l in reversed(range(self.document().blockCount())):
    #         if str(self.document().findBlockByNumber(l).text()).startswith('>>>'):
    #             return l

    def update_readonly(self):
        # print 'save cursor\n'
        # self.last_cursor = self.textCursor()
        cursor = self.textCursor()

        if cursor.block().blockNumber()<self.get_last_block_num():
            pass
            self.setReadOnly(True)
            # self.blockSignals(True)
            # cursor.setPosition(22)
            # self.blockSignals(False)

        else:
            self.setReadOnly(False)

        



    def get_last_block_num(self):
        block_count = self.document().blockCount()
        for i in xrange(block_count,-1,-1):
            if str(self.document().findBlockByNumber(i).text()).startswith('>>>'):
                return i

        # if nothing found, return last line, but this is not possible
        return block_count


    def is_editing_allowed(self,cursor=None):
        allowed= False
        if cursor is None:
            cursor = self.textCursor()
        cur_blk_num = cursor.block().blockNumber()
        last_blk_num = self.get_last_block_num()
        _logger.info('is_editing_allowed %s %s %s %s' %(cur_blk_num,last_blk_num,self.textCursor().position(),self.textCursor().block().position()))
        if cur_blk_num >=last_blk_num:
            allowed=True
            # consider >>>
            if cur_blk_num == last_blk_num:
                if cursor.position() - cursor.block().position() <= 3:
                    allowed=False
                pass
        return allowed


    def mouseMoveEvent(self,event):
        # _logger.info('event: %s' %event.__class__)
        # _logger.info('release mouse button %s' %event.buttons())




        if event.button()== Qt.LeftButton:

            # cursor = self.textCursor()
            # # _logger.info('release mouse')
            # if cursor.hasSelection():
            #     start = cursor.selectionStart()
            #     end = cursor.selectionEnd()



            # set selected text color before releasing mouse button
            tmp = self.textCursor().charFormat()
            # tmp.setBackground(QtGui.QBrush(self.palette().highlight().color()))
            # tmp.setForeground(QtGui.QBrush(self.palette().background().color()))
            tmp.setBackground(QtGui.QBrush(self.palette().text().color()))
            tmp.setForeground(QtGui.QBrush(self.palette().base().color()))

            self.textCursor().setCharFormat(tmp)
            # print 'cursor changed'
            # self.textCursor().setPosition(3)

        super(PyInterp,self).mouseMoveEvent(event)    

    def mouseReleaseEvent(self,event):
        # _logger.info('event: %s' %event.__class__)
        # _logger.info('release mouse button %s' %event.buttons())
        cursor = self.textCursor()
        if event.button()== Qt.LeftButton:
            # check if editing is allowed
            # if not self.is_editing_allowed():
                # print 'left'
                # return
            # self.setTextCursor(self.last_cursorself.last_cursor)

            # store selected range if highlight selection
            
            # _logger.info('release mouse')
            if cursor.hasSelection():
                start = cursor.selectionStart()
                end = cursor.selectionEnd()
                self.selected_range=(start,end)
                # _logger.info('save last selection: %s %s' %(start,end))



            else:
                self.selected_range=None

        elif event.button()== Qt.MidButton:
            cursor = self.cursorForPosition(event.pos())
            if self.is_editing_allowed(cursor):
                self.setReadOnly(False)
            else:
                # self.setReadOnly(True)
                return
        else:
            # print 'other'
            pass

        super(PyInterp,self).mouseReleaseEvent(event) 
        # _logger.info('restore last pos: %s' %self.last_cursor_pos)
        cursor.setPosition(self.last_cursor_pos) 
        # if event.button()== Qt.MiddleButton:
        #     self.setReadOnly(True)  

    def mousePressEvent(self,event):
        if event.buttons()== Qt.LeftButton:
            # check if editing is allowed
            # if not self.is_editing_allowed():
                # print 'left'
                # return
            # self.textCursor().setCharFormat(self.default_char_format)
            # block_count = self.document().blockCount()
            # for i in xrange(block_count,-1,-1):
            #     self.document().findBlockByNumber(i).textCursor().setCharFormat(self.default_char_format)

            # select all
            cursor=self.textCursor()
            if self.selected_range:
                start,end = self.selected_range
                cursor.setPosition(start)
                cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
                self.setTextCursor(cursor)

                self.textCursor().setCharFormat(self.default_char_format)
                # cursor.setCharFormat(self.default_char_format)

            pass
            # self.last_cursor = self.textCursor()
        elif event.buttons()== Qt.MidButton:
            # _logger.info('press mouse button %s' %event.buttons())
            # if middle click location is allowed to edit
            cursor = self.cursorForPosition(event.pos())
            if self.is_editing_allowed(cursor):
                _logger.info('editing_allowed' )
                self.setReadOnly(False)   
            else:
                _logger.info('not allowed' )
            #     event.ignore()
                return 

        else:
            # print 'other'
            pass

        super(PyInterp,self).mousePressEvent(event)
        self.textCursor().setCharFormat(self.default_char_format)
        # if event.buttons()== Qt.MiddleButton:
        #     self.setReadOnly(True)
        #     pass

    def dropEvent(self,event):
        # if drop from self
        if event.source():
            # print 'self drag'
            event.accept()
            # self.viewport().update()
            return

        # insert file path instead uri when dragging files from system
        cursor = self.cursorForPosition(event.pos())
        if self.is_editing_allowed(cursor):
            if event.mimeData().hasUrls:
                for url in event.mimeData().urls():
                    self.insertPlainText(str(url.toLocalFile()))
                return
        else:
            return

        super(PyInterp,self).dropEvent(event)

    # def dragEvent(self,event):
    #     # print event.source()
    #     if event.source():
    #         # print 'self drag'
    #         event.accept()
    #         # self.viewport().update()
    #         return

    #     super(PyInterp,self).dragEvent(event)

    def dragMoveEvent(self,event):
        # if drag from self
        if event.source():
            # print 'self drag'
            event.accept()
            self.viewport().update()
            return
        super(PyInterp,self).dragMoveEvent(event)

    def insertFromMimeData(self,source):
        # super(PyInterp,self).insertPlainText(source.text())
        # self.insertPlainText(source.text())
        lines = source.text().split('\n')
        if len(lines)>1:
            self.multiLine=True
            i=1
            for line in lines:

                if i>1:
                    self.marker()
                self.insertPlainText(line)
                self.insertPlainText('\n')
                i+=1

                self.command += str(line) + '\n' 
                

        else:
            # if source.hasUrls():
            #     print 'has url'
            #     # self.insertPlainText(source.urls()[0].path())
            # else:
                # text = source.text()
                # if text.startswith('file://'):
                #     text=text.lstrip('file://')
            self.insertPlainText(source.text())
            # self.insertPlainText('xxx')

    def marker(self):
        if self.multiLine:
            self.insertPlainText('... ')
        else:
            self.insertPlainText('>>> ')

        # update last_cursor_pos
        self.last_cursor_pos=self.textCursor().position()

    def initInterpreter(self, interpreterLocals=None):
        if interpreterLocals:
            # when we pass in locals, we don't want it to be named "self"
            # so we rename it with the name of the class that did the passing
            # and reinsert the locals back into the interpreter dictionary

            # maybe this still works when run outside maya
            try:
                selfName = interpreterLocals['self'].__class__.__name__
                interpreterLocalVars = interpreterLocals.pop('self')
                self.interpreterLocals[selfName] = interpreterLocalVars
            except:
                pass
                self.interpreterLocals = interpreterLocals
        else:
            self.interpreterLocals = interpreterLocals
        self.interpreter = self.InteractiveInterpreter(self.interpreterLocals)

    def updateInterpreterLocals(self, newLocals):
        className = newLocals.__class__.__name__
        self.interpreterLocals[className] = newLocals

    def write(self, line):
        self.insertPlainText(line)
        self.ensureCursorVisible()

        self.document().lastBlock().setUserState(666)

    def clearCurrentBlock(self):
        # block being current row
        length = len(self.document().lastBlock().text()[4:])
        if length == 0:
            return None
        else:
            # should have a better way of doing this but I can't find it
            [self.textCursor().deletePreviousChar() for x in xrange(length)]
        return True

    def recallHistory(self):
        # used when using the arrow keys to scroll through history
        self.clearCurrentBlock()
        if self.historyIndex <> -1:
            self.insertPlainText(self.history[self.historyIndex])
        return True

    def customCommands(self, command):

        if command == '!hist':  # display history
            self.append('')  # move down one line
            # vars that are in the command are prefixed with ____CC and deleted
            # once the command is done so they don't show up in dir()
            backup = self.interpreterLocals.copy()
            history = self.history[:]
            history.reverse()
            for i, x in enumerate(history):
                iSize = len(str(i))
                delta = len(str(len(history))) - iSize
                line = line = ' ' * delta + '%i: %s' % (i, x) + '\n'
                self.write(line)
            self.updateInterpreterLocals(backup)
            self.marker()
            return True

        if re.match('!hist\(\d+\)', command):  # recall command from history
            backup = self.interpreterLocals.copy()
            history = self.history[:]
            history.reverse()
            index = int(command[6:-1])
            self.clearCurrentBlock()
            command = history[index]
            if command[-1] == ':':
                self.multiLine = True
            self.write(command)
            self.updateInterpreterLocals(backup)
            return True

        return False

    def is_bracket_matching(self,line):
        multiline=False

        c = self.command or line
        # print 'command:',c
        if c.count('(')!=c.count(')') or c.count('[')!=c.count(']') or c.count('{')!=c.count('}'):
            multiline=True

        # print 'multiline:',multiline
        return multiline


    def keyPressEvent(self, event):

        if event.key() == Qt.Key_Tab:
            line = str(self.document().lastBlock().text())[4:]
            # set width based on window width
            # window_width=self.frameGeometry().width()
            # self.completer.config.terminalwidth=160


            self.completer.construct(line)

            if len(self.completer.rl_matches) == 1:
                self.clearCurrentBlock()
                self.insertPlainText(self.completer.rl_matches[0])
            else:
                # print 'repeat:', self.completer.repeated

                mod = self.completer.repeated % len(self.completer.completions)
                if mod == 0:
                    # print self.completer.rl_matches
                    print ' '
                    col_print(self.completer.rl_matches)
                else:

                    print ' '
                    print '\n'.join(self.completer.rl_matches)
                    # print self.completer.rl_matches
                self.marker()
                self.insertPlainText(line)

            # scroll to bottom
            self.ensureCursorVisible()
            return

        # if event.key() == Qt.Key_Escape:
        #     # proper exit
        #     self.interpreter.runIt('exit()')

        # ctrl-c to reset current line 
        if event.key()== Qt.Key_C and event.modifiers()==Qt.ControlModifier:
            # self.interpreter.runIt('exit()')
            self.append('')
            self.command = ''
            self.marker()
            return

        if event.key() == Qt.Key_Down:
            if self.historyIndex == len(self.history):
                self.historyIndex -= 1
            try:
                if self.historyIndex > -1:
                    self.historyIndex -= 1
                    self.recallHistory()
                else:
                    self.clearCurrentBlock()
            except:
                pass
            return None

        if event.key() == Qt.Key_Up:
            try:
                if len(self.history) - 1 > self.historyIndex:
                    self.historyIndex += 1
                    self.recallHistory()
                else:
                    self.historyIndex = len(self.history)
            except:
                pass
            return None

        if event.key() == Qt.Key_Home:
            # set cursor to position 4 in current block. 4 because that's where
            # the marker stops
            blockLength = len(self.document().lastBlock().text()[4:])
            lineLength = len(self.document().toPlainText())
            position = lineLength - blockLength
            textCursor = self.textCursor()
            textCursor.setPosition(position)
            self.setTextCursor(textCursor)
            return None

        if event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
            # don't allow deletion of marker
            # if qt version < 4.7, have to use position() - block().position()
            # if self.textCursor().positionInBlock() == 4:
            if self.textCursor().position() - self.textCursor().block().position() == 4:
                return None

            if not self.is_editing_allowed():
                return None

        if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
            # set cursor to end of line to avoid line splitting
            textCursor = self.textCursor()
            position = len(self.document().toPlainText())
            textCursor.setPosition(position)
            self.setTextCursor(textCursor)

            line = str(self.document().lastBlock().text())[4:]  # remove marker
            line.rstrip()
            self.historyIndex = -1

            if self.customCommands(line):
                return None
            else:
                try:
                    line[-1]
                    self.haveLine = True
                    if line.strip()[-1] == ':' or self.is_bracket_matching(line):
                        self.multiLine = True
                    self.history.insert(0, line)
                except:
                    self.haveLine = False

                if self.haveLine and self.multiLine:  # multi line command
                    self.command += line + '\n'  # + command and line
                    self.append('')  # move down one line
                    self.marker()  # handle marker style
                    return None

                if self.haveLine and not self.multiLine:  # one line command
                    self.command = line  # line is the command
                    self.append('')  # move down one line
                    self.interpreter.runIt(self.command)
                    self.command = ''  # clear command
                    self.marker()  # handle marker style
                    return None

                if self.multiLine and not self.haveLine:  # multi line done
                    self.append('')  # move down one line
                    self.interpreter.runIt(self.command)
                    self.command = ''  # clear command
                    self.multiLine = False  # back to single line
                    self.marker()  # handle marker style
                    return None

                if not self.haveLine and not self.multiLine:  # just enter
                    self.append('')
                    self.marker()
                    return None
                return None

        # allow all other key events
        super(PyInterp, self).keyPressEvent(event)
Example #4
0
class PyInterp(QtGui.QTextEdit):
    class InteractiveInterpreter(code.InteractiveInterpreter):
        def __init__(self, locals):
            code.InteractiveInterpreter.__init__(self, locals)

        def runIt(self, command):
            symbol = "single" if '\n' not in command else "exec"
            code.InteractiveInterpreter.runsource(self, command, symbol=symbol)

    def __init__(self, parent, global_vars=None):
        super(PyInterp, self).__init__(parent)

        self.stdout_bak = sys.stdout
        self.stderr_bak = sys.stderr

        sys.stdout = self
        sys.stderr = self
        self.refreshMarker = False  # to change back to >>> from ...
        self.multiLine = False  # code spans more than one line
        self.command = ''  # command to be ran
        self.printBanner()  # print sys info
        self.marker()  # make the >>> or ... marker
        self.history = []  # list of commands entered
        self.historyIndex = -1
        self.interpreterLocals = {}

        # self.last_cursor=None

        # setting the color for bg and text
        palette = QtGui.QPalette()
        text_color = QtGui.QColor(179, 179, 179)
        # highlight_color=self.palette().highlight().color()
        bg_color = QtGui.QColor(46, 46, 46)
        palette.setColor(QtGui.QPalette.Base, bg_color)
        palette.setColor(QtGui.QPalette.Text, text_color)

        # palette.setColor(QtGui.QPalette.Highlight, highlight_color)
        # palette.setColor(QtGui.QPalette.HighlightedText, bg_color)
        # set highlight color

        self.setPalette(palette)
        self.setFont(QtGui.QFont('DejaVu Sans Mono', 10))

        # save default format
        self.default_char_format = self.textCursor().charFormat()

        # save selected \
        self.selected_range = None

        # last cursor position
        self.last_cursor_pos = self.textCursor().position()

        # initilize interpreter with self locals

        if global_vars is None:
            global_vars = inspect.getouterframes(
                inspect.currentframe())[-1][0].f_globals
        self.initInterpreter(global_vars)

        from rlcompleter2 import Completer

        self.completer = Completer()

        # extend default menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.textChanged.connect(self.check_multiline)

        # set cursor shape
        # self.viewport().setCursor(Qt.PointingHandCursor)
        # self.setCursorWidth(10)
        self.cursorPositionChanged.connect(self.update_readonly)

        # disable rich text pasting
        self.setAcceptRichText(False)

        # clear undo
        self.document().clearUndoRedoStacks()

        # self.setAcceptDrops(False)

        # event.ignore()
    def showContextMenu(self, pos):
        menu = self.createStandardContextMenu()
        menu.addSeparator()

        resetAction = menu.addAction('Reset')
        testAction = menu.addAction('Debug')
        action = menu.exec_(self.mapToGlobal(pos))
        # delete menu
        if action == testAction:
            # print len(locals()),len(globals()),len(vars(self.interpreter)),len(self.interpreter.locals)
            # print len(self.interpreter.locals)

            # print self.interpreter.locals
            self.textCursor().setPosition(self.last_cursor_pos)
        if action == resetAction:
            self.clear()
            self.marker()
            # update last_cursor_pos
            self.last_cursor_pos = self.textCursor().position()

    def check_multiline(self):
        self.blockSignals(True)
        # print 'check'
        with open('/tmp/test', 'a') as f:
            f.write('changed')
        self.blockSignals(False)

    def printBanner(self):
        self.write(sys.version)
        self.write(' on ' + sys.platform + '\n')
        self.write('PyQt4 ' + QtCore.PYQT_VERSION_STR + '\n')
        # msg = 'Type !hist for a history view and !hist(n) history index recall'
        # self.write(msg + '\n')

    # def get_last_line(self):
    #     for l in reversed(range(self.document().blockCount())):
    #         if str(self.document().findBlockByNumber(l).text()).startswith('>>>'):
    #             return l

    def update_readonly(self):
        # print 'save cursor\n'
        # self.last_cursor = self.textCursor()
        cursor = self.textCursor()

        if cursor.block().blockNumber() < self.get_last_block_num():
            pass
            self.setReadOnly(True)
            # self.blockSignals(True)
            # cursor.setPosition(22)
            # self.blockSignals(False)

        else:
            self.setReadOnly(False)

    def get_last_block_num(self):
        block_count = self.document().blockCount()
        for i in xrange(block_count, -1, -1):
            if str(self.document().findBlockByNumber(i).text()).startswith(
                    '>>>'):
                return i

        # if nothing found, return last line, but this is not possible
        return block_count

    def is_editing_allowed(self, cursor=None):
        allowed = False
        if cursor is None:
            cursor = self.textCursor()
        cur_blk_num = cursor.block().blockNumber()
        last_blk_num = self.get_last_block_num()
        _logger.debug('is_editing_allowed %s %s %s %s' %
                      (cur_blk_num, last_blk_num, self.textCursor().position(),
                       self.textCursor().block().position()))
        if cur_blk_num >= last_blk_num:
            allowed = True
            # consider >>>
            if cur_blk_num == last_blk_num:
                if cursor.position() - cursor.block().position() <= 3:
                    allowed = False
                pass
        return allowed

    def mouseMoveEvent(self, event):
        # _logger.info('event: %s' %event.__class__)
        # _logger.info('release mouse button %s' %event.buttons())

        if event.button() == Qt.LeftButton:

            # cursor = self.textCursor()
            # # _logger.info('release mouse')
            # if cursor.hasSelection():
            #     start = cursor.selectionStart()
            #     end = cursor.selectionEnd()

            # set selected text color before releasing mouse button
            tmp = self.textCursor().charFormat()
            # tmp.setBackground(QtGui.QBrush(self.palette().highlight().color()))
            # tmp.setForeground(QtGui.QBrush(self.palette().background().color()))
            tmp.setBackground(QtGui.QBrush(self.palette().text().color()))
            tmp.setForeground(QtGui.QBrush(self.palette().base().color()))

            self.textCursor().setCharFormat(tmp)
            # print 'cursor changed'
            # self.textCursor().setPosition(3)

        super(PyInterp, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        # _logger.info('event: %s' %event.__class__)
        # _logger.info('release mouse button %s' %event.buttons())
        cursor = self.textCursor()
        if event.button() == Qt.LeftButton:
            # check if editing is allowed
            # if not self.is_editing_allowed():
            # print 'left'
            # return
            # self.setTextCursor(self.last_cursorself.last_cursor)

            # store selected range if highlight selection

            # _logger.info('release mouse')
            if cursor.hasSelection():
                start = cursor.selectionStart()
                end = cursor.selectionEnd()
                self.selected_range = (start, end)
                # _logger.info('save last selection: %s %s' %(start,end))

            else:
                self.selected_range = None

        elif event.button() == Qt.MidButton:
            cursor = self.cursorForPosition(event.pos())
            if self.is_editing_allowed(cursor):
                self.setReadOnly(False)
            else:
                # self.setReadOnly(True)
                return
        else:
            # print 'other'
            pass

        super(PyInterp, self).mouseReleaseEvent(event)
        # _logger.info('restore last pos: %s' %self.last_cursor_pos)
        cursor.setPosition(self.last_cursor_pos)
        # if event.button()== Qt.MiddleButton:
        #     self.setReadOnly(True)

    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            # check if editing is allowed
            # if not self.is_editing_allowed():
            # print 'left'
            # return
            # self.textCursor().setCharFormat(self.default_char_format)
            # block_count = self.document().blockCount()
            # for i in xrange(block_count,-1,-1):
            #     self.document().findBlockByNumber(i).textCursor().setCharFormat(self.default_char_format)

            # select all
            cursor = self.textCursor()
            if self.selected_range:
                start, end = self.selected_range
                cursor.setPosition(start)
                cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
                self.setTextCursor(cursor)

                self.textCursor().setCharFormat(self.default_char_format)
                # cursor.setCharFormat(self.default_char_format)

            pass
            # self.last_cursor = self.textCursor()
        elif event.buttons() == Qt.MidButton:
            # _logger.info('press mouse button %s' %event.buttons())
            # if middle click location is allowed to edit
            cursor = self.cursorForPosition(event.pos())
            if self.is_editing_allowed(cursor):
                _logger.debug('editing_allowed')
                self.setReadOnly(False)
            else:
                _logger.debug('not allowed')
                #     event.ignore()
                return

        else:
            # print 'other'
            pass

        super(PyInterp, self).mousePressEvent(event)
        self.textCursor().setCharFormat(self.default_char_format)
        # if event.buttons()== Qt.MiddleButton:
        #     self.setReadOnly(True)
        #     pass

    def dropEvent(self, event):
        # if drop from self
        if event.source():
            # print 'self drag'
            event.accept()
            # self.viewport().update()
            return

        # insert file path instead uri when dragging files from system
        cursor = self.cursorForPosition(event.pos())
        if self.is_editing_allowed(cursor):
            if event.mimeData().hasUrls:
                for url in event.mimeData().urls():
                    self.insertPlainText(str(url.toLocalFile()))
                return
        else:
            return

        super(PyInterp, self).dropEvent(event)

    # def dragEvent(self,event):
    #     # print event.source()
    #     if event.source():
    #         # print 'self drag'
    #         event.accept()
    #         # self.viewport().update()
    #         return

    #     super(PyInterp,self).dragEvent(event)

    def dragMoveEvent(self, event):
        # if drag from self
        if event.source():
            # print 'self drag'
            event.accept()
            self.viewport().update()
            return
        super(PyInterp, self).dragMoveEvent(event)

    def insertFromMimeData(self, source):
        # super(PyInterp,self).insertPlainText(source.text())
        # self.insertPlainText(source.text())
        lines = source.text().split('\n')
        if len(lines) > 1:
            self.multiLine = True
            i = 1
            for line in lines:

                if i > 1:
                    self.marker()
                self.insertPlainText(line)
                self.insertPlainText('\n')
                i += 1

                self.command += str(line) + '\n'

        else:
            # if source.hasUrls():
            #     print 'has url'
            #     # self.insertPlainText(source.urls()[0].path())
            # else:
            # text = source.text()
            # if text.startswith('file://'):
            #     text=text.lstrip('file://')
            self.insertPlainText(source.text())
            # self.insertPlainText('xxx')

    def marker(self):
        if self.multiLine:
            self.insertPlainText('... ')
        else:
            self.insertPlainText('>>> ')

        # update last_cursor_pos
        self.last_cursor_pos = self.textCursor().position()

    def initInterpreter(self, interpreterLocals=None):
        if interpreterLocals:
            # when we pass in locals, we don't want it to be named "self"
            # so we rename it with the name of the class that did the passing
            # and reinsert the locals back into the interpreter dictionary

            # maybe this still works when run outside maya
            try:
                selfName = interpreterLocals['self'].__class__.__name__
                interpreterLocalVars = interpreterLocals.pop('self')
                self.interpreterLocals[selfName] = interpreterLocalVars
            except:
                pass
                self.interpreterLocals = interpreterLocals
        else:
            self.interpreterLocals = interpreterLocals
        self.interpreter = self.InteractiveInterpreter(self.interpreterLocals)

    def updateInterpreterLocals(self, newLocals):
        className = newLocals.__class__.__name__
        self.interpreterLocals[className] = newLocals

    def write(self, line):
        self.insertPlainText(line)
        self.ensureCursorVisible()

        self.document().lastBlock().setUserState(666)

    def clearCurrentBlock(self):
        # block being current row
        length = len(self.document().lastBlock().text()[4:])
        if length == 0:
            return None
        else:
            # should have a better way of doing this but I can't find it
            [self.textCursor().deletePreviousChar() for x in xrange(length)]
        return True

    def recallHistory(self):
        # used when using the arrow keys to scroll through history
        self.clearCurrentBlock()
        if self.historyIndex <> -1:
            self.insertPlainText(self.history[self.historyIndex])
        return True

    def customCommands(self, command):

        if command == '!hist':  # display history
            self.append('')  # move down one line
            # vars that are in the command are prefixed with ____CC and deleted
            # once the command is done so they don't show up in dir()
            backup = self.interpreterLocals.copy()
            history = self.history[:]
            history.reverse()
            for i, x in enumerate(history):
                iSize = len(str(i))
                delta = len(str(len(history))) - iSize
                line = line = ' ' * delta + '%i: %s' % (i, x) + '\n'
                self.write(line)
            self.updateInterpreterLocals(backup)
            self.marker()
            return True

        if re.match('!hist\(\d+\)', command):  # recall command from history
            backup = self.interpreterLocals.copy()
            history = self.history[:]
            history.reverse()
            index = int(command[6:-1])
            self.clearCurrentBlock()
            command = history[index]
            if command[-1] == ':':
                self.multiLine = True
            self.write(command)
            self.updateInterpreterLocals(backup)
            return True

        return False

    def is_bracket_matching(self, line):
        multiline = False

        c = self.command or line
        # print 'command:',c
        if c.count('(') != c.count(')') or c.count('[') != c.count(
                ']') or c.count('{') != c.count('}'):
            multiline = True

        # print 'multiline:',multiline
        return multiline

    def keyPressEvent(self, event):

        if event.key() == Qt.Key_Tab:
            line = str(self.document().lastBlock().text())[4:]
            # set width based on window width
            # window_width=self.frameGeometry().width()
            # self.completer.config.terminalwidth=160

            self.completer.construct(line)

            if len(self.completer.rl_matches) == 1:
                self.clearCurrentBlock()
                self.insertPlainText(self.completer.rl_matches[0])
            else:
                # print 'repeat:', self.completer.repeated

                mod = self.completer.repeated % len(self.completer.completions)
                if mod == 0:
                    # print self.completer.rl_matches
                    print ' '
                    col_print(self.completer.rl_matches)
                else:

                    print ' '
                    print '\n'.join(self.completer.rl_matches)
                    # print self.completer.rl_matches
                self.marker()
                self.insertPlainText(line)

            # scroll to bottom
            self.ensureCursorVisible()
            return

        # if event.key() == Qt.Key_Escape:
        #     # proper exit
        #     self.interpreter.runIt('exit()')

        # ctrl-c to reset current line
        if event.key() == Qt.Key_C and event.modifiers() == Qt.ControlModifier:
            # self.interpreter.runIt('exit()')
            self.append('')
            self.command = ''
            self.marker()
            return

        if event.key() == Qt.Key_Down:
            if self.historyIndex == len(self.history):
                self.historyIndex -= 1
            try:
                if self.historyIndex > -1:
                    self.historyIndex -= 1
                    self.recallHistory()
                else:
                    self.clearCurrentBlock()
            except:
                pass
            return None

        if event.key() == Qt.Key_Up:
            try:
                if len(self.history) - 1 > self.historyIndex:
                    self.historyIndex += 1
                    self.recallHistory()
                else:
                    self.historyIndex = len(self.history)
            except:
                pass
            return None

        if event.key() == Qt.Key_Home:
            # set cursor to position 4 in current block. 4 because that's where
            # the marker stops
            blockLength = len(self.document().lastBlock().text()[4:])
            lineLength = len(self.document().toPlainText())
            position = lineLength - blockLength
            textCursor = self.textCursor()
            textCursor.setPosition(position)
            self.setTextCursor(textCursor)
            return None

        if event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
            # don't allow deletion of marker
            # if qt version < 4.7, have to use position() - block().position()
            # if self.textCursor().positionInBlock() == 4:
            if self.textCursor().position() - self.textCursor().block(
            ).position() == 4:
                return None

            if not self.is_editing_allowed():
                return None

        if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
            # set cursor to end of line to avoid line splitting
            textCursor = self.textCursor()
            position = len(self.document().toPlainText())
            textCursor.setPosition(position)
            self.setTextCursor(textCursor)

            line = str(self.document().lastBlock().text())[4:]  # remove marker
            line.rstrip()
            self.historyIndex = -1

            if self.customCommands(line):
                return None
            else:
                try:
                    line[-1]
                    self.haveLine = True
                    if line.strip()[-1] == ':' or self.is_bracket_matching(
                            line):
                        self.multiLine = True
                    self.history.insert(0, line)
                except:
                    self.haveLine = False

                if self.haveLine and self.multiLine:  # multi line command
                    self.command += line + '\n'  # + command and line
                    self.append('')  # move down one line
                    self.marker()  # handle marker style
                    return None

                if self.haveLine and not self.multiLine:  # one line command
                    self.command = line  # line is the command
                    self.append('')  # move down one line
                    self.interpreter.runIt(self.command)
                    self.command = ''  # clear command
                    self.marker()  # handle marker style
                    return None

                if self.multiLine and not self.haveLine:  # multi line done
                    self.append('')  # move down one line
                    self.interpreter.runIt(self.command)
                    self.command = ''  # clear command
                    self.multiLine = False  # back to single line
                    self.marker()  # handle marker style
                    return None

                if not self.haveLine and not self.multiLine:  # just enter
                    self.append('')
                    self.marker()
                    return None
                return None

        # allow all other key events
        super(PyInterp, self).keyPressEvent(event)