pythonpyqt4qtconsole

PyQt4 : making certain text on QTextEdit uneditable


Is there any way I can make certain text on QTextEdit permanent. Applications like cmd.exe where the current user directory is displayed and the rest of the screen is up for input. I tried inserting a QLabel but unable to do so, here's my code, I am currently taking user input through a separate line edit.

UPDATE I had a look at Ipython QtConsole where the line number is displayed constantly, how can I do that, I am looking in the source but if anyone who already knows it, please do tell. Here is the QtConsole for ipython notebook, I am trying to replicate this.

enter image description here

import os
import sys
import PyQt4
import PyQt4.QtCore
from PyQt4.QtGui import *


def main():
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())


class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        # create objects
        label = QLabel(self.tr("Enter command and press Return"))
        self.le = QLineEdit()
        self.te = QTextEdit()
        self.lbl = QLabel(str(os.getcwd())+"> ")

        # layout
        layout = QVBoxLayout(self)
        layout.addWidget(label)
        layout.addWidget(self.le)
        layout.addWidget(self.te)
        self.setLayout(layout)

        # styling
        self.te.setReadOnly(True)

        # create connection
        self.mytext = str(self.le.text())
        self.connect(self.le, PyQt4.QtCore.SIGNAL("returnPressed(void)"),
                     self.display)

    def display(self):
        mytext = str(self.le.text())
        self.te.append(self.lbl +str(os.popen(mytext).read()))
        self.le.setText("")


if __name__ == "__main__":
    main()

Solution

  • A simple solution is to create a class that inherits from QTextEdit and overwrite and add the necessary attributes as shown below:

    class TextEdit(QTextEdit):
        def __init__(self, *args, **kwargs):
            QTextEdit.__init__(self, *args, **kwargs)
            self.staticText = os.getcwd()
            self.counter = 1
            self.setReadOnly(True)
    
        def append(self, text):
            n_text = "{text} [{number}] > ".format(text=self.staticText, number=self.counter)
            self.counter += 1
            QTextEdit.append(self, n_text+text)
    

    Complete Code:

    import os
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    class TextEdit(QTextEdit):
        def __init__(self, *args, **kwargs):
            QTextEdit.__init__(self, *args, **kwargs)
            self.staticText = os.getcwd()
            self.counter = 1
            self.setReadOnly(True)
    
        def append(self, text):
            n_text = "{text} [{number}] > ".format(text=self.staticText, number=self.counter)
            self.counter += 1
            QTextEdit.append(self, n_text+text)
    
    
    class MyWindow(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
            label = QLabel(self.tr("Enter command and press Return"), self)
            self.le = QLineEdit(self)
            self.te = TextEdit(self)
            # layout
            layout = QVBoxLayout(self)
            layout.addWidget(label)
            layout.addWidget(self.le)
            layout.addWidget(self.te)
            self.setLayout(layout)
            self.connect(self.le, SIGNAL("returnPressed(void)"), self.display)
            # self.le.returnPressed.connect(self.display)
    
        def display(self):
            command = str(self.le.text())
            resp = str(os.popen(command).read())
            self.te.append(resp)
            self.le.clear()
    
    
    def main():
        app = QApplication(sys.argv)
        w = MyWindow()
        w.show()
        sys.exit(app.exec_())
    
    if __name__ == "__main__":
        main()
    

    To emulate QtConsole we must overwrite some methods of QTextEdit, catch some events, and verify that it does not eliminate the prefix as I show below:

    import os
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    class TextEdit(QTextEdit):
        def __init__(self, *args, **kwargs):
            QTextEdit.__init__(self, *args, **kwargs)
            self.staticText = os.getcwd()
            self.counter = 1
            self.prefix = ""
            self.callPrefix()
            self.setContextMenuPolicy(Qt.CustomContextMenu)
            self.customContextMenuRequested.connect(self.onCustomContextMenuRequest)
    
        def onCustomContextMenuRequest(self, point):
            menu = self.createStandardContextMenu()
            for action in menu.actions():
                if "Delete" in action.text():
                    action.triggered.disconnect()
                    menu.removeAction(action)
                elif "Cu&t" in action.text():
                    action.triggered.disconnect()
                    menu.removeAction(action)
                elif "Paste" in action.text():
                    action.triggered.disconnect()
    
            act = menu.exec_(point)
            if act:
                if "Paste" in act.text():
                    self.customPaste()
    
    
        def customPaste(self):
            self.moveCursor(QTextCursor.End)
            self.insertPlainText(QApplication.clipboard().text())
            self.moveCursor(QTextCursor.End)
    
        def clearCurrentLine(self):
            cs = self.textCursor()
            cs.movePosition(QTextCursor.StartOfLine)
            cs.movePosition(QTextCursor.EndOfLine)
            cs.select(QTextCursor.LineUnderCursor)
            text = cs.removeSelectedText()
    
        def isPrefix(self, text):
            return self.prefix == text
    
        def getCurrentLine(self):
            cs = self.textCursor()
            cs.movePosition(QTextCursor.StartOfLine)
            cs.movePosition(QTextCursor.EndOfLine)
            cs.select(QTextCursor.LineUnderCursor)
            text = cs.selectedText()
            return text
    
        def keyPressEvent(self, event):
            if event.key() == Qt.Key_Return:
                command = self.getCurrentLine()[len(self.prefix):]
                self.execute(command)
                self.callPrefix()
                return
            elif event.key() == Qt.Key_Backspace:
                if self.prefix == self.getCurrentLine():
                    return
            elif event.matches(QKeySequence.Delete):
                return
            if event.matches(QKeySequence.Paste):
                self.customPaste()
                return
            elif self.textCursor().hasSelection():
                t = self.toPlainText()
                self.textCursor().clearSelection()
                QTextEdit.keyPressEvent(self, event)
                self.setPlainText(t)
                self.moveCursor(QTextCursor.End)
                return
            QTextEdit.keyPressEvent(self, event)
    
        def callPrefix(self):
            self.prefix = "{text} [{number}] >".format(text=self.staticText, number=self.counter)
            self.counter += 1
            self.append(self.prefix)
    
        def execute(self, command):
            resp = os.popen(command).read()
            self.append(resp)
    
    
    class MyWindow(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
            label = QLabel(self.tr("Enter command and press Return"), self)
            self.te = TextEdit(self)
            # layout
            layout = QVBoxLayout(self)
            layout.addWidget(label)
            layout.addWidget(self.te)
            self.setLayout(layout)
    
    def main():
        app = QApplication(sys.argv)
        w = MyWindow()
        w.show()
        sys.exit(app.exec_())
    
    if __name__ == "__main__":
        main()