Results 1 to 10 of 10

Thread: Stuck with QNetworkManager and Q_INVOKABLE

  1. #1
    Join Date
    Apr 2014
    Posts
    116
    Qt products
    Qt5
    Platforms
    MacOS X
    Thanks
    8

    Default Stuck with QNetworkManager and Q_INVOKABLE

    Hi,

    I am really stuck with my code. My plan is to write a c++ class for my mobile app (Sailfish OS) that looks if a xml file is stored on the device, if so if is up to date by comparing it with one on a web server. When not up to date than to download it. The result shall be display with QML.
    The class is done and does what I want but I have trouble to integrate it into QML. One thing is that some functions are accessible from QML and others are not and I do not see why. Accessible are load(), replyFinished() and getHeaders(). The other is that the return of load() is emitted before the network things are done. How can I change that?

    .h
    Qt Code:
    1. #ifndef XMLFILEHANDLER_H
    2. #define XMLFILEHANDLER_H
    3.  
    4. #include <QObject>
    5. #include <QFile>
    6. #include <QFileInfo>
    7. #include <QDir>
    8. #include <QStandardPaths>
    9. #include <QTextStream>
    10. #include <QNetworkAccessManager>
    11. #include <QNetworkRequest>
    12. #include <QNetworkReply>
    13. #include <QUrl>
    14. #include <QDateTime>
    15. #include <QDebug>
    16.  
    17. class XmlFileHandler : public QObject
    18. {
    19. Q_OBJECT
    20. public:
    21. explicit XmlFileHandler(QObject *parent = 0);
    22. Q_PROPERTY(int fileStat READ fileStat WRITE setFileStat NOTIFY fileStatChanged)
    23.  
    24. Q_INVOKABLE int load();
    25. Q_INVOKABLE int fileStat();
    26. Q_INVOKABLE QString filePath();
    27.  
    28. // Q_INVOKABLE bool setFilePath(const QString &file);
    29.  
    30. void setFileStat(int curStat);
    31.  
    32. QString fileName;
    33.  
    34. signals:
    35. void fileStatChanged(int stat);
    36.  
    37. public slots:
    38. void replyFinished (QNetworkReply *reply);
    39. void getHeaders (QNetworkReply *reply);
    40.  
    41. private:
    42. int stat;
    43. QDir fileLocation;
    44. QUrl url;
    45. QFileInfo xmlInfo;
    46. QNetworkAccessManager *manager;
    47. QNetworkAccessManager *headManager;
    48. };
    49.  
    50. #endif // XMLFILEHANDLER_H
    To copy to clipboard, switch view to plain text mode 
    .cpp
    Qt Code:
    1. #include "xmlfilehandler.h"
    2.  
    3. XmlFileHandler::XmlFileHandler(QObject *parent) : QObject(parent){
    4. fileLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
    5. url = QUrl("http://myURI/my.xml");
    6. xmlInfo = QFileInfo(fileLocation.filePath("my.xml"));
    7.  
    8. stat = 0;
    9. }
    10.  
    11. int XmlFileHandler::load(){
    12. manager = new QNetworkAccessManager(this);
    13. headManager = new QNetworkAccessManager(this);
    14. connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    15. connect(headManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(getHeaders(QNetworkReply*)));
    16.  
    17. if (xmlInfo.exists()) {
    18. // Check if local file is up to date
    19. headManager->head(QNetworkRequest(url));
    20. } else {
    21. qDebug() << "Need to download something here";
    22. qDebug() << QDir().mkpath(fileLocation.path());
    23. manager->get(QNetworkRequest(url));
    24. }
    25. qDebug() << stat;
    26. return stat;
    27. }
    28.  
    29. void XmlFileHandler::replyFinished(QNetworkReply *reply){
    30. if(reply->error()) {
    31. qDebug() << "ERROR!";
    32. qDebug() << reply->errorString();
    33. stat = -1;
    34. fileStatChanged(stat);
    35. }else{
    36. QFile *file = new QFile(fileLocation.filePath(my.xml"));
    37. if(file->open(QIODevice::ReadWrite | QIODevice::Truncate)){
    38. QTextStream out(file);
    39. out << reply->readAll();
    40. qDebug() << out.status();
    41. file->close();
    42. stat = 2;
    43. fileStatChanged(stat);
    44. }else{
    45. qDebug() << "Error opening file" << file->errorString();
    46. stat = -1;
    47. fileStatChanged(stat);
    48. }
    49. }
    50. reply->deleteLater();
    51. }
    52.  
    53. void XmlFileHandler::getHeaders(QNetworkReply *reply){
    54. if (reply->operation() == QNetworkAccessManager::HeadOperation){
    55. qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
    56. qDebug() << xmlInfo.lastModified().toLocalTime();
    57. if(xmlInfo.lastModified().toLocalTime()>reply->header(QNetworkRequest::LastModifiedHeader).toDateTime()){
    58. qDebug() << "No need for Update";
    59. stat = 1;
    60. fileStatChanged(stat);
    61. }else{
    62. qDebug() << "Update required";
    63. manager->get(QNetworkRequest(url));
    64. }
    65. }else{
    66. stat = -1;
    67. fileStatChanged(stat);
    68. }
    69. reply->deleteLater();
    70. }
    71.  
    72. QString XmlFileHandler::filePath(){
    73. return fileLocation.filePath("ausbildung.xml");
    74. }
    75. int XmlFileHandler::fileStat(){
    76. return stat;
    77. }
    78.  
    79. void XmlFileHandler::setFileStat(const int curStat){
    80. stat = curStat;
    81. fileStatChanged(stat);
    82. }
    To copy to clipboard, switch view to plain text mode 

    main.qml
    Qt Code:
    1. import QtQuick 2.0
    2. import Sailfish.Silica 1.0
    3. import "pages"
    4.  
    5. import harbour.Test 1.0
    6.  
    7. ApplicationWindow
    8. {
    9. initialPage: Component { FirstPage { } }
    10. cover: Qt.resolvedUrl("cover/CoverPage.qml")
    11. allowedOrientations: Orientation.All
    12. _defaultPageOrientations: Orientation.All
    13.  
    14. XmlFileHandler {
    15. id: handler;
    16. }
    17. }
    To copy to clipboard, switch view to plain text mode 

    page.qml
    Qt Code:
    1. import QtQuick 2.0
    2. import Sailfish.Silica 1.0
    3.  
    4.  
    5. Page {
    6. id: page
    7.  
    8. // To enable PullDownMenu, place our content in a SilicaFlickable
    9. SilicaFlickable {
    10. anchors.fill: parent
    11.  
    12. // PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView
    13. PullDownMenu {
    14. MenuItem {
    15. text: qsTr("Check for Update")
    16. onClicked: {
    17. handler.load()
    18. console.log(handler.fileStat()) //<- Not working
    19. }
    20.  
    21. }
    22. }
    23.  
    24. // Tell SilicaFlickable the height of its content.
    25. contentHeight: column.height
    26.  
    27. // Place our content in a Column. The PageHeader is always placed at the top
    28. // of the page, followed by our content.
    29. Column {
    30. id: column
    31.  
    32. width: page.width
    33. spacing: Theme.paddingLarge
    34. PageHeader {
    35. title: qsTr("UI Template")
    36. }
    37.  
    38. Text {
    39. id: stat
    40. color: Theme.secondaryHighlightColor
    41. font.pixelSize: Theme.fontSizeSmall
    42. width: parent.width-Theme.paddingLarge
    43. wrapMode: Text.Wrap
    44. text: "hier:"+handler.filePath() // <- not picked up by auto complete but works
    45. }
    46. }
    47. }
    48. }
    To copy to clipboard, switch view to plain text mode 

    Thanks

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    QNetworkAccessManager's operations are asynchronous.
    I would recommend to use that instead of trying to make it look synchronous.

    How do you process the XML file?

    Also:
    - don't create the QFile on the heap if you don't need it across methods. currently you are leaking it
    - don't create the network access managers in load without checking if you have already done so, otherwise you create new ones every time you call load()
    - you could just use one network access manager if you connect to the reply's finished signal
    - call setFileStat instead of manually setting the variable and manually emitting the change signal.
    - make setFileStat check if it actually has a change before emitting the change signal

    Cheers,
    _

  3. #3
    Join Date
    Apr 2014
    Posts
    116
    Qt products
    Qt5
    Platforms
    MacOS X
    Thanks
    8

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Thanks for the hints.
    I read that the network manager works asynchronous but I do not know what to look for to make it work the way I need it to. Can I build in a wait or progress feature bevor return is sent? Is it possible to build in a onStatChanged method that QML can see?
    Currently I am not processing the xml file. This part is missing at the moment but will be done within QML. I have done it once using XmlListModel. I hope I can set a local path as source. But I need to make sure that I have to updated file first.

    1.) I don't understand this. QFile is only created when I want to write something
    2.) It would be better to put them in XmlFileHandler::XmlFileHandler(QObject *parent) : QObject(parent){?
    3.) I have tried that but then get and header was triggered on load. How can I separate this?

    Any clue why not all methods are picked up in QML?

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Quote Originally Posted by KeineAhnung View Post
    I read that the network manager works asynchronous but I do not know what to look for to make it work the way I need it to. Can I build in a wait or progress feature bevor return is sent?
    Yes, but that would be a hack. As I said I would suggest to let it work asynchronously as intended.
    Also more declarative.

    Quote Originally Posted by KeineAhnung View Post
    Is it possible to build in a onStatChanged method that QML can see?
    That is already the case, a property has a change signal and each signal has a signal handler.

    Quote Originally Posted by KeineAhnung View Post
    Currently I am not processing the xml file. This part is missing at the moment but will be done within QML. I have done it once using XmlListModel. I hope I can set a local path as source.
    XmlListModel's source can be a local file.

    Quote Originally Posted by KeineAhnung View Post
    1.) I don't understand this. QFile is only created when I want to write something
    It is only needed within a certain block. No need to create it on the heap.
    Especially not without deleting it.

    Quote Originally Posted by KeineAhnung View Post
    2.) It would be better to put them in XmlFileHandler::XmlFileHandler(QObject *parent) : QObject(parent){?
    Yes, that would work as well.

    Quote Originally Posted by KeineAhnung View Post
    3.) I have tried that but then get and header was triggered on load. How can I separate this?
    Connect to different slots.

    Quote Originally Posted by KeineAhnung View Post
    Any clue why not all methods are picked up in QML?
    What do you mean?

    Cheers,
    _

  5. #5
    Join Date
    Apr 2014
    Posts
    116
    Qt products
    Qt5
    Platforms
    MacOS X
    Thanks
    8

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    3.) They are already connected to different slots. But since the signal is the same both slots are triggered, aren't they?

    Regarding the QML. Some function are picked up by auto complete and some not. I do not know what I did wrong that for example the stat value cannot be accessed by QML and therefore no onStatChanged...

  6. #6
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Wiki edits
    17

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Editor autocomplete has nothing to do with whether the symbols are good for a compiler or the QML engine.
    Do the symbols work if you simply type them?
    Have you rerun qmake and rebuilt since you last changed the PROPERTY or Q_INVOKABLE macros?

  7. #7
    Join Date
    Apr 2014
    Posts
    116
    Qt products
    Qt5
    Platforms
    MacOS X
    Thanks
    8

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    I redid the project completely and more or even all methods are picked up in QML. Before I tried the clean and build all option from Qt bur this did not help. I did not try qmake.

    Thanks for the help so far. I think I got the app working now. But I am not sure if all cases are covered. So in case there is no network connection etc.

    If there are more suggestions on how to improve, I am thankful for everything =)

  8. #8
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Quote Originally Posted by KeineAhnung View Post
    3.) They are already connected to different slots. But since the signal is the same both slots are triggered, aren't they?
    No, each reply object has its "own" signal, after all the very first argument of the connect() call is the sender object,.

    Cheers,
    _

  9. #9
    Join Date
    Apr 2014
    Posts
    116
    Qt products
    Qt5
    Platforms
    MacOS X
    Thanks
    8

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Hi,

    I still do net get how to do it with only one network manager. If I write the code like this:
    Qt Code:
    1. manager = new QNetworkAccessManager(this);
    2. connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    3. connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(getHeaders(QNetworkReply*)));
    To copy to clipboard, switch view to plain text mode 
    I end up with an empty xml file after I reopen the app. I think this is because the replyFinished slot is triggered with the getHeaders SLOT. At least I that the console outputs of both functions. Since the file is opened with open(QIODevice::ReadWrite | QIODevice::Truncate) the xml file is cleared. Since I only asked for the headers the stream is empty and nothing is written to the file.
    For me that is clear because I connected the same signal to two slots. How do I need to write this if I only want to use on network manager?

    Thanks

  10. #10
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts

    Default Re: Stuck with QNetworkManager and Q_INVOKABLE

    Quote Originally Posted by KeineAhnung View Post
    Hi,

    I still do net get how to do it with only one network manager. If I write the code like this:
    Qt Code:
    1. manager = new QNetworkAccessManager(this);
    2. connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    3. connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(getHeaders(QNetworkReply*)));
    To copy to clipboard, switch view to plain text mode 
    That will of course not work, this is the same sender/signal combination, so of course both slots are going to be executed upon emission.

    Hence me writing:
    Quote Originally Posted by anda_skoa View Post
    - you could just use one network access manager if you connect to the reply's finished signal
    Cheers,
    _

Similar Threads

  1. Replies: 2
    Last Post: 26th January 2015, 13:54
  2. Q_INVOKABLE in derived classes
    By golubevsv in forum Qt Programming
    Replies: 1
    Last Post: 18th September 2014, 14:36
  3. Replies: 1
    Last Post: 16th June 2013, 20:12
  4. Can't make Q_INVOKABLE macro use compile
    By frankiefrank in forum Qt Programming
    Replies: 5
    Last Post: 22nd September 2011, 14:35
  5. Problem using Q_INVOKABLE on private constructor
    By graeme in forum Qt Programming
    Replies: 1
    Last Post: 29th May 2010, 22:50

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.