Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. How to have a button in QML execute a function laid out in Python (PySide6) file?
QtWS25 Last Chance

How to have a button in QML execute a function laid out in Python (PySide6) file?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
10 Posts 2 Posters 2.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I Offline
    I Offline
    I.Mironov
    wrote on last edited by
    #1

    I've spent the last 5 hours trying to find an answer, to no avail, and I'm at my wit's end, so I'm taking a break. I'm hoping someone on here will be able to help point me in the right direction. There is also a TL;DR: at the bottom. :)

    First off, to help with understanding what I'm dealing with, here is a link to my project on GitHub so that anyone can see all of its code:
    https://github.com/ion-mironov/LED_control_panel

    Main.qml contains the code for the GUI, led_matrix.py has the code for the animations, and main,py runs handles everything.

    Currently, in my QML file I have some Images that I have set up to behave as buttons. Here's an excerpt:

    Image {
      id: leftSignal
      source: 'images/left_' + leftSignal.buttonState + '.svg'
      Layout.fillWidth: true
      Layout.fillHeight: true
      fillMode: Image.PreserveAspectFit
      property string buttonState: "off"
    
      states: [
          State { name: 'off' },
          State { name: 'on' }
      ]
    
      MouseArea {
        anchors.fill: parent
    
        onClicked: {
          rightSignal.buttonState = 'off';
          brakeLights.buttonState = 'off';
          parkingLights.buttonState = 'off';
          leftSignal.buttonState = (leftSignal.buttonState === 'on') ? 'off' : 'on';
          }
       }
    }
    

    They toggle on and off just fine. What I am wanting to accomplish now is to have each of them execute their respective LED animations when they're clicked/tapped on.

    I'm currently stuck on how to accomplish this, as searching for "how to execute Python function with QML button" (and variations of those search parameters) nets some scattered responses, and most of them don't actually come close to what I'm wanting to achieve. Also, the Qt documentation has not really been helpful in this case, especially to a novice such as myself.

    TL;DR: I made custom buttons in QML and I want to trigger different LED animations when I select any of them, but can't find the right answer.

    B 1 Reply Last reply
    1
    • I I.Mironov

      @Bob64 Yes, that's actually what I've been attempting all last night, but a lot of what I found online wasn't helpful or related to what I was wanting. Hence the reason why I'm asking on the forum; I genuinely do not know what I should do. I couldn't even find proper code examples for my problem. Most of what I found was either seriously outdated or only focusing on how to do one thing in either QML or Python but not utilizing both.

      I'm sure it's something really simple and easily overlooked, but I have not figured it out, which is annoying and a bit frustrating.

      B Offline
      B Offline
      Bob64
      wrote on last edited by Bob64
      #6

      @I-Mironov does this help?

      main.py

      import sys
      from pathlib import Path
      
      from PySide6.QtCore import QObject, Slot
      from PySide6.QtGui import QGuiApplication
      from PySide6.QtQml import QQmlApplicationEngine
      
      class ControlPanel(QObject):
          def __init__(self):
              super().__init__()
              
          @Slot(str)
          def do_it(self, id):
              print(f"do_it called with {id}!")
      
      
      if __name__ == '__main__':
          app = QGuiApplication(sys.argv)
          engine = QQmlApplicationEngine()
      
          controlPanel = ControlPanel()
          engine.rootContext().setContextProperty("controlPanel", controlPanel)
      
          qml_file = Path(__file__).parent / 'main.qml'
          engine.load(qml_file)
      
          if not engine.rootObjects():
              sys.exit(-1)
      
          sys.exit(app.exec())
      
      

      main.qml

      import QtQuick 2.0
      import QtQuick.Controls 2.1
      import QtQuick.Window 2.1
      
      ApplicationWindow {
          height: 400; width: 800
          visible: true
      
          Button {
              anchors.centerIn: parent
              height: 100; width: 200	
      	text: "Click Here"
      	onClicked: {
      	    controlPanel.do_it("CLICKED!")
      	}
          }
      }
      
      I 2 Replies Last reply
      3
      • I I.Mironov

        I've spent the last 5 hours trying to find an answer, to no avail, and I'm at my wit's end, so I'm taking a break. I'm hoping someone on here will be able to help point me in the right direction. There is also a TL;DR: at the bottom. :)

        First off, to help with understanding what I'm dealing with, here is a link to my project on GitHub so that anyone can see all of its code:
        https://github.com/ion-mironov/LED_control_panel

        Main.qml contains the code for the GUI, led_matrix.py has the code for the animations, and main,py runs handles everything.

        Currently, in my QML file I have some Images that I have set up to behave as buttons. Here's an excerpt:

        Image {
          id: leftSignal
          source: 'images/left_' + leftSignal.buttonState + '.svg'
          Layout.fillWidth: true
          Layout.fillHeight: true
          fillMode: Image.PreserveAspectFit
          property string buttonState: "off"
        
          states: [
              State { name: 'off' },
              State { name: 'on' }
          ]
        
          MouseArea {
            anchors.fill: parent
        
            onClicked: {
              rightSignal.buttonState = 'off';
              brakeLights.buttonState = 'off';
              parkingLights.buttonState = 'off';
              leftSignal.buttonState = (leftSignal.buttonState === 'on') ? 'off' : 'on';
              }
           }
        }
        

        They toggle on and off just fine. What I am wanting to accomplish now is to have each of them execute their respective LED animations when they're clicked/tapped on.

        I'm currently stuck on how to accomplish this, as searching for "how to execute Python function with QML button" (and variations of those search parameters) nets some scattered responses, and most of them don't actually come close to what I'm wanting to achieve. Also, the Qt documentation has not really been helpful in this case, especially to a novice such as myself.

        TL;DR: I made custom buttons in QML and I want to trigger different LED animations when I select any of them, but can't find the right answer.

        B Offline
        B Offline
        Bob64
        wrote on last edited by Bob64
        #2

        @I-Mironov I haven't looked at your project in detail and also I am a C++/QML rather than Python/QML user, but is the issue just that you want to call the runAnimation method when a button is pressed?

        In that case you should be able to call controlPanel.runAnimation(...) (with appropriate arguments) from your button clicked handler.

        Edit: note that this works because you have exposed controlPanel as a context property to QML, and I assume (by analogy with what I know about C++) that decorating runAnimation with the @Slot decorator is sufficient to make it callable from QML.

        I 1 Reply Last reply
        0
        • B Bob64

          @I-Mironov I haven't looked at your project in detail and also I am a C++/QML rather than Python/QML user, but is the issue just that you want to call the runAnimation method when a button is pressed?

          In that case you should be able to call controlPanel.runAnimation(...) (with appropriate arguments) from your button clicked handler.

          Edit: note that this works because you have exposed controlPanel as a context property to QML, and I assume (by analogy with what I know about C++) that decorating runAnimation with the @Slot decorator is sufficient to make it callable from QML.

          I Offline
          I Offline
          I.Mironov
          wrote on last edited by I.Mironov
          #3

          @Bob64 Crap, I forgot that main,py had some additional functions and statements in them, of which I wasn't sure if some of them were wrong and need to be edited.

          I was having issues with the runAnimation being callable in my Main.qml file; I was getting errors about "Unqualified access" or "Unused import" for it (I'm using VS Code). I read that you cannot import Python files into QML, and that's where I was starting to hit a wall of figuring out how to get everything to work.

          B 1 Reply Last reply
          0
          • I I.Mironov

            @Bob64 Crap, I forgot that main,py had some additional functions and statements in them, of which I wasn't sure if some of them were wrong and need to be edited.

            I was having issues with the runAnimation being callable in my Main.qml file; I was getting errors about "Unqualified access" or "Unused import" for it (I'm using VS Code). I read that you cannot import Python files into QML, and that's where I was starting to hit a wall of figuring out how to get everything to work.

            B Offline
            B Offline
            Bob64
            wrote on last edited by
            #4

            @I-Mironov it is correct that you can't import Python files into QML, but you should not need to do that. What you are doing looks like it is roughly along the right lines. The idea is that you are passing something callable into the QML layer.

            I think you should simplify what you are trying to do down to the most basic thing. You want to invoke a method that you define in your Python code when you click a button defined in QML code. If I were you I would make a new simple project just focusing on this. This is the sort of thing I did a lot when I was first learning QML (and sometimes still do) - I made lots of tiny projects to figure out how to do the individual things I wanted to do and once I was comfortable with them I brought them into my "real" project.

            I 1 Reply Last reply
            0
            • B Bob64

              @I-Mironov it is correct that you can't import Python files into QML, but you should not need to do that. What you are doing looks like it is roughly along the right lines. The idea is that you are passing something callable into the QML layer.

              I think you should simplify what you are trying to do down to the most basic thing. You want to invoke a method that you define in your Python code when you click a button defined in QML code. If I were you I would make a new simple project just focusing on this. This is the sort of thing I did a lot when I was first learning QML (and sometimes still do) - I made lots of tiny projects to figure out how to do the individual things I wanted to do and once I was comfortable with them I brought them into my "real" project.

              I Offline
              I Offline
              I.Mironov
              wrote on last edited by I.Mironov
              #5

              @Bob64 Yes, that's actually what I've been attempting all last night, but a lot of what I found online wasn't helpful or related to what I was wanting. Hence the reason why I'm asking on the forum; I genuinely do not know what I should do. I couldn't even find proper code examples for my problem. Most of what I found was either seriously outdated or only focusing on how to do one thing in either QML or Python but not utilizing both.

              I'm sure it's something really simple and easily overlooked, but I have not figured it out, which is annoying and a bit frustrating.

              B 1 Reply Last reply
              0
              • I I.Mironov

                @Bob64 Yes, that's actually what I've been attempting all last night, but a lot of what I found online wasn't helpful or related to what I was wanting. Hence the reason why I'm asking on the forum; I genuinely do not know what I should do. I couldn't even find proper code examples for my problem. Most of what I found was either seriously outdated or only focusing on how to do one thing in either QML or Python but not utilizing both.

                I'm sure it's something really simple and easily overlooked, but I have not figured it out, which is annoying and a bit frustrating.

                B Offline
                B Offline
                Bob64
                wrote on last edited by Bob64
                #6

                @I-Mironov does this help?

                main.py

                import sys
                from pathlib import Path
                
                from PySide6.QtCore import QObject, Slot
                from PySide6.QtGui import QGuiApplication
                from PySide6.QtQml import QQmlApplicationEngine
                
                class ControlPanel(QObject):
                    def __init__(self):
                        super().__init__()
                        
                    @Slot(str)
                    def do_it(self, id):
                        print(f"do_it called with {id}!")
                
                
                if __name__ == '__main__':
                    app = QGuiApplication(sys.argv)
                    engine = QQmlApplicationEngine()
                
                    controlPanel = ControlPanel()
                    engine.rootContext().setContextProperty("controlPanel", controlPanel)
                
                    qml_file = Path(__file__).parent / 'main.qml'
                    engine.load(qml_file)
                
                    if not engine.rootObjects():
                        sys.exit(-1)
                
                    sys.exit(app.exec())
                
                

                main.qml

                import QtQuick 2.0
                import QtQuick.Controls 2.1
                import QtQuick.Window 2.1
                
                ApplicationWindow {
                    height: 400; width: 800
                    visible: true
                
                    Button {
                        anchors.centerIn: parent
                        height: 100; width: 200	
                	text: "Click Here"
                	onClicked: {
                	    controlPanel.do_it("CLICKED!")
                	}
                    }
                }
                
                I 2 Replies Last reply
                3
                • B Bob64

                  @I-Mironov does this help?

                  main.py

                  import sys
                  from pathlib import Path
                  
                  from PySide6.QtCore import QObject, Slot
                  from PySide6.QtGui import QGuiApplication
                  from PySide6.QtQml import QQmlApplicationEngine
                  
                  class ControlPanel(QObject):
                      def __init__(self):
                          super().__init__()
                          
                      @Slot(str)
                      def do_it(self, id):
                          print(f"do_it called with {id}!")
                  
                  
                  if __name__ == '__main__':
                      app = QGuiApplication(sys.argv)
                      engine = QQmlApplicationEngine()
                  
                      controlPanel = ControlPanel()
                      engine.rootContext().setContextProperty("controlPanel", controlPanel)
                  
                      qml_file = Path(__file__).parent / 'main.qml'
                      engine.load(qml_file)
                  
                      if not engine.rootObjects():
                          sys.exit(-1)
                  
                      sys.exit(app.exec())
                  
                  

                  main.qml

                  import QtQuick 2.0
                  import QtQuick.Controls 2.1
                  import QtQuick.Window 2.1
                  
                  ApplicationWindow {
                      height: 400; width: 800
                      visible: true
                  
                      Button {
                          anchors.centerIn: parent
                          height: 100; width: 200	
                  	text: "Click Here"
                  	onClicked: {
                  	    controlPanel.do_it("CLICKED!")
                  	}
                      }
                  }
                  
                  I Offline
                  I Offline
                  I.Mironov
                  wrote on last edited by
                  #7

                  @Bob64 I will give that a shot right now and report back. In the meantime, I figured I'd show a screenshot of what I encounter when I try using the runAnimation decorator:

                  55135454-e05b-4329-bf95-f845a78128b3-image.png

                  Now, this question came into my head: should I try testing this on my Raspberry Pi?

                  I'm building this project on my desktop but it's my Pi that would be the main source for handling the LED animations, since rpi_ws281x can't really be handled by Windows.

                  1 Reply Last reply
                  0
                  • B Bob64

                    @I-Mironov does this help?

                    main.py

                    import sys
                    from pathlib import Path
                    
                    from PySide6.QtCore import QObject, Slot
                    from PySide6.QtGui import QGuiApplication
                    from PySide6.QtQml import QQmlApplicationEngine
                    
                    class ControlPanel(QObject):
                        def __init__(self):
                            super().__init__()
                            
                        @Slot(str)
                        def do_it(self, id):
                            print(f"do_it called with {id}!")
                    
                    
                    if __name__ == '__main__':
                        app = QGuiApplication(sys.argv)
                        engine = QQmlApplicationEngine()
                    
                        controlPanel = ControlPanel()
                        engine.rootContext().setContextProperty("controlPanel", controlPanel)
                    
                        qml_file = Path(__file__).parent / 'main.qml'
                        engine.load(qml_file)
                    
                        if not engine.rootObjects():
                            sys.exit(-1)
                    
                        sys.exit(app.exec())
                    
                    

                    main.qml

                    import QtQuick 2.0
                    import QtQuick.Controls 2.1
                    import QtQuick.Window 2.1
                    
                    ApplicationWindow {
                        height: 400; width: 800
                        visible: true
                    
                        Button {
                            anchors.centerIn: parent
                            height: 100; width: 200	
                    	text: "Click Here"
                    	onClicked: {
                    	    controlPanel.do_it("CLICKED!")
                    	}
                        }
                    }
                    
                    I Offline
                    I Offline
                    I.Mironov
                    wrote on last edited by I.Mironov
                    #8

                    @Bob64 said in How to have a button in QML execute a function laid out in Python (PySide6) file?:

                    @I-Mironov does this help?

                    Son. Of. A. Bi--...

                    You have got to be kidding me. I honestly thought that because that "Unqualified access" error showed, that it would not work at all. But, I went and tested your code and it was still able to work. I genuinely, GENUINELY thought it wouldn't just because of that error. So, I never bothered to actually test it on my Pi.

                    e2d9acf0-3d59-4dcc-b8ea-eea0534a8780-image.png

                    1 Reply Last reply
                    1
                    • I Offline
                      I Offline
                      I.Mironov
                      wrote on last edited by I.Mironov
                      #9
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • I Offline
                        I Offline
                        I.Mironov
                        wrote on last edited by
                        #10

                        Okay, so now I know that I can ignore things like "Unqualified access" when it comes to something like this.

                        But, now I'm facing a new problem: I cannot get the LEDs to do anything. I've tested them with a simple script to ensure they do work, and they do. So, there's something going on with my project's code somewhere that is causing them to not work correctly. I've included print statements which do show up, just not the lights.

                        I think I'll be better off making a simple QML and Python setup to test a basic light animation and see what's going on.

                        1 Reply Last reply
                        0
                        • I I.Mironov has marked this topic as solved on

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved