How to run python code in maya python interpreter's global scope?

I want to make a python shell in maya, here is the code I have now. Currently the problem is the code executed in this interpreter is in code.InteractiveInterpreter class instance( inside my pyqt ui ) , but not in maya. For instance if I run import math in the interpreter, math is not available in maya, or vice versa. However because

self.initInterpreter(locals())

is called when the interpreter is initiated, the names are copied from maya interpreter to my pyqt based interpreter, but after it’s created, they are completely independent and out of sync. How can I make them in sync ?

when enter key is pressed, the code is executed in the interpreter built into the pyqt gui but “not inside” maya’s python interpreter. ( if you run mc.polyCube() it still creates a cube in maya, but all the variables defined in this pyqt based interpreter is not accessible from maya script editor. )



import os
import re
import sys
import code

from PyQt4.QtGui import *
from PyQt4.QtCore import *

import sip

import maya.cmds as cmds
import maya.OpenMayaUI as mui


# http://stackoverflow.com/questions/12431555/enabling-code-completion-in-an-embedded-python-interpreter

class MyInterpreter(QWidget):

    def __init__(self, parent):

        super(MyInterpreter, self).__init__(parent)
        hBox = QHBoxLayout()

        self.setLayout(hBox)
        self.textEdit = PyInterp(self)

        # this is how you pass in locals to the interpreter
        self.textEdit.initInterpreter(locals())

        self.resize(850, 400)
        # self.centerOnScreen()

        hBox.addWidget(self.textEdit)
        hBox.setMargin(0)
        hBox.setSpacing(0)

    def centerOnScreen(self):
        # center the widget on the screen
        resolution = QDesktopWidget().screenGeometry()
        self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
                  (resolution.height() / 2) - (self.frameSize().height() / 2))


class PyInterp(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):
        super(PyInterp,  self).__init__(parent)

        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 = {}

        # setting the color for bg and text
        # palette = QPalette()
        # palette.setColor(QPalette.Base, QColor(0, 0, 0))
        # palette.setColor(QPalette.Text, QColor(0, 255, 0))
        # self.setPalette(palette)
        self.setFont(QFont('Courier', 10))

        # initilize interpreter with self locals
        self.initInterpreter(locals())


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

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

    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
            selfName = interpreterLocals['self'].__class__.__name__
            interpreterLocalVars = interpreterLocals.pop('self')
            self.interpreterLocals[selfName] = interpreterLocalVars
        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()

    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) + '
'
                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 keyPressEvent(self, event):

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

        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:
                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[-1] == ':':
                        self.multiLine = True
                    self.history.insert(0, line)
                except:
                    self.haveLine = False

                if self.haveLine and self.multiLine:  # multi line command
                    self.command += line + '
'  # + 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)

# http://stackoverflow.com/a/30861871/2052889


def col_print(lines, term_width=90, indent=0, pad=2):
    n_lines = len(lines)
    if n_lines == 0:
        return

    col_width = max(len(line) for line in lines)
    n_cols = int((term_width + pad - indent)/(col_width + pad))
    n_cols = min(n_lines, max(1, n_cols))

    col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
    if (n_cols - 1) * col_len >= n_lines:
        n_cols -= 1

    cols = [lines[i*col_len: i*col_len + col_len] for i in range(n_cols)]

    rows = list(zip(*cols))
    rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
    rows.extend(rows_missed)

    for row in rows:
        print(" "*indent + (" "*pad).join(line.ljust(col_width)
                                          for line in row))


def main():
    if str(qApp.applicationName()).lower().startswith('maya'):
        global win
        try:
            win.close()
        except:
            pass
        win = MyInterpreter(None)
        win.show()

if __name__ == "__main__":
    main()

it seems working if I pass locals() as a variable to code.interactiveinterpreter’s init method. but why ?

When you test out passing locals() to code.interactiveinterpreter directly, are you trying that right in the script editor? Because I see two calls to locals(), but I don’t think either one is going to give you the Maya interpreter’s locals. When you have:

self.textEdit.initInterpreter(locals())

That line is inside PyInterp’s init method, so it’s just going to return the locals dictionary for that small namespace. If what you want is the locals dict that you get when you just type “locals()” in the script editor, you’ll have to ask for them while they’re still… well, local. Or use globals(), if this isn’t going to be in a module.

I might also have a look at this: How to define free-variable in python? - Stack Overflow

I hope I’m on the same page with what you’re trying to achieve. Was any of that helpful?