3

クラスの関数をスロットにするには、クラスはQObjectから継承する必要があります。ただし、QObjectは非常に大量のメモリを消費します。それがいくらで、メモリが各クラスまたは各オブジェクト用であるかどうかはわかりません。私のコードには多くの小さなデータがあり、その関数はいつかスロットになる可能性があります。クラスの関数を使うときに一時的にスロットにする方法があるのだろうか。使用後、スロットコストのメモリは削除されます。次のコードは、要件を示しています。

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().
4

3 に答える 3

8

Qt5を使用できる場合は、シグナルをプレーン関数と静的メソッド(基本的にはおかしな名前のプレーン関数)に接続できます。

connect(action, &QAction::triggered, 
        &SmallData::statF); 

ここactionで、はQActionインスタンスであり、SmallData::statFはの静的メソッドですSmallData

Christian Rauのコメントに従って編集し、特定のインスタンスを呼び出すために、ラムダに接続することもできます。

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

すでにQt4を使用QSignalMapperすると、さらにいくつかのオブジェクトを使用して、ほぼ同じ効果を実現できます。vecこれにより、どのオブジェクトがそれを発行したかに基づいて、シグナルにパラメーター(この場合はおそらく整数インデックス)を追加することができます。ただし、Qt4では、レシーバーは常にQObjectである必要があります。

于 2013-02-04T16:18:06.227 に答える
3

シグナルスロットメカニズムを使用する場合、回避QObjectすることはできませんが、関数を呼び出すスロットを持つ一時オブジェクトを作成することができます。オブジェクトを適切に解放するように注意する必要があります。何かのようなもの:

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次に、必要なたびに(多かれ少なかれ一時的な)オブジェクトを作成します。

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

このoneShotパラメーターを使用すると、トリガーされたときにスロットが自動的に解消されるかどうかを制御できます。

唯一の問題は、このスロットが呼び出されない場合、漏れが発生していることですCallback。これに対応するために、引数に意味のあるものを渡すことができますparent。これにより、Qtは、少なくとも後のある時点で適切な削除を処理します。

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

このようにして、コールバックオブジェクト(したがってシグナルスロット接続)の存続期間を他のオブジェクトにバインドすることもできます(たとえば、アクション自体や、アイテムが選択されていない場合のアクションの削除など)。

または、現在選択されているアイテムのオブジェクトを記憶し、Callback削除されたら自分で適切に削除することもできます。

免責事項:上記の例にはC ++ 11がたくさん含まれていることに注意してください。ただし、これをC++03用に書き直す気にはなっていません。同様に、このソリューションをさらに改善することもできます。おそらく、の代わりにテンプレート化されたファンクターを使用しますstd::function(ただし、私が正しく覚えていれば、Qtメタオブジェクトシステムはテンプレートをそれほど好きではありません)。


編集:結局、フランク・オスターフェルトが彼のコメントで提案した解決策は、上記の私の過度に一般的なオブジェクトの生涯の狂気よりもはるかに簡単なアプローチかもしれません:アクションをより高いレベルのオブジェクト(メインウィジェットまたは多分データベクトルを含むアイテムモデル)そしてF現在選択されているアイテムを呼び出します:

connect(action, SIGNAL(triggered()), this, SLOT(callF()));
...
void MyController::callF()
{
    treeView.selectedItem()->F();
}
于 2013-02-04T15:59:57.883 に答える
0

Qtではあなたがやろうとしていることは不可能だと思います。

本当にQObjectを継承したくない場合は、ブースト信号とスロットメカニズムを確認することをお勧めします。

于 2013-02-04T15:46:32.967 に答える