• Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Register
  • Login
UBports Robot Logo UBports Forum
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Register
  • Login

Porting my Qml + Python app to Ubports(Beginner)

Scheduled Pinned Locked Moved Solved App Development
16 Posts 6 Posters 1.5k Views 3 Watching
Loading More Posts
  • 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.
    • A Offline
      aarontheissueguy
      last edited by 3 Jan 2021, 16:38

      Hello everyone,
      I have recently started learning Qml and thought it would be fun to make a small App that counts the amount of water you drink per day with a simple GUI. I started programming with Atom and everything went well. If I use qmlscene on my Pc to run the application everything seems to work fine. I finally decided to port my App to UbPorts because I knew qml + python is compatible with it. I tinkered with Clickable a bit and everything seemed to work with qml. I copied over my files made some modifications to my qml using the template given by clickable and tried to send it over to my VollaPhone using ssh. The app installed but nothing seemed to work. Images didnt appear python didnt seem to work and so on... only the pure qml worked properly. I think the files just were not detected properly. do I need to use a specific tool or api to refer to file locations? Did I do wrong more than this?(Very well possible). Do I need to tweak clickable configs? Ive looked through the documentation several times but just couldnt get it to work. Here is the Important stuff of my original project(I excuse the spaghetti, I didnt think I would share this Code šŸ™‚ :
      main.py:

      from datetime import date
      def addWater(amount): #add a defined amount in ml of water to the today.txt file
          file = open( "data/" + str(date) + ".txt","a")
          file.write(str(amount) + "\n")
          file.close()
      
      def undoWater(): #delete the last line of the today.txt file
          file = open("today.txt","r+")
          lines = []
          for line in file:
              lines.append(str(line))
          #print(lines)
          del lines[-1]
          file.seek(0)
          file.truncate()
          #print(lines)
          file.close()
          file = open("today.txt","a")
          for line in lines:
              file.write(line)
          file.close()
      
      def storeUnit(unit):
          file = open( "data/" + "unit" + ".txt","a")
          file.seek(0)
          file.truncate()
          file.write(str(unit))
          file.close()
      
      def returnUnit():
          file = open( "data/" + "unit" + ".txt","r")
          unit = file.readlines()
          print(unit[0])
          file.close()
          return unit[0]
      
      def storeGoal(goal):
          file = open( "data/" + "goal" + ".txt","a")
          file.seek(0)
          file.truncate()
          file.write(str(goal))
          file.close()
      
      def returnGoal():
          file = open( "data/" + "goal" + ".txt","r")
          goal = file.readlines()
          print(goal[0])
          file.close()
          return goal[0]
      
      def progressImage():
          file = open( "data/" + str(date) + ".txt","r")
          goal = returnGoal()
          progressList = file.readlines()
          progress = 0
          for line in progressList:
              progress += int(line)
              print(progress)
          progress = float(progress) / float(goal)
          if progress >= 1.0:
              return "images/glass5"
          elif progress >= 0.75:
              return "images/glass4"
          elif progress >= 0.5:
              return "images/glass3"
          elif progress >= 0.25:
              return "images/glass2"
          elif progress >= 0.0:
              return "images/glass1"
          print(progress)
          file.close()
      
      def progressPercent():
              file = open( "data/" + str(date) + ".txt","r")
              goal = returnGoal()
              progressList = file.readlines()
              progress = 0
              for line in progressList:
                  progress += int(line)
                  print(progress)
              progress = float(progress) / float(goal)
              return progress
      
      
      date = date.today()
      addWater(0)
      

      Main.qml:

      import QtQuick 2.7
      //import Ubuntu.Components 1.3
      import QtQuick.Window 2.0
      //import QtQuick.Controls 2.2
      import QtQuick.Layouts 1.3
      import Qt.labs.settings 1.0
      import io.thp.pyotherside 1.3
      Window {
          id: root
          //objectName: 'mainView'
          //applicationName: 'watercount.aaron'
          //automaticOrientation: true
      
          width: 1080
          height: 1920
          Python {
            id: py
            Component.onCompleted: {
                // Print version of plugin and Python interpreter
                console.log('PyOtherSide version: ' + pluginVersion());
                console.log('Python version: ' + pythonVersion());
      
                addImportPath(Qt.resolvedUrl('.'));
                importModule('main', function() {});
                console.log('after importModule');
                py.call('main.progressImage', [], function(result) {
                    console.log("after call of progressImage")
                    progressImage.source = result
                })
                py.call('main.returnGoal', [], function(result) {
                    console.log("after call of returnGoal")
                    goal.text = result
                })
                py.call('main.returnUnit', [], function(result) {
                    console.log("after call of returnUnit")
                    unit.text = result
                })
                py.call('main.progressPercent', [], function(result) {
                    console.log("after call of progressPercent")
                    progressBar.width = result * rectangleBg.width
                })
              }
          }
          Image{
            id: progressImage
            source: Qt.resolvedUrl('../WaterCount/images/glass1.png')
            width: root.width / 1.5
            height: progressImage.width
            x: root.width / 2 - progressImage.width / 2
            y: root.height / 8
            MouseArea {
                anchors.fill: parent
                hoverEnabled: false
                onPressed: {
                  parent.scale = 0.8
                }
                onReleased: {
                  py.call('main.addWater', [unit.text], function(result) {
                      console.log("after call of addWater")
                  })
                  py.call('main.progressImage', [], function(result) {
                      console.log("after call of progressImage")
                      progressImage.source = result
                  })
                  py.call('main.progressPercent', [], function(result) {
                      console.log("after call of progressPercent")
                      progressBar.width = result * rectangleBg.width
                  })
                  parent.scale = 1
      
                }
            }
      
          }
          Rectangle{
            id: goalRectangle
            anchors.left: progressImage.left
            anchors.top: progressImage.bottom
            anchors.topMargin: progressImage.height / 15
            anchors.leftMargin: goalText.width
            width: goal.width + 10
            height: goal.height + 10
            color: "grey" //#455a64
            TextInput{
              id: goal
              anchors.centerIn: parent
              text: "2000"
              font.pixelSize: progressImage.width / 15
              Text{
                id: goalText
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.left
                text: "Goal: "
                font.pixelSize: progressImage.width / 15
              }
              Text{
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.right
                text: " ml"
                font.pixelSize: progressImage.width / 15
              }
            }
          }
          Rectangle{
            id: saveGoalRectangle
            width: saveGoalText.width + 10
            height: saveGoalText.height + 10
            color: "grey"
            anchors.right: progressImage.right
            anchors.verticalCenter: goalRectangle.verticalCenter
            Text{
              id: saveGoalText
              text: "Save"
              font.pixelSize: goal.font.pixelSize
              anchors.centerIn:  parent
            }
            MouseArea {
                anchors.fill: parent
                onPressed: {
                  saveGoalRectangle.scale = 0.8
                  saveGoalText.font.pixelSize = saveGoalText.font.pixelSize * 0.8
                }
                onReleased: {
                  py.call('main.storeGoal', [goal.text], function(result) {
                      console.log("after call of storeGoal")
                  })
                  py.call('main.progressImage', [], function(result) {
                      console.log("after call of progressImage")
                      progressImage.source = result
                  })
                  py.call('main.progressPercent', [], function(result) {
                      console.log("after call of progressPercent")
                      progressBar.width = result * rectangleBg.width
                  })
                  saveGoalRectangle.scale = 1
                  saveGoalText.font.pixelSize = saveGoalText.font.pixelSize * 1.2
      
                }
            }
          }
          Rectangle{
            id: unitRectangle
            anchors.left: progressImage.left
            anchors.top: goalRectangle.bottom
            anchors.topMargin: progressImage.height / 15
            anchors.leftMargin: unitText.width
            width: unit.width + 10
            height: unit.height + 10
            color: "grey" //#455a64
            TextInput{
              id: unit
              anchors.centerIn: parent
              text: "250"
              font.pixelSize: progressImage.width / 15
              Text{
                id: unitText
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.left
                text: "Unit: "
                font.pixelSize: progressImage.width / 15
              }
              Text{
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.right
                text: " ml"
                font.pixelSize: progressImage.width / 15
              }
            }
          }
          Rectangle{
            id: saveUnitRectangle
            width: saveGoalText.width + 10
            height: saveGoalText.height + 10
            color: "grey"
            anchors.right: progressImage.right
            anchors.verticalCenter: unitRectangle.verticalCenter
            Text{
              id: saveUnitText
              text: "Save"
              font.pixelSize: unit.font.pixelSize
              anchors.centerIn:  parent
            }
            MouseArea {
                anchors.fill: parent
                onPressed: {
                  saveUnitRectangle.scale = 0.8
                  saveUnitText.font.pixelSize = saveUnitText.font.pixelSize * 0.8
                }
                onReleased: {
                  py.call('main.storeUnit', [unit.text], function(result) {
                      console.log("after call of storeUnit")
                  })
                  py.call('main.progressImage', [], function(result) {
                      console.log("after call of progressImage")
                      progressImage.source = result
                  })
                  saveUnitRectangle.scale = 1
                  saveUnitText.font.pixelSize = saveUnitText.font.pixelSize * 1.2
      
                }
            }
          }
          Rectangle{
            anchors.fill: parent
            z: -2
            color: "#587684"
          }
          Rectangle{
            id: rectangleBg
            clip: true
            anchors.left: progressImage.left
            anchors.right: progressImage.right
            anchors.top: progressImage.bottom
            anchors.bottom: parent.bottom
            anchors.leftMargin: -(progressImage.width / 8)
            anchors.rightMargin: -(progressImage.width / 8)
            anchors.bottomMargin: (progressImage.width / 8)
            z:-1
            color: "#455a64"
            Rectangle{
              clip: true
              anchors.bottom: parent.bottom
              anchors.bottomMargin: parent.height / 10
              id: progressBar
              color: "lightblue"
              height: parent.height/ 15
            }
            Text{
              anchors.bottom: progressBar.top
              anchors.bottomMargin: progressBar.height
              anchors.horizontalCenter: parent.horizontalCenter
              text: "Progress:"
              font.pixelSize: progressImage.width / 15
            }
          }
      }
      
      

      file structure:

      .
      ā”œā”€ā”€ data
      │   ā”œā”€ā”€ 2021-01-03.txt
      │   ā”œā”€ā”€ goal.txt
      │   └── unit.txt
      ā”œā”€ā”€ images
      │   ā”œā”€ā”€ glass1.png
      │   ā”œā”€ā”€ glass2.png
      │   ā”œā”€ā”€ glass3.png
      │   ā”œā”€ā”€ glass4.png
      │   └── glass5.png
      ā”œā”€ā”€ main.cpp
      ā”œā”€ā”€ main.py
      ā”œā”€ā”€ Main.qml
      ā”œā”€ā”€ __pycache__
      │   └── main.cpython-38.pyc
      ā”œā”€ā”€ qml.qrc
      ā”œā”€ā”€ WaterCount.pro
      └── WaterCount.pro.user
      
      

      GUI:
      Screenshot from 2021-01-03 17-36-05.png

      Thank you! Aaron

      joniusJ M 2 Replies Last reply 3 Jan 2021, 18:07 Reply Quote 0
      • joniusJ Offline
        jonius @aarontheissueguy
        last edited by 3 Jan 2021, 18:07

        @aarontheissueguy does it work in Desktop Mode (clickable desktop)? Could you upload the whole app and clickable config somewhere (e.g. Gitlab) and provide the link?

        A 2 Replies Last reply 3 Jan 2021, 19:25 Reply Quote 1
        • A Offline
          aarontheissueguy @jonius
          last edited by 3 Jan 2021, 19:25

          @jonius said in Porting my Qml + Python app to Ubports(Beginner):

          clickable desktop

          Ok I decided to reorganize my project and add some comments to make it more structured. Clickable desktop returns pretty much the same result as on mobile. I uploaded the updated version on GitHub https://github.com/Aarontheissueguy/WaterCount a Clickable is included in the build directory. Thanks for taking the time. I have no Idea what is causing the issue.

          AppLeeA 1 Reply Last reply 3 Jan 2021, 19:52 Reply Quote 0
          • AppLeeA Offline
            AppLee @aarontheissueguy
            last edited by 3 Jan 2021, 19:52

            Hi @aarontheissueguy

            Did you create a click package ?
            I think you can achieve your goal with copying the correct file at the right location, but it's easier to create a click file and install it after pushing it to your device.

            A 1 Reply Last reply 3 Jan 2021, 20:36 Reply Quote 1
            • M Offline
              makeixo
              last edited by 3 Jan 2021, 20:02

              šŸ“

              1 Reply Last reply Reply Quote 1
              • A Offline
                aarontheissueguy @AppLee
                last edited by 3 Jan 2021, 20:36

                @applee Yes, you can find it in the build directory of the projekt or make it yourself with clickable.

                AppLeeA 1 Reply Last reply 3 Jan 2021, 22:17 Reply Quote 0
                • A Offline
                  aarontheissueguy @jonius
                  last edited by 3 Jan 2021, 21:01

                  @jonius A small update:
                  I managed to get

                  clickable desktop
                  

                  working! by putting the files that were stored in the data directory into the src directory. It still does not work on mobile though šŸ˜ž . Small steps ...

                  lduboeufL 1 Reply Last reply 3 Jan 2021, 21:47 Reply Quote 0
                  • lduboeufL Offline
                    lduboeuf @aarontheissueguy
                    last edited by 3 Jan 2021, 21:47

                    @aarontheissueguy do you have some logs ? clickable logs

                    A 1 Reply Last reply 4 Jan 2021, 02:16 Reply Quote 1
                    • AppLeeA Offline
                      AppLee @aarontheissueguy
                      last edited by 3 Jan 2021, 22:17

                      @aarontheissueguy
                      Oh, yes I see.
                      Sorry it was a quick answer, I didn't look at the github.
                      Another wild guess, but maybe some apparmor rights for the data files. But I don't remember any requirements for internal data organization or right management...

                      1 Reply Last reply Reply Quote 1
                      • A Offline
                        aarontheissueguy @lduboeuf
                        last edited by 4 Jan 2021, 02:16

                        @lduboeuf Here are the logs for the code in the reposetory on mobile:

                        Pseudo-terminal will not be allocated because stdin is not a terminal.
                        Welcome to Ubuntu 16.04.7 LTS (GNU/Linux 4.4.146+ aarch64)
                        
                         * Documentation:  https://help.ubuntu.com
                         * Management:     https://landscape.canonical.com
                         * Support:        https://ubuntu.com/advantage
                        "PyOtherSide error: Traceback (most recent call last):\n\n  File \"<string>\", line 1, in <module>\n\nNameError: name 'main' is not defined\n"
                        Unhandled PyOtherSide error: Function not found: 'main.progressImage' (Traceback (most recent call last):
                        
                          File "<string>", line 1, in <module>
                        
                        NameError: name 'main' is not defined
                        )
                        qml: after call of progressImage
                        Unhandled PyOtherSide error: file:///opt/click.ubuntu.com/watercount.aaron/1.0.0/qml/Main.qml:196: Error: Cannot assign [undefined] to QUrl
                        QObject::startTimer: Timers cannot be started from another thread
                        "PyOtherSide error: Traceback (most recent call last):\n\n  File \"<string>\", line 1, in <module>\n\nNameError: name 'main' is not defined\n"
                        Unhandled PyOtherSide error: Function not found: 'main.addWater' (Traceback (most recent call last):
                        
                          File "<string>", line 1, in <module>
                        
                        NameError: name 'main' is not defined
                        )
                        qml: after call of addWater
                        "PyOtherSide error: Traceback (most recent call last):\n\n  File \"<string>\", line 1, in <module>\n\nNameError: name 'main' is not defined\n"
                        Unhandled PyOtherSide error: Function not found: 'main.progressImage' (Traceback (most recent call last):
                        
                          File "<string>", line 1, in <module>
                        
                        NameError: name 'main' is not defined
                        )
                        qml: after call of progressImage
                        Unhandled PyOtherSide error: file:///opt/click.ubuntu.com/watercount.aaron/1.0.0/qml/Main.qml:63: Error: Cannot assign [undefined] to QUrl
                        "PyOtherSide error: Traceback (most recent call last):\n\n  File \"<string>\", line 1, in <module>\n\nNameError: name 'main' is not defined\n"
                        Unhandled PyOtherSide error: Function not found: 'main.progressPercent' (Traceback (most recent call last):
                        
                          File "<string>", line 1, in <module>
                        
                        NameError: name 'main' is not defined
                        )
                        qml: after call of progressPercent
                        
                        
                        
                        

                        Here are the logs for the same code on desktop(Working)

                        Mounting device home to /home/aaron/.clickable/home
                        QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-aaron'
                        QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-aaron'
                        Failed to create /home/aaron/.cache for shader cache (Permission denied)---disabling.
                        "/home/aaron/projects/WaterCountClickableDesktop/WaterCount/build/all/app/install/%U" does not exist.
                        Got library name:  "/usr/lib/x86_64-linux-gnu/qt5/qml/io/thp/pyotherside/libpyothersideplugin.so"
                        qml: PyOtherSide version: 1.5.3
                        qml: Python version: 3.5.2
                        qml: after importModule
                        qml: after call of progressImage
                        qml: after call of returnGoal
                        qml: after call of returnUnit
                        qml: after call of progressPercent
                        
                        
                        
                        1 Reply Last reply Reply Quote 0
                        • A Offline
                          aarontheissueguy
                          last edited by 4 Jan 2021, 21:36

                          !Solved (does this work here?) I was able to find a solution by making several changes to my code and reading many.. many.. logs and documentations. If you want to have a closer look at them I recommend taking a look at GitHub but ill try to break it down here anyways:

                          1. Read and write permissions. This gives enough information.
                          2. Non existent directories need to be created manually I added a python function to do that.
                          3. You cant have anything outside of your functions except import stuff because it will cause an error.
                          4. Some smaller formatting problems.

                          I might create an UpToDate beginners guide on this topic because there werent many detailed all in one guides about this topic.

                          LakotaubpL 1 Reply Last reply 5 Jan 2021, 11:37 Reply Quote 2
                          • LakotaubpL Offline
                            Lakotaubp @aarontheissueguy
                            last edited by 5 Jan 2021, 11:37

                            @aarontheissueguy Ifb you nwant to mark this as solved use Topic Tools (cog wheel top right) ask as question, then mark as solved.

                            A 1 Reply Last reply 5 Jan 2021, 19:27 Reply Quote 0
                            • A Offline
                              aarontheissueguy @Lakotaubp
                              last edited by 5 Jan 2021, 19:27

                              This post is deleted!
                              1 Reply Last reply Reply Quote 0
                              • joniusJ Offline
                                jonius
                                last edited by 6 Jan 2021, 10:56

                                Great you solved it. Improved documentation is always a big help! šŸ™‚

                                1 Reply Last reply Reply Quote 0
                                • M Offline
                                  makeixo @aarontheissueguy
                                  last edited by 16 Jan 2021, 15:30

                                  @aarontheissueguy said in Porting my Qml + Python app to Ubports(Beginner):

                                  I have recently started learning Qml and thought it would be fun to make a small App that counts the amount of water you drink per day with a simple GUI.

                                  Hi Aaron,

                                  can I ask you a question? How did you start with qml? I am learning Python as well. I have written a small and very simple mental arithmetic app. Before I go on I think it may be good to write a simple gui for further development. In the book I use for learning python there is a chapter how you write a gui with ktinker, which you can run at your desktop. I wonder if there is a simple guide for beginners about qml as well.

                                  A 1 Reply Last reply 20 Jan 2021, 21:07 Reply Quote 0
                                  • A Offline
                                    aarontheissueguy @makeixo
                                    last edited by 20 Jan 2021, 21:07

                                    @makeixo I used the first episodes of the qml guide on Yputube made by KDAB. Its intendet for c++ but you dont need it to learn the basics. Python + qml is a different topic though. Ubports uses pyotherside for the communication between the two. You can take a look at my app if you want to see how to do it. (its pretty basic) https://github.com/Aarontheissueguy/WaterCount
                                    There are some other resources on this forum aswell.

                                    With that being said If Python is your first language, I recomend that you concentrate on learning it first. GUI's are'nt really that important for many fun personal projects. You can still come back to this project one or two months down the road of learning python. Feel free to ask again if you have a question.

                                    1 Reply Last reply Reply Quote 0
                                    • First post
                                      Last post