-1

I have created a paint UI and I want to change the size of the brush using a QSlider.

I want to call a method when the user changes the value of the slider

brushThick = QSlider(Qt.Horizontal, self)
value = brushThick.value()
brushThick.setFocusPolicy(Qt.NoFocus)
brushThick.setGeometry(30, 40, 200, 30)
lcd = QLCDNumber(self)

brushSize.valueChanged.connect(lcd.display)

I call this method when the slider has moved

brushThick.sliderMoved.connect(self.changeBrushSize(value))

The method I am calling is

def changeBrushSize(self, size):
    self.brushSize = size

I am new to python and do not understand why it is not accepting the size when it is passed. I store the size using the value() which returns a int. I can set brushSize manually and it works but not dynamically when the user uses the slider.

The error I am getting is

brushThick.sliderMoved.connect(self.changeBrushSize(value))
TypeError: changeBrushSize() takes 1 positional argument but 2 were given

I have read this post but still do not understand

TypeError: method() takes 1 positional argument but 2 were given

Full Code

from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QFileDialog, QSlider, QLabel, QVBoxLayout, QLCDNumber, \
    QFrame, QHBoxLayout, QSplitter, QGridLayout
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen, QPixmap
import sys
from PyQt5.QtCore import Qt, QPoint

class PaintingApplication(QMainWindow): # documentation https://doc.qt.io/qt-5/qmainwindow.html
    '''
    Painting Application class
    '''
    def __init__(self):
        super().__init__()

        # set window title
        self.setWindowTitle("Paint Application")

        # set the windows dimensions
        top = 400
        left = 400
        width = 800
        height = 600
        self.setGeometry(top, left, width, height)

        #set the icon
        # windows version
        self.setWindowIcon(QIcon("./icons/paint-brush.png")) # documentation: https://doc.qt.io/qt-5/qwidget.html#windowIcon-prop
        # mac version - not yet working
        # self.setWindowIcon(QIcon(QPixmap("./icons/paint-brush.png")))

        # image settings (default)
        self.image = QImage(self.size(), QImage.Format_RGB32) # documentation: https://doc.qt.io/qt-5/qimage.html#QImage-1
        self.image.fill(Qt.white) # documentation: https://doc.qt.io/qt-5/qimage.html#fill-1

        # draw settings (default)
        self.drawing = False
        self.brushSize = 3
        self.brushColor = Qt.black # documenation: https://doc.qt.io/qtforpython/PySide2/QtCore/Qt.html

        # reference to last point recorded by mouse
        self.lastPoint = QPoint() # documenation: https://doc.qt.io/qt-5/qpoint.html

        # set up menus
        mainMenu = self.menuBar() # create and a menu bar
        fileMenu = mainMenu.addMenu(" File") # add the file menu to the menu bar, the space is required as "File" is reserved in Mac
        brushSizeMenu = mainMenu.addMenu(" Brush Size") # add the "Brush Size" menu to the menu bar
        brushColorMenu = mainMenu.addMenu(" Brush Colour") # add the "Brush Colour" menu to the menu bar

        # save menu item
        saveAction = QAction(QIcon("./icons/save.png"), "Save", self)   # create a save action with a png as an icon, documenation: https://doc.qt.io/qt-5/qaction.html
        saveAction.setShortcut("Ctrl+S")                                # connect this save action to a keyboard shortcut, documentation: https://doc.qt.io/qt-5/qaction.html#shortcut-prop
        fileMenu.addAction(saveAction)                                  # add the save action to the file menu, documentation: https://doc.qt.io/qt-5/qwidget.html#addAction
        saveAction.triggered.connect(self.save)                         # when the menu option is selected or the shortcut is used the save slot is triggered, documenation: https://doc.qt.io/qt-5/qaction.html#triggered

        # clear
        clearAction = QAction(QIcon("./icons/clear.png"), "Clear", self) # create a clear action with a png as an icon
        clearAction.setShortcut("Ctrl+C")                                # connect this clear action to a keyboard shortcut
        fileMenu.addAction(clearAction)                                  # add this action to the file menu
        clearAction.triggered.connect(self.clear)                        # when the menu option is selected or the shortcut is used the clear slot is triggered


        # brush thickness
        threepxAction = QAction(QIcon("./icons/threepx.png"), "3px", self)
        threepxAction.setShortcut("Ctrl+3") #TODO changed the control options to be numbers
        brushSizeMenu.addAction(threepxAction) # connect the action to the function below
        threepxAction.triggered.connect(self.threepx)

        fivepxAction = QAction(QIcon("./icons/fivepx.png"), "5px", self)
        fivepxAction.setShortcut("Ctrl+5")
        brushSizeMenu.addAction(fivepxAction)
        fivepxAction.triggered.connect(self.fivepx)

        sevenpxAction = QAction(QIcon("./icons/sevenpx.png"), "7px", self)
        sevenpxAction.setShortcut("Ctrl+7")
        brushSizeMenu.addAction(sevenpxAction)
        sevenpxAction.triggered.connect(self.sevenpx)

        ninepxAction = QAction(QIcon("./icons/ninepx.png"), "9px", self)
        ninepxAction.setShortcut("Ctrl+9")
        brushSizeMenu.addAction(ninepxAction)
        ninepxAction.triggered.connect(self.ninepx)

        # brush colors
        blackAction = QAction(QIcon("./icons/black.png"), "Black", self)
        blackAction.setShortcut("Ctrl+B")
        brushColorMenu.addAction(blackAction);
        blackAction.triggered.connect(self.black)

        redAction = QAction(QIcon("./icons/red.png"), "Red", self)
        redAction.setShortcut("Ctrl+R")
        brushColorMenu.addAction(redAction);
        redAction.triggered.connect(self.red)

        greenAction = QAction(QIcon("./icons/green.png"), "Green", self)
        greenAction.setShortcut("Ctrl+G")
        brushColorMenu.addAction(greenAction);
        greenAction.triggered.connect(self.green)

        yellowAction = QAction(QIcon("./icons/yellow.png"), "Yellow", self)
        yellowAction.setShortcut("Ctrl+Y")
        brushColorMenu.addAction(yellowAction);
        yellowAction.triggered.connect(self.yellow)



        # A QSlider to change the size of the brush
        #   - Qt.Horizontal insure the slider scales horizontally
        #   - Creating a label for the slider
        brushThick = QSlider(Qt.Horizontal, self)
        value = brushThick.value()
        brushThick.setFocusPolicy(Qt.NoFocus)
        brushThick.setGeometry(30, 40, 200, 30)
        lcd = QLCDNumber(self)

        brushThick.valueChanged.connect(lcd.display)
        brushThick.sliderMoved.connect(self.changeBrushSize(1))



        lcd.move(550, 50)
        brushThick.move(550, 100)
        self.show()



    # Change Brush Size
    def changeBrushSize(self, value):
        self.brushSize = value



    # event handlers
    def mousePressEvent(self, event):       # when the mouse is pressed, documentation: https://doc.qt.io/qt-5/qwidget.html#mousePressEvent
        if event.button() ==Qt.LeftButton:  # if the pressed button is the left button
            self.drawing = True             # enter drawing mode
            self.lastPoint = event.pos()    # save the location of the mouse press as the lastPoint
            print(self.lastPoint)           # print the lastPoint for debigging purposes

    def mouseMoveEvent(self, event):                        # when the mouse is moved, documenation: documentation: https://doc.qt.io/qt-5/qwidget.html#mouseMoveEvent
     if event.buttons() & Qt.LeftButton & self.drawing:     # if there was a press, and it was the left button and we are in drawing mode
            painter = QPainter(self.image)                  # object which allows drawing to take place on an image
            # allows the selection of brush colour, brish size, line type, cap type, join type. Images available here http://doc.qt.io/qt-5/qpen.html
            painter.setPen(QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
            painter.drawLine(self.lastPoint, event.pos())   # draw a line from the point of the orginal press to the point to where the mouse was dragged to
            self.lastPoint= event.pos()                     # set the last point to refer to the point we have just moved to, this helps when drawing the next line segment
            self.update()                                   # call the update method of the widget which calls the paintEvent of this class

    def mouseReleaseEvent(self, event):                     # when the mouse is released, documentation: https://doc.qt.io/qt-5/qwidget.html#mouseReleaseEvent
        if event.button == Qt.LeftButton:                   # if the released button is the left button, documenation: https://doc.qt.io/qt-5/qt.html#MouseButton-enum ,
            self.drawing = False                            # exit drawing mode

    # paint events
    def paintEvent(self, event):
        # you should only create and use the QPainter object in this method, it should be a local variable
        canvasPainter = QPainter(self)                      # create a new QPainter object, documenation: https://doc.qt.io/qt-5/qpainter.html
        canvasPainter.drawImage(self.rect(), self.image, self.image.rect()) # draw the image , documentation: https://doc.qt.io/qt-5/qpainter.html#drawImage-1

    # resize event - this fuction is called
    def resizeEvent(self, event):
        self.image = self.image.scaled(self.width(), self.height())

    # slots
    def save(self):
        filePath, _ = QFileDialog.getSaveFileName(self, "Save Image","", "PNG(*.png);;JPG(*.jpg *.jpeg);;All Files (*.*)")
        if filePath =="": # if the file path is empty
            return # do nothing and return
        self.image.save(filePath) # save file image to the file path


    def clear(self):
        self.image.fill(Qt.white)   # fill the image with white, documentaiton: https://doc.qt.io/qt-5/qimage.html#fill-2
        self.update()               # call the update method of the widget which calls the paintEvent of this class

    def threepx(self):              # the brush size is set to 3
        self.brushSize = 3

    def fivepx(self):
        self.brushSize = 5

    def sevenpx(self):
        self.brushSize = 7

    def ninepx(self):
        self.brushSize = 9

    def black(self):                # the brush color is set to black
        self.brushColor = Qt.black

    def black(self):
        self.brushColor = Qt.black

    def red(self):
        self.brushColor = Qt.red

    def green(self):
        self.brushColor = Qt.green

    def yellow(self):
        self.brushColor = Qt.yellow

    # open a file
    def open(self):
        '''
        This is an additional function which is not part of the tutorial. It will allow you to:
         - open a file doalog box,
         - filter the list of files according to file extension
         - set the QImage of your application (self.image) to a scaled version of the file)
         - update the widget
        '''
        filePath, _ = QFileDialog.getOpenFileName(self, "Open Image", "",
                                                  "PNG(*.png);;JPG(*.jpg *.jpeg);;All Files (*.*)")
        if filePath == "":   # if not file is selected exit
            return
        with open(filePath, 'rb') as f: #open the file in binary mode for reading
            content = f.read() # read the file
        self.image.loadFromData(content) # load the data into the file
        width = self.width() # get the width of the current QImage in your application
        height = self.height() # get the height of the current QImage in your application
        self.image = self.image.scaled(width, height) # scale the image from file and put it in your QImage
        self.update() # call the update method of the widget which calls the paintEvent of this class



if __name__=="__main__":
    app = QApplication(sys.argv)
    window = PaintingApplication()
    window.show()
    app.exec() # start the event loop running
Dave
  • 97
  • 8
  • I suspect you have multiple definitions of the method. Post your full code. – Barmar Dec 02 '20 at 21:00
  • I have posted the full code, it is towards the bottom before the methods – Dave Dec 02 '20 at 21:01
  • 1
    I don't see a problem with the call to `self.changeBrushSize()`. However, you're using the result as an argument to `connect()`, and the function doesn't return anything. – Barmar Dec 02 '20 at 21:06
  • Didn't you post the same question earlier today? Anyway, I don't get your error _"method takes 1 argument but 2 were given"_. Instead, I get _"brushThick.sliderMoved.connect(self.changeBrushSize(1)) TypeError: argument 1 has unexpected type 'NoneType'"_. You need to look up how to use `connect()` – Pranav Hosangadi Dec 02 '20 at 21:06
  • Yes but I went to try solve it again but failed – Dave Dec 02 '20 at 21:08
  • 1
    Add `return value` to the end of `def changeBrushSize`. – Barmar Dec 02 '20 at 21:08
  • 1
    And change that line to `brushThick.sliderMoved.connect(self.changeBrushSize)`. Then it works as expected https://i.stack.imgur.com/ah7iW.png – Pranav Hosangadi Dec 02 '20 at 21:09
  • 1
    Please do not delete a question just to repost another one which is almost the same as [the deleted one](https://stackoverflow.com/questions/65114796/why-can-i-not-call-this-method-in-python-using-pyqt5), it's considered abuse, and users with enough reputation can see deleted posts also. – musicamante Dec 02 '20 at 21:17

1 Answers1

2

There are two very important problems:

  • while your changeBrushSize function has two (positional) arguments, the first (self) is always referred to the instance, so you should not normally provide it: when an instance method is called, the self is always implied, so the call should be self.changeBrushSize(5), since only the second size argument will be used; the last error you got is because you tried to provide more arguments, and there's no use to that;
  • connect() takes a callable as argument, while you are actually executing the function (which probably returns None), and even if you removed that self as explained above, the program will crash anyway because what the function returns is not a callable;

If you want to update the self.brushSize value when the slider changes, do the following:

    brushSize.sliderMoved.connect(self.changeBrushSize)

As you can see, there's no parenthesis. When the signal is emitted, Qt will call all slots/functions connected to it, using the signal arguments.

I strongly suggest you to read and study more about the following topics starting with the related links, as they represent the basics and are fundamentals to python and programming in general, and can never be ignored:

musicamante
  • 41,230
  • 6
  • 33
  • 58