Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. multiple thread with QNetworkAccessManager
Forum Updated to NodeBB v4.3 + New Features

multiple thread with QNetworkAccessManager

Scheduled Pinned Locked Moved Unsolved General and Desktop
19 Posts 3 Posters 6.5k Views 3 Watching
  • 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.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #6

    Also depending on your server/network setup starting a hundred connections might not be the best idea.

    Like I wrote, QNetworkAccessManager already handles several concurrent connections. What you can do is write a worker object that will contain a QNetworkAccessManager and you pass it the data chunk you'd like to send.

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    VolebabV 1 Reply Last reply
    0
    • SGaistS SGaist

      Also depending on your server/network setup starting a hundred connections might not be the best idea.

      Like I wrote, QNetworkAccessManager already handles several concurrent connections. What you can do is write a worker object that will contain a QNetworkAccessManager and you pass it the data chunk you'd like to send.

      VolebabV Offline
      VolebabV Offline
      Volebab
      wrote on last edited by
      #7

      @SGaist it's local, and normally servers can handle thousands of requests per second, that is not a problem.
      You said that I can create a worker object, but how to share the same list view with multiple workers? I mean, the list has 10000 itens, each worker can get an item, how to be sure that it won't be a mess?

      1 Reply Last reply
      0
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #8

        It will depend on your architecture. You can have your worker object query a "task list" to get a job. Or have a "task manager" that will generate the workers as needed and give them the tasks they should do.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        VolebabV 1 Reply Last reply
        0
        • SGaistS SGaist

          It will depend on your architecture. You can have your worker object query a "task list" to get a job. Or have a "task manager" that will generate the workers as needed and give them the tasks they should do.

          VolebabV Offline
          VolebabV Offline
          Volebab
          wrote on last edited by
          #9

          @SGaist Is there an example, I don't know how to do this.

          kshegunovK 1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #10

            Which part ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            VolebabV 1 Reply Last reply
            0
            • VolebabV Volebab

              @SGaist Is there an example, I don't know how to do this.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by kshegunov
              #11

              @Volebab
              Here's how a worker object is defined and used (barebone code only):

              class MyWorkerObject : public QObject
              {
                  Q_OBJECT
              
              public:
                  WorkerObject()
                      : QObject(NULL), nam(NULL)
                  {
                  }
              
                  ~WorkerObject()
                  {
                      delete nam;
                  }
              
              public slots:
                  void initialize()
                  {
                      nam = new QNetworkAccessManager;
                  }
              
                  void sendRequest()
                  {
                      // Send your requests here (connect to the proper controlling signal)
                  }
              
                 QNetworkAccessManager * nam;
              };
              

              Which you use like this:

              QThread * workerThread = new QThread();
              
              MyWorkerObject * workerObject = new MyWorkerObject;
              workerObject->moveToThread(workerThread);
              
              QObject::connect(workerThread, SIGNAL(started()), workerObject, SLOT(initialize()));
              QObject::connect(workerThread, SIGNAL(finished()), workerObject, SLOT(deleteLater()));
              QObject::connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
              QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), workerThread, SLOT(quit()));
              
              workerThread->start();
              // Connect the appropriate signal(s) to MyWorkerObject::sendRequest
              

              PS.
              I should really sit down and write a threading tutorial for the wiki ...

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #12

                @kshegunov In the absolute, there's already an example in QThread's documentation but improvement to the doc/examples are always a good idea :)

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                kshegunovK 1 Reply Last reply
                0
                • SGaistS SGaist

                  Which part ?

                  VolebabV Offline
                  VolebabV Offline
                  Volebab
                  wrote on last edited by Volebab
                  #13

                  I will try to explain it better:

                  I have a file with 10000 item, each line is a complete name of a person, like: "Johnnie Doe". We have an intranet with an api that we can make a request, example: http://192.168.182.10/user.php?name=Johnnie Doe and it will return a json with a valid property that specify if the costumer is validated or not in the system, along with other information about she/him, like name, documents and so on. The things that I want to do is:

                  • How and if is possible to read a big file with thousands of lines into a list without crashing or making the software slow?
                  • How to use multiple threads (which I can specify how many) in order to speed up the process of connecting with the intranet api.
                  • How to get the result in json of each thread and put in a table with only the name of the costumer and the result of the valid property. For example: I made a consult for the costumer "Johnnie Doe" and he is validated, so I want to put in a table his name and true for the validation.
                  • How to make each thread get items on the list without making a mess, for example, I have 5 threads getting items from the list, isn't it going to get messy? How to make each one get an item from the list without problems?

                  I work in a company and I have to do this mostly manually and I want to automate that, it's a pain.

                  @kshegunov - Thank you for the example.

                  kshegunovK 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    @kshegunov In the absolute, there's already an example in QThread's documentation but improvement to the doc/examples are always a good idea :)

                    kshegunovK Offline
                    kshegunovK Offline
                    kshegunov
                    Moderators
                    wrote on last edited by kshegunov
                    #14

                    @SGaist
                    I suppose so, but this code I can write in my sleep. I can't seem to remember the number of times I've written it here. Also the documentation doesn't really cover some finer points like actually waiting for the thread to finish, or running a loop through the event loop (although this should be simple to gather by yourself if you understand how the API works in the first place).

                    Read and abide by the Qt Code of Conduct

                    1 Reply Last reply
                    0
                    • VolebabV Volebab

                      I will try to explain it better:

                      I have a file with 10000 item, each line is a complete name of a person, like: "Johnnie Doe". We have an intranet with an api that we can make a request, example: http://192.168.182.10/user.php?name=Johnnie Doe and it will return a json with a valid property that specify if the costumer is validated or not in the system, along with other information about she/him, like name, documents and so on. The things that I want to do is:

                      • How and if is possible to read a big file with thousands of lines into a list without crashing or making the software slow?
                      • How to use multiple threads (which I can specify how many) in order to speed up the process of connecting with the intranet api.
                      • How to get the result in json of each thread and put in a table with only the name of the costumer and the result of the valid property. For example: I made a consult for the costumer "Johnnie Doe" and he is validated, so I want to put in a table his name and true for the validation.
                      • How to make each thread get items on the list without making a mess, for example, I have 5 threads getting items from the list, isn't it going to get messy? How to make each one get an item from the list without problems?

                      I work in a company and I have to do this mostly manually and I want to automate that, it's a pain.

                      @kshegunov - Thank you for the example.

                      kshegunovK Offline
                      kshegunovK Offline
                      kshegunov
                      Moderators
                      wrote on last edited by kshegunov
                      #15

                      @Volebab said:

                      How and if is possible to read a big file with thousands of lines into a list without crashing or making the software slow?

                      You put the reading of the file and the heavy lifting in (a) worker object's slot(s) (like above). You control the worker object through signals (that are connected to its slots). The worker object notifies the GUI (or main thread) again by raising signals, which you connect to slots in the widgets.

                      How to use multiple threads (which I can specify how many) in order to speed up the process of connecting with the intranet api.

                      As already discussed, use one thread for the file reading, leave the network access manager to thread the network requests itself. Use the asynchronous API of the NAM, so you don't block your worker thread (it emits signals when a reply was received, connect those to slots in your worker object).

                      How to get the result in json of each thread and put in a table with only the name of the costumer and the result of the valid property. For example: I made a consult for the costumer "Johnnie Doe" and he is validated, so I want to put in a table his name and true for the validation.

                      If talking about a table widget (GUI) then emit a signal from the worker object with the customer's information and connect that signal to a slot in the widget/controller object that will add it to the UI.

                      How to make each thread get items on the list without making a mess, for example, I have 5 threads getting items from the list, isn't it going to get messy? How to make each one get an item from the list without problems?

                      You are thinking even lower level. With multiple threads accessing the same data you need to protect that data. The most basic thing to do in that situation is to have a thread safe queue (which is done with a help of a mutual exclusion lock QMutex and a semaphore QSemaphore) but really, this is low-level stuff that requires experience. If you limit your threads and objects to communicate through signals and slots you don't need to worry about that.

                      Kind regards.

                      Read and abide by the Qt Code of Conduct

                      VolebabV 1 Reply Last reply
                      1
                      • kshegunovK kshegunov

                        @Volebab said:

                        How and if is possible to read a big file with thousands of lines into a list without crashing or making the software slow?

                        You put the reading of the file and the heavy lifting in (a) worker object's slot(s) (like above). You control the worker object through signals (that are connected to its slots). The worker object notifies the GUI (or main thread) again by raising signals, which you connect to slots in the widgets.

                        How to use multiple threads (which I can specify how many) in order to speed up the process of connecting with the intranet api.

                        As already discussed, use one thread for the file reading, leave the network access manager to thread the network requests itself. Use the asynchronous API of the NAM, so you don't block your worker thread (it emits signals when a reply was received, connect those to slots in your worker object).

                        How to get the result in json of each thread and put in a table with only the name of the costumer and the result of the valid property. For example: I made a consult for the costumer "Johnnie Doe" and he is validated, so I want to put in a table his name and true for the validation.

                        If talking about a table widget (GUI) then emit a signal from the worker object with the customer's information and connect that signal to a slot in the widget/controller object that will add it to the UI.

                        How to make each thread get items on the list without making a mess, for example, I have 5 threads getting items from the list, isn't it going to get messy? How to make each one get an item from the list without problems?

                        You are thinking even lower level. With multiple threads accessing the same data you need to protect that data. The most basic thing to do in that situation is to have a thread safe queue (which is done with a help of a mutual exclusion lock QMutex and a semaphore QSemaphore) but really, this is low-level stuff that requires experience. If you limit your threads and objects to communicate through signals and slots you don't need to worry about that.

                        Kind regards.

                        VolebabV Offline
                        VolebabV Offline
                        Volebab
                        wrote on last edited by Volebab
                        #16

                        @kshegunov

                        Amazing answer and I get most of what you said, the only thing though that I have no idea how to accomplish is:

                        You are thinking even lower level. With multiple threads accessing the same data you need to protect that data. The most basic thing to do in that situation is to have a thread safe queue (which is done with a help of a mutual exclusion lock QMutex and a semaphore QSemaphore) but really, this is low-level stuff that requires experience. If you limit your threads and objects to communicate through signals and slots you don't need to worry about that.

                        I have a QListWidget full of names, a QTableWidget to put informations, I have a worker to read the big file and put each line as the QListWidget item, and I have a worker using QNetworkAccessManager to connect with the API.
                        The thing now is: How the worker using QNetworkAccessManager gets the item from the QListWidget without collapsing with other worker getting at the same time?

                        I know that it might be ask too much, but if you provide me an example I would be really happy. I really need to get this working so I can ease my job here on the company.

                        kshegunovK 1 Reply Last reply
                        0
                        • VolebabV Volebab

                          @kshegunov

                          Amazing answer and I get most of what you said, the only thing though that I have no idea how to accomplish is:

                          You are thinking even lower level. With multiple threads accessing the same data you need to protect that data. The most basic thing to do in that situation is to have a thread safe queue (which is done with a help of a mutual exclusion lock QMutex and a semaphore QSemaphore) but really, this is low-level stuff that requires experience. If you limit your threads and objects to communicate through signals and slots you don't need to worry about that.

                          I have a QListWidget full of names, a QTableWidget to put informations, I have a worker to read the big file and put each line as the QListWidget item, and I have a worker using QNetworkAccessManager to connect with the API.
                          The thing now is: How the worker using QNetworkAccessManager gets the item from the QListWidget without collapsing with other worker getting at the same time?

                          I know that it might be ask too much, but if you provide me an example I would be really happy. I really need to get this working so I can ease my job here on the company.

                          kshegunovK Offline
                          kshegunovK Offline
                          kshegunov
                          Moderators
                          wrote on last edited by kshegunov
                          #17

                          @Volebab
                          Aha! Well that's a bit counter-intuitive indeed. The worker can emit a signal that it can process data (send request or w/e). The GUI is subscribed to that signal and in the slot that handles it, it raises it's own signal with the data. The signal that GUI emits is connected to the worker and the worker gets its data. It sounds like a rollercoaster, but is actually quite simple. Something like this (again bare-bone code only):

                          class MyWorker : public QObject
                          {
                              Q_OBJECT
                          
                          signals:
                              void canProcessData();
                          
                          public slots:
                              void process(MyDataContainer data)
                              {
                                  // Send requests w/e
                          
                                 // If you want to repeat, emit canProcessData() at the end
                              }
                          };
                          
                          class GuiClass : public QObject // Can be widget or QObject, depending on the exact architecture of your application
                          {
                              Q_OBJECT
                          
                          signals:
                              void startDataProcessing();  // This will start the data processing
                              void dataToProcess(MyDataContainer data);
                          
                          public slots:
                              void dataRequested()
                              {
                                  MyDataContainer data; // Collect the data from the GUI side
                          
                                  // After collecting the data emit the appropriate signal
                                  emit dataToProcess(data);
                              }
                          };
                          

                          You connect those signals in a loop between the two objects:

                          // These two are only for completeness, you have to adjust the pointers so they are referencing the correct objects
                          MyWorker * worker;
                          GuiClass * gui;
                          
                          QObject::connect(gui, SIGNAL(startDataProcessing()), worker, SIGNAL(canProcessData()));  // Delegating the signal so you can start the loop
                          QObject::connect(worker, SIGNAL(canProcessData()), gui, SLOT(dataRequested()));  // The worker requests a new batch of data
                          QObject::connect(gui, SIGNAL(dataToProcess(MyDataContainer)), worker, SLOT(dataToProcess(MyDataContainer)));  // The GUI provides new data for the worker
                          

                          This is how you can "pull" data (as opposed to the usual "push") from another thread.



                          If you have two workers, you can connect one directly to the other. Suppose readWorker is the worker object that reads the data, netWorker is the one making the requests, and gui is the GUI class. The power of the signal-slot mechanism should become obvious here:

                          class ReadWorker : public QObject
                          {
                              Q_OBJECT
                          
                          signals:
                              void haveCustomerName(CustomerName);
                          
                          public slots:
                              void startReading()
                              {
                                  // Read the file, for each set of customer name emit the signal
                                  while ( /*... reading the file ... */)  {
                                      CustomerName name;   // You get a customer from the file
                                      emit haveCustomerName(name);
                                  }
                              }
                          };
                          
                          class NetworkWorker : public QObject
                          {
                               Q_OBJECT
                          
                          signals:
                               void haveCustomerData(CustomerData);
                          
                          public slots:
                               void getCustomerData(CustomerName name)
                               {
                                    // Make the network request, connect other signals if need etc.
                                    // At the end of the day, this slot should emit haveCustomerData when it has received it from the network
                               }
                          };
                          
                          class GuiClass : public QObject // Can be widget or QObject, depending on the exact architecture of your application
                          {
                              Q_OBJECT
                          
                          signals:
                              void start();  // This will start the data processing (i.e. reading the file and sending requests)
                          
                          public slots:
                              void onNewCustomerName(CustomerName name)
                             {
                                  // This will give you the customer name while waiting for response from the network. You can use it to pre-populate the GUI
                             }
                              void onNewCustomerData(CustomerData data)
                              {
                                   // The data had arrived and the worker has notified you. Use it to update the GUI
                              }
                          };
                          

                          You can connect those three "in a triangle":

                          // These are only for completeness, you have to adjust the pointers so they are referencing the correct objects
                          ReadWorker * readWorker;
                          NetworkWorker * netWorker;
                          GuiClass * gui;
                          
                          // Starting the reading when the GUI emits start()
                          QObject::connect(gui, SIGNAL(start()), readWorker, SLOT(startReading())); 
                          // One worker notifies the other that a customer name has been read. The network worker can start making requests with that
                          QObject::connect(readWorker, SIGNAL(haveCustomerName(CustomerName)), netWorker, SLOT(getCustomerData(CustomerName))); 
                          // The worker that reads the data also notifies the GUI for the customer name, so the GUI can pre-populate (if it wishes) some widgets or w/e
                          QObject::connect(readWorker, SIGNAL(haveCustomerName(CustomerName)), gui, SLOT(onNewCustomerName(CustomerName))); 
                          // The worker that handles the network notifies the GUI that customer data is available, GUI should then update itself to reflect that.
                          QObject::connect(netWorker, SIGNAL(haveCustomerData(CustomerData)), gui, SLOT(onNewCustomerData(CustomerData))); 
                          

                          Well, it turned out a bit long-ish than I intended initially ... anyway, I think this should help.
                          Kind regards.

                          Read and abide by the Qt Code of Conduct

                          VolebabV 1 Reply Last reply
                          1
                          • kshegunovK kshegunov

                            @Volebab
                            Aha! Well that's a bit counter-intuitive indeed. The worker can emit a signal that it can process data (send request or w/e). The GUI is subscribed to that signal and in the slot that handles it, it raises it's own signal with the data. The signal that GUI emits is connected to the worker and the worker gets its data. It sounds like a rollercoaster, but is actually quite simple. Something like this (again bare-bone code only):

                            class MyWorker : public QObject
                            {
                                Q_OBJECT
                            
                            signals:
                                void canProcessData();
                            
                            public slots:
                                void process(MyDataContainer data)
                                {
                                    // Send requests w/e
                            
                                   // If you want to repeat, emit canProcessData() at the end
                                }
                            };
                            
                            class GuiClass : public QObject // Can be widget or QObject, depending on the exact architecture of your application
                            {
                                Q_OBJECT
                            
                            signals:
                                void startDataProcessing();  // This will start the data processing
                                void dataToProcess(MyDataContainer data);
                            
                            public slots:
                                void dataRequested()
                                {
                                    MyDataContainer data; // Collect the data from the GUI side
                            
                                    // After collecting the data emit the appropriate signal
                                    emit dataToProcess(data);
                                }
                            };
                            

                            You connect those signals in a loop between the two objects:

                            // These two are only for completeness, you have to adjust the pointers so they are referencing the correct objects
                            MyWorker * worker;
                            GuiClass * gui;
                            
                            QObject::connect(gui, SIGNAL(startDataProcessing()), worker, SIGNAL(canProcessData()));  // Delegating the signal so you can start the loop
                            QObject::connect(worker, SIGNAL(canProcessData()), gui, SLOT(dataRequested()));  // The worker requests a new batch of data
                            QObject::connect(gui, SIGNAL(dataToProcess(MyDataContainer)), worker, SLOT(dataToProcess(MyDataContainer)));  // The GUI provides new data for the worker
                            

                            This is how you can "pull" data (as opposed to the usual "push") from another thread.



                            If you have two workers, you can connect one directly to the other. Suppose readWorker is the worker object that reads the data, netWorker is the one making the requests, and gui is the GUI class. The power of the signal-slot mechanism should become obvious here:

                            class ReadWorker : public QObject
                            {
                                Q_OBJECT
                            
                            signals:
                                void haveCustomerName(CustomerName);
                            
                            public slots:
                                void startReading()
                                {
                                    // Read the file, for each set of customer name emit the signal
                                    while ( /*... reading the file ... */)  {
                                        CustomerName name;   // You get a customer from the file
                                        emit haveCustomerName(name);
                                    }
                                }
                            };
                            
                            class NetworkWorker : public QObject
                            {
                                 Q_OBJECT
                            
                            signals:
                                 void haveCustomerData(CustomerData);
                            
                            public slots:
                                 void getCustomerData(CustomerName name)
                                 {
                                      // Make the network request, connect other signals if need etc.
                                      // At the end of the day, this slot should emit haveCustomerData when it has received it from the network
                                 }
                            };
                            
                            class GuiClass : public QObject // Can be widget or QObject, depending on the exact architecture of your application
                            {
                                Q_OBJECT
                            
                            signals:
                                void start();  // This will start the data processing (i.e. reading the file and sending requests)
                            
                            public slots:
                                void onNewCustomerName(CustomerName name)
                               {
                                    // This will give you the customer name while waiting for response from the network. You can use it to pre-populate the GUI
                               }
                                void onNewCustomerData(CustomerData data)
                                {
                                     // The data had arrived and the worker has notified you. Use it to update the GUI
                                }
                            };
                            

                            You can connect those three "in a triangle":

                            // These are only for completeness, you have to adjust the pointers so they are referencing the correct objects
                            ReadWorker * readWorker;
                            NetworkWorker * netWorker;
                            GuiClass * gui;
                            
                            // Starting the reading when the GUI emits start()
                            QObject::connect(gui, SIGNAL(start()), readWorker, SLOT(startReading())); 
                            // One worker notifies the other that a customer name has been read. The network worker can start making requests with that
                            QObject::connect(readWorker, SIGNAL(haveCustomerName(CustomerName)), netWorker, SLOT(getCustomerData(CustomerName))); 
                            // The worker that reads the data also notifies the GUI for the customer name, so the GUI can pre-populate (if it wishes) some widgets or w/e
                            QObject::connect(readWorker, SIGNAL(haveCustomerName(CustomerName)), gui, SLOT(onNewCustomerName(CustomerName))); 
                            // The worker that handles the network notifies the GUI that customer data is available, GUI should then update itself to reflect that.
                            QObject::connect(netWorker, SIGNAL(haveCustomerData(CustomerData)), gui, SLOT(onNewCustomerData(CustomerData))); 
                            

                            Well, it turned out a bit long-ish than I intended initially ... anyway, I think this should help.
                            Kind regards.

                            VolebabV Offline
                            VolebabV Offline
                            Volebab
                            wrote on last edited by
                            #18

                            @kshegunov What is the propose of the GuiClass and MyDataContainer classes? I mean, I have QMainWindow, isn't it already a gui class? And Why MyDataContainer if I will be reading using the ReadWorker?

                            kshegunovK 1 Reply Last reply
                            0
                            • VolebabV Volebab

                              @kshegunov What is the propose of the GuiClass and MyDataContainer classes? I mean, I have QMainWindow, isn't it already a gui class? And Why MyDataContainer if I will be reading using the ReadWorker?

                              kshegunovK Offline
                              kshegunovK Offline
                              kshegunov
                              Moderators
                              wrote on last edited by
                              #19

                              @Volebab

                              What is the propose of the GuiClass and MyDataContainer classes?

                              GuiClass is a name for the class managing the user interface. It can be a QObject, or a MainWindow subclass or practically anything that has a QObject ancestor. If you have derived from QMainWindow, which is the usual approach, then GuiClass is exactly your main window class.

                              MyDataContainer, again is a generic name I'd chosen when writing the example. It's the class that contains the data (whence the name) to be transferred between the threads. As you can see there are no declarations for it, it can be QVector, QImage, QString or some integral type as int. It can also be a complex user-defined type (a structure or a class, but then some registrations are needed).

                              I mean, I have QMainWindow, isn't it already a gui class?

                              It is and you can use that instead of GuiClass.

                              And Why MyDataContainer if I will be reading using the ReadWorker?

                              So you can transfer the data safely between the threads.
                              If you give access directly (that is you don't use signals and slots) to an object's method/property then you have to put locks for each method call and/or property access that might happen from two threads.

                              Kind regards.

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply
                              0

                              • Login

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