mohsen
mohsen

Reputation: 1806

How use signal in base class from all inherited class in qt?

I have a qt quick 2 application,I have base class for my c++ classes

class ManagerEngine : public QObject
{
    Q_OBJECT
public:
    explicit ManagerEngine(QObject *parent = nullptr);
    bool isProcessing();
    void setIsProcssing(bool isProcessing);
signals:    
    void isProcessingChanged(bool isProcessing);
private:
     bool m_IsProccessing;
};

I have many class that has inherited from above class

class BookManager : public ManagerEngine
{
};

void BookManager::getList(bool refreshList){
    setIsProcssing(true);
    //get data from server
    setIsProcssing(false);
}

Now in thease classes I want to show BusyIndicator when the method is getting some data from server.

BookPage.qml

BookManager{
    id: bm
    onIsProcessingChanged: {
        busyIndicator.visible=isProcessing; // busyIndicator is in main.qml
    }
}

CategoryPage.qml

CategoryManager{
    id: cm
    onIsProcessingChanged: {
        busyIndicator.visible=isProcessing;
    }
}

QuestionPage.qml

QuestionManager{
    id: qm
    onIsProcessingChanged: {
        busyIndicator.visible=isProcessing;
    }
}

//I have many class like : login signup and ... inherited from base class

main.qml

BusyIndicator{
    id:busyIndicator        
}

All works ok in BookPage.qml and ... (above) and in thease page busyIndicator will display.But I want do this on time.

I tryed this: I use base classManagerEngine for display BusyIndicator

I want to display busyIndicator Like below.(i Declared Base class in main page.Now if i open BookPage.qml for display book list,now the busyIndicator must be visible

main.qml

ManagerEngine{
    id: me
    onIsProcessingChanged: {
        progressIndicator.visible=isProcessing;
    }
}
BusyIndicator{
    id:busyIndicator        
}

But It don't work.Is there another way for doing this work(for example can i use static keyword)

Upvotes: 0

Views: 854

Answers (1)

This won't ever work, because those classes seem to work in the main thread, and when the processing is being done, no user interface updates are taking place, and your application is frozen.

In all cases, the solution has to begin with making the busy indicator a proper property - this will magically fix your Qt Quick problems, and you can simply bind to the property and not have to use the signal explicitly at all. That's what property notifications are for, and QML handles them for you. You also don't want the setter to be public - this is internal, read-only state. You also want const-correct signatures.

After fixing all of the problems, we get:

class ManagerEngine : public QObject {
    Q_OBJECT
    Q_PROPERTY(bool isProcessing READ isProcessing NOTIFY isProcessingChanged)
public:
    using QObject::QObject; //for C++14 & up
    bool isProcessing() const;
    Q_SIGNAL void isProcessingChanged(bool);
protected:
    void setIsProcessing(bool);
private:
    bool m_isProccessing = false;
};

/// This method is not thread-safe
void ManagerEngine::setIsProcessing(bool p) {
  if (p == m_isProcessing) return;
  m_isProcessing = p; // precondition of the signal
  return emit isProcessingChanged(m_isProcessing);
}

/// This method is not thread-safe
bool ManagerEngine::isProcessing() const {
  return m_isProcessing;
}

If you want the processing to be a state shared across all instances of the manager engine, then make it a property of the class, not of the object. In C++, a static member declaration means that it's a class member, not object member.

Interface

class ManagerEngine : public QObject {
    Q_OBJECT
    Q_PROPERTY(bool isProcessing READ isProcessing NOTIFY isProcessingChanged)
public:
    using QObject::QObject; //for C++14 & up
    ~ManagerEngine() override;
    bool isProcessing() const;
    Q_SIGNAL void isProcessingChanged(bool);
protected:
    void setIsProcessing(bool);
private:
    bool m_isProcessing = false; // per-object
    static QAtomicInt m_processingCount; // per-class
};

Implementation

QAtomicInt ManagerEngine::m_processingCount;

ManagerEngine::~MangerEngine() {
  setIsProcessing(false);
  // Perhaps it'd be more appropriate instead to simply
  // Q_ASSERT(!m_isProcessing) if the engine should not be destroyed
  // during processing.
}

// This method is not thread-safe, but multiple engines can
// reside in different threads.
void ManagerEngine::setIsProcessing(bool p) {
  if (p == m_isProcessing) return;
  int const delta = p ? (+1) : (-1);
  auto count = m_processingCount.load();
  Q_ASSERT(count >= 0);
  Q_ASSERT(p || count > 0);
  m_isProcessing = p;
  while (!m_processingCount.testAndSetOrdered(count, count+delta)) {
    count = m_processingCount.load();
  }
  // The signal will be emitted only when the global state changes,
  // and exactly once per global state change.
  if ((count > 0) != ((count+delta) > 0))
    emit isProcessingChanged(count > 0);
  return;
}

// Note: Due to data races, it is not guaranteed that the result of
// this method is the same as the parameter in the isProcessingChanged
// signal. Only the signal's parameter is guaranteed to change state
// in a race-free fashion, i.e. always toggle (alternate) and never repeat.
bool ManagerEngine::isProcessing() const {
  auto const count = m_processingCount.load();
  Q_ASSERT(count >= 0);
  Q_ASSERT(!m_isProcessing || count > 0);
  return count > 0;
}

Upvotes: 1

Related Questions