2013-02-04 2 views
3

클래스의 기능을 슬롯으로 만들려면 클래스가 QObject에서 상속해야합니다. 그러나 QObject는 상당히 많은 양의 메모리를 차지합니다. 얼마나 많은지, 메모리가 각 클래스 또는 각 객체에 대한 것인지 여부는 확실하지 않습니다. 내 코드에는 함수가 슬롯이 될 수있는 많은 작은 데이터가 있습니다. 클래스의 함수를 사용할 때 시간 함수 적으로 슬롯을 만드는 방법이 있는지 궁금합니다. 사용 후 슬롯 비용 메모리가 삭제됩니다. 다음 코드는 요구 사항을 보여줍니다.Qt에서 시간 함수로 슬롯을 만드는 방법은 무엇입니까?

class SmallData // size of 2 or 3 integers. 
{ 
public: 
    virtual void F(); // use it as a slot. 
    virtual QMenu* createMenu(); // use this to create the context menu with 
           // an action connected to F() 
    ... 
}; 

// use the small data 
vector<SmallData> vec(1000000); // the vector is put at a tree view. When an 
           // item in the tree view is selected, a context 
           // menu pop up with an action to run F(). 
SmallData* data = treeView.selectedItem();  
connect(action, SIGNAL(triggered()), data, SLOT(F())); // How to make F() to be 
                 // a slot just here. 
                 // The action is from 
                 // data->createMenu(). 
+0

QObject가 상당히 많은 양의 메모리를 차지한다고 생각합니까? – aschepler

+0

나는 그것을주의 깊게 체크했으나 체크하지 않았다. 메커니즘을 구현하기 위해 부모 포인터 및 기타 데이터가 있습니다. – user1899020

+4

그건 불가능합니다. 또한 백만 개의 항목이있는 경우 사용자 지정 QAbstractItemModel 대신 항목 기반 모델을 사용하면 QObject뿐 아니라 병목 현상이 발생합니다. 단일 슬롯을 추가하고 선택한 SmallData 객체를 찾아보고 F()를 호출합니다. –

답변

8

당신이 Qt5를 사용할 수있는 경우에, 당신 수 connect signals to plain functions and static methods (이 본질적으로) 재미있게라는 이름의 일반 기능을 다시 :

connect(action, &QAction::triggered, 
     &SmallData::statF); 
actionQAction 인스턴스입니다

SmallData::statFSmallData의 정적 방법이다. 기독교 라우의 코멘트 당

편집, 특정 인스턴스를 호출, 당신은 또한 람다에 연결할 수 있습니다

이미 QT4와
connect(action, &QAction::triggered, 
     [data]() { data->F(); }); 

, 당신은 몇 가지 더 많은 개체로, 거의 같은 효과를 얻을 수 QSignalMapper을 사용할 수 있습니다 . 그것은 어떤 오브젝트가 그것을 방출했는지에 근거하여 매개 변수 (이 경우 아마도 vec에 정수 인덱스)를 추가하여 신호를 보냅니다. 그러나 Qt4에서 수신기는 항상 QObject이어야합니다.

+0

이것이 작동하면 가장 간단한 해결책이 될 것입니다. 내 SmallData :: stateF는 가상이며 정적이 아닙니다. 그것을하는 어떤 방법? – user1899020

+1

+1 와우, 이제는 멋지 네 (새로운 Qt를 너무 많이 보지 못했다). 정적 인 것들을 피하기 위해 C++ 11의 기능적 기능과 함께 사용할 수도 있습니다 :'connect (action, & QAction :: triggered, std :: bind (& SmallData :: F, data)); 이것이 작동한다면 (최소한 지원되는'tr1 :: bind'에 대해 말하면) 추가 할 수 있습니다. 그러면 그것은 IMHO의 가장 좋은 대답입니다. –

+0

@ user1899020 불행하게도 메소드 포인터는 (필자가 아는 한) 지원되지 않습니다. 특정 객체 인스턴스의 메소드를 호출하려면 QObject 인스턴스 여야합니다. QSignalMapper를 사용하면 differenet 매개 변수를 전달할 수 있습니다.이 매개 변수를 기반으로 QObject에서 신호를 내 보낸다면 아마도 도움이 될 것입니다. – hyde

3

신호 슬롯 메커니즘을 사용하는 경우 QObject 주위를 돌아 다니지는 않지만 기능을 호출하는 슬롯이있는 임시 개체를 만드는 것이 좋습니다. 당신은 그 물체를 적절히 놓아 두는 것만으로도 신경 써야합니다. 뭔가 같이 : 슬롯이 자동으로 한 번 트리거 용해해야하는지 여부를 제어 할 수있는 oneShot 매개 변수와

SmallData* data = treeView.selectedItem(); 
connect(action, SIGNAL(triggered()), 
     makeCallback(std::bind(&SmallData::F, data)), SLOT(call())); 

:

class Callback : public QObject 
{ 
    Q_OBJECT 

public: 
    typedef std::function<void()> FunctionType; 

    Callback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr) 
     : QObject(parent), fn_(std::move(fn)), oneShot_(oneShot) {} 

public slots: 
    void call() 
    { 
     fn_();  //delegate to callback 
     if(oneShot_) 
      deleteLater(); //not needed anymore 
    } 

private: 
    FunctionType fn_; 
    bool oneShot_; 
}; 

Callback* makeCallback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr) 
{ 
    return new Callback(std::move(fn), oneShot, parent); 
} 

당신은 단지 Callback 개체 필요한 때마다 (더 많거나 적은 일시적를) 만들 수 있습니다.

유일한 문제는이 슬롯을 호출하지 않으면 Callback이 누출된다는 것입니다.

SmallData* data = treeView.selectedItem(); 
connect(action, SIGNAL(triggered()), 
     makeCallback(std::bind(&SmallData::F, data), true, this), SLOT(call())); 

이 방법 당신은 또한 콜백 개체의 수명을 바인딩 할 수 있습니다 (: Qt는 적어도 시간에 어떤 나중에에서 적절한 삭제 관심 있도록이를 수용하려면은 parent 인수에 의미있는 무언가를 전달할 수 있습니다 따라서 신호 슬롯 연결)을 다른 객체 (예 : 작업 자체 및 항목이 선택되지 않은 경우 작업 삭제 또는 이와 유사한 것)에 연결합니다.

또는, 현재 선택된 항목에 대한 Callback 개체를 기억하고 삭제 된 후에는 적절한 삭제를 직접 처리 할 수 ​​있습니다.

면책 조항 : 위의 예제에는 C++ 11이 많이 포함되어 있지만 C++ 03에서는이 부분을 다시 작성해야 할 필요가 없습니다. 마찬가지로이 해결책은 더 이상 어쩌면 std::function 대신에 템플릿 화 된 펑터를 사용합니다 (그러나 Qt 메타 오브젝트 시스템이 템플릿을 많이 좋아하지 않는다는 것을 정확하게 기억한다면).


편집 : 그냥 하나에 동작을 연결 : 결국 그의 의견에 프랭크 Osterfeld에 의해 제안 된 솔루션은 위의 내 지나치게 일반적인 개체 수명 광기보다는 상황에 훨씬 더 간단한 방법이 될 수있다 (아마도 메인 위젯 또는 데이터 벡터를 포함하는 항목 모델) 높은 수준의 객체와 현재 선택한 항목에 F 전화의 슬롯 :

connect(action, SIGNAL(triggered()), this, SLOT(callF())); 
... 
void MyController::callF() 
{ 
    treeView.selectedItem()->F(); 
} 
관련 문제