2017-12-09 8 views
0

このゲームでは、私たちは「ゲーム」を作る必要があります。このゲームでは、迷路を視覚化し、その道を見つけます。この視覚化は、2つの異なる方法で可能でなければなりません。私たちの場合には、テキストベースの視覚化とより良い方法です。戦略パターンQtあいまいな基盤

これにQtを使用する必要があります。QMainWindowにQWidgetを使用しています。このウィジェットは、1つまたは他の視覚化になります。ゲーム中に視覚化を切り替えることができ、戦略パターンを使用してインタフェース(ViewInterface)を作成し、両方のビジュアライゼーションでこれを実装できることが分かります。 ViewInterfaceの実装のほかに、両方のビジュアライゼーションは別のクラスを継承し、テキストベースではQPlainTextEdit(テキスト付きのQWidgetを持つ)であり、FancyではQDialogです。私のコントローラでは、私のコントローラには、QWidetを満たすために使用されるViewInterfaceへのポインタがありますが、このViewInterfaceもQWidgetから継承しなければなりません。QWidgetはこのエラーを引き起こします:QObjectは 'TerminalView'のあいまいなベースです。

ゲームの再生中にビューを切り替えることができ、現在アクティブなビューでのみ更新を呼び出す必要があるため、特定のビューを 'setWidget'に渡すことはできません。

here is a visualization of the inheritance structure:

私が何か間違ったことをやっているか、どのように私はこの問題を解決することができますか? (私はすでにそれについて考えていましたが、解決策はありません)。

答えて

1

ここでの問題は、あなたが二回にQObjectを継承していることである:最初のViewInterface階層に、QDialog階層(diamond problem)第二。 (仮想継承は、階層全体で使用する必要がありますので、は動作しません)あなたのFancyViewとがTextViewのクラスの仮想継承を使用して

を試してみてくださいしかし、あなたのデザインを持つ別の問題がある:QDialogとQPlainTextEdit両方はQWidgetのを継承します。この問題を解決するには、次の操作を行います。

  1. QObjectから継承せずにViewInterfaceを作成します。この抽象クラスはFancyViewとTextViewのインタフェースを定義し、共通のロジックを実装する場合と実装しない場合があります。

  2. QDialogとViewInterface、QPlainTextEditとViewInterfaceそれぞれから複数の継承を持つFancyViewとTextViewを実装します。

このようにして、あいまいな基本クラスに問題が発生することはありません。

UPDATE:

Did not see your edit yet: indeed this would solve the issue, but another problem rises: if I do this, I can't use a ViewInterface pointer to set my QWidget. It is possible indeed, but in my opinion this is not really clean

まあ、それは本当の問題です。明白な解決策はQWidget*の代わりにViewInterface*を使用しないことです。しかし、これはかなりのコードを変更する必要があることを意味し、実際にはあなたのケースではそれほど大きくないかもしれません。与えられたコメントに関して

、私は別の解決策を提案する:(全ての所望のインタフェース機能を有する)QWidget継承ViewInterface

  1. を:ViewInterfaceコンストラクタセットレイアウトで

    class ViewInterface: public QWidget { 
        Q_OBJECT 
        ... 
    } 
    
  2. すべきウィジェットで使用され、設定:

    ViewInterface::ViewInterface (QWidget* i_parent) 
        : QWidget (i_parent) { 
        auto layout {new QGridLayout (this)}; 
    
        // Zeroed margins to make full fit. 
        layout->setContentsMargins (0, 0, 0, 0); 
    
        setLayout (layout); 
    } 
    
  3. 派生クラスのコンストラクタで
  4. レイアウトに特定のウィジェットを追加:

    class FancyView : public ViewInterface { 
        Q_OBJECT 
    
        FancyView (QWidget* i_parent) 
         : ViewInterface (i_parent) 
         , dialog_widget_ {new QDialog (this)} { 
         layout->addWidget (dialog_widget_); 
        } 
        ... 
    
    private: 
        QDialog* dialog_widget_; 
    } 
    
  5. をターゲット・ウィジェットを使用して所望のインターフェースを実装します。イベントを処理する場合は、QObject::eventFilter()を使用します。この場合、上記のコードのFancyViewオブジェクトをdialog_widget_のイベントフィルタとして設定する必要があります。

注:この場合、あなたはQDialogとしてFancyViewを使用することはできません。この問題の解決策は、プロキシQDialogシグナル、スロット、およびパブリック関数であり、FancyViewのメソッド、シグナル、およびスロットのプロキシとして機能するさらに別のラッパークラスFancyViewDialogを作成します。それは素晴らしい解決策ではありませんが、ViewInterfaceQWidgetの間の "is-a"関係を可能にするダイヤモンド問題の周りに他の方法はありません。

+0

、私はエラーを取得する 'QObjectを' 'はTerminalView' 私のコードの曖昧な拠点です。 .. }; ' – YFrickx

+0

あなたの編集はまだ見えませんでしたが、これで問題は解決しますが、別の問題が発生します:これを行うと、ViewInterfaceポインタを使ってQWidgetを設定できません。確かに可能ですが、私の意見ではこれは本当にきれいではありません – YFrickx

0

インターフェイスは、仮想メソッドで抽象的であり、具体的な基本クラスがありません。 ViewInterfaceQWidgetから継承しないでください。それはあなたの問題を解決します。

は、今そこにQWidgetViewInterfaceインスタンスを変換するには、少なくとも2つのソリューションです:

  1. テンプレートViewInterfaceのユーザーは、それらを使用したコンクリート型はQWidgetから派生実際にはないことを確認しています。型が実行時の多型性がない場合は、これが機能します。

  2. ランタイムポリモーフィズムがある場合は、インターフェイスにQWidget * widget() = 0メソッドを追加し、それを派生メソッドに実装します。それは簡単です:QWidget * widget() override { return this; }

インターフェイスは信号とスロットの両方を持つことができますが、仮想メソッドでなければなりませんが、確かに動作します。例えば、 this answerを使用して仮想信号を開始します。

ViewInterfaceという2つの具体的な実装の間でいくつかのコードを共有する場合は、ViewInterfaceから派生した追加クラスを作成して共有コードを提供することができます。 TerminalViewFancyViewの両方がそのクラスから派生します。ヘルパークラスは、基本クラス型でパラメータ化することができます。: `クラスTerminalView:パブリック仮想QPlainTextEdit、パブリック仮想 ViewInterface { Q_OBJECT 私はこれを行う場合class TerminalView : ViewHelper<QPlainTextEdit> { ... };

関連する問題