ivaquero
ivaquero

Reputation: 91

Pyside6/PyQT6: How can I change the font color on click?

I am a pyside6 learner. And I am trying to create a clock based on pyside6, and I'd like to change its font color on click. How can I make it work?

The following codes are parts of this project.

class Clock(QWidget):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self.left = 1100
        self.top = 800
        self.width = 320
        self.height = 60

        # Menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.mouseRightMenu)

        # Font
        self.font_color = 'blue'

        # UI
        self.initUI()

    def changeFontColor(self, fcolor) -> None:
        self.font_color = fcolor


   def mouseRightMenu(self, pos) -> None:
        self.menu = QMenu(self)

        # font color
        fontColorMenu = self.menu.addMenu('font color')
        self.actionB1 = QAction('black', self)
        self.actionB2 = QAction('red', self)
        self.actionB3 = QAction('yellow', self)
        self.actionB1.triggered.connect(self.changeFontColor('black'))

    def initUI(self) -> None:
        # geometry of main window
        self.setGeometry(self.left, self.top, self.width, self.height)

        # label object
        self.label = QLabel()
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setFont(font)
        self.label.setStyleSheet(f'color:{self.font_color};')

if __name__ == '__main__':

    App = QApplication(sys.argv)
    clock = Clock()
    clock.show()  # show all the widgets
    App.exit(App.exec())  # start the app

Currently changeFontColor function doesn't work, I don't know what I have miss here.

Upvotes: 0

Views: 2735

Answers (1)

musicamante
musicamante

Reputation: 48424

You are missing two fundamental aspects.

First of all self.font_color is just a variable (or, to be precise, an attribute).

You are explicitly using it within initUi() along with self.label.setStyleSheet(), but that doesn't "magically" make that variable interactive.

What you're doing would never work, and here's a basic explanation:

color = 'blue'
styleSheet = f'color: {color}'
print(styleSheet)
color = 'green'
print(styleSheet)

If you consider the above, both the print statements will return the same value: changing color doesn't change styleSheet.

That is mostly due to the fact that strings are immutable types (so, styleSheet can never be affected by any change in color), but also because there's no dynamic relation between color and styleSheet.

What actually changes the aspect of a widget is an explicit call to setStyleSheet(), and changing the string contents used for a previous call is completely useless. You must call setStyleSheet() again in order to apply it.


Then, you're doing another mistake: signal connections use callables, but you're explicitly calling the changeFontColor() function.

Consider this:

def myFunction(arg):
    print(f'arg is "{arg}"')
    return arg


# this is a call (note the parentheses), which will *execute* the function
>>> myFunction('hello')
# and will show the follwing:
arg is "hello"
# while the following will show a different result:
>>> print(myFunction('hello'))
arg is "hello"
hello

# this is a reference, which will do nothing
>>> myFunction
<function __main__.myFunction>
# notice tht difference with the above:
>>> print(myFunction)
<function myFunction at 0xZZZZZZZZ>

Using self.someObject.someSignal.connect(someFunction()) is wrong, (unless someFunction returns a reference to another function).

You're directly calling your function (that doesn't return a callable), which actually prompts an error. You probably didn't notice (IDEs are often unable to show all output and full tracebacks, and most Qt errors are not fatal), but if you run your program in a terminal or prompt you'll probably see an error saying that a TypeError occurred, because you're trying to connect to a NoneType.

What you need to do is the following:

    def changeFontColor(self, fcolor) -> None:
        # explicitly call setStyleSheet() with the color name
        self.label.setStyleSheet(f'color: {fcolor};')

    def mouseRightMenu(self, pos) -> None:
        # ...
        self.actionB1.triggered.connect(lambda: 
            self.changeFontColor('black'))

lambda creates a function reference, but doesn't call it: when the triggered signal is emitted, the function will finally be called.

The above is similar to the following:

    def changeFontColor(self) -> None:
        self.label.setStyleSheet('color: black')

    def mouseRightMenu(self, pos) -> None:
        # ...
        self.actionB1.triggered.connect(self.changeFontColor)

With the difference that we directly call the function, which internally uses a static value ('black').

I strongly suggest you to do some research on object references, callables, and object types.

Upvotes: 3

Related Questions