Search code examples
pythonpysideqpainterqtextedit

Adding additional parameters to QWidget subclass using PySide


I'm adding a color parameter to the LineBand subclass of QWidget. I've found several examples of how to add additional parameters to a subclass in Python 3 and believe I've followed the advice. Yet, when I call the new version of the class using box = LineBand(self.widget2, color), I get the error File "C:/Users/...", line 63, in showBoxes ... box = LineBand(viewport, color) ... TypeError: __init__() takes 2 positional arguments but 3 were given. But, I'm only calling LineBand with 2 arguments, right? Below is the complete code. I've commented all the sections I've changed. I've also commented out the code that changes the background color of the text in order to see the colored lines more clearly (when they actually are drawn). The background color code works fine.

import sys
from PySide.QtCore import *
from PySide.QtGui import *

db = ((5,8,'A',Qt.darkMagenta),(20,35,'B',Qt.darkYellow),(45,60,'C',Qt.darkCyan)) # added color to db

class TextEditor(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        text="This is example text that is several lines\nlong and also\nstrangely broken up and can be\nwrapped."
        self.setText(text)
        cursor = self.textCursor()
        for n in range(0,len(db)):
            row = db[n]
            startChar = row[0]
            endChar = row[1]
            id = row[2]
            color = row[3]  # assign color from db to variable
            cursor.setPosition(startChar)
            cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, endChar-startChar)
            #charfmt = cursor.charFormat()
            #charfmt.setBackground(QColor(color)) # assign color to highlight (background)
            #cursor.setCharFormat(charfmt)
        cursor.clearSelection()
        self.setTextCursor(cursor)

    def getBoundingRect(self, start, end):
        cursor = self.textCursor()
        cursor.setPosition(end)
        last_rect = end_rect = self.cursorRect(cursor)
        cursor.setPosition(start)
        first_rect = start_rect = self.cursorRect(cursor)
        if start_rect.y() != end_rect.y():
            cursor.movePosition(QTextCursor.StartOfLine)
            first_rect = last_rect = self.cursorRect(cursor)
            while True:
                cursor.movePosition(QTextCursor.EndOfLine)
                rect = self.cursorRect(cursor)
                if rect.y() < end_rect.y() and rect.x() > last_rect.x():
                    last_rect = rect
                moved = cursor.movePosition(QTextCursor.NextCharacter)
                if not moved or rect.y() > end_rect.y():
                    break
            last_rect = last_rect.united(end_rect)
        return first_rect.united(last_rect)



class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.edit = TextEditor(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.edit)
        self.boxes = []

    def showBoxes(self):
        while self.boxes:
            self.boxes.pop().deleteLater()
        viewport = self.edit.viewport()
        for start, end, id, color in db:  # get color too
            rect = self.edit.getBoundingRect(start, end)
            box = LineBand(viewport, color) # call LineBand with color as argument
            box.setGeometry(rect)
            box.show()
            self.boxes.append(box)

    def resizeEvent(self, event):
        self.showBoxes()
        super().resizeEvent(event)

class LineBand(QWidget):
    def __init__(self, color):  # define color within __init__
        super().__init__(self)
        self.color = color


    def  paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setPen(QPen(color, 1.8)) # call setPen with color
        painter.drawLine(self.rect().topLeft(), self.rect().bottomRight())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    window.showBoxes()
    app.exec_()
    sys.exit(app.exec_())

Solution

  • When a method is not overwritten it will be the same as the implemented method of the parent so if you want it to work you must add those parameters, since these depend many times on the parent a simple way is to use *args and **kwargs and pass the new parameter as the first parameter. In addition you must use self.color instead of color since color only exists in the constructor.

    class Window(QWidget):
        [...]
        def showBoxes(self):
            while self.boxes:
                self.boxes.pop().deleteLater()
            viewport = self.edit.viewport()
            for start, end, id, color in db:  # get color too
                rect = self.edit.getBoundingRect(start, end)
                box = LineBand(color, viewport) # call LineBand with color as argument
                box.setGeometry(rect)
                box.show()
                self.boxes.append(box)
        [...]
    
    class LineBand(QWidget):
        def __init__(self, color, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
            self.color = color
    
    
        def  paintEvent(self, event):
            painter = QPainter(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QPen(self.color, 1.8)) # call setPen with color
            painter.drawLine(self.rect().topLeft(), self.rect().bottomRight())
    

    Output:

    enter image description here