在不同的GUI类(菜单,工具栏等)之间共享操作的最佳方法是什么?

时间:2014-07-07 19:13:28

标签: c++ qt oop user-interface qt5

我有几组动作(例如,复制,粘贴,撤消,重做,显示可停靠窗口XYZ,缩放等),我不想在多个位置复制但由不同部分共享GUI,如主菜单,工具栏和右键菜单。

分享它们的最佳方式是什么?我正在使用Qt 5.3和C ++,但这主要是独立于任何特定的GUI框架或语言。

一些可能性:

  1. 指定一个中心位置,比如主窗口,用文本,图标和回调创建所有这些位置。然后:

    1. 在创建GUI的子组件时,将操作传递给构造函数。这可以使构造函数参数列表相当长。

    2. 构建子组件后,在GUI的子组件上调用setter,并传入所有必要的操作。这使得构造函数更短,但最终并不是更漂亮。

    3. 从主窗口提供getter并让子组件获得他们想要的操作。子组件通常具有指向主窗口的指针。这使得主窗口不知道谁在乎哪个动作,但它也暴露了一群公众成员(除非我使用Attorney-Client idiom或类似的)。

    4. 将它们添加到一个单独的global-ish存储库,其中主窗口添加它们,用户根据需要按名称或键或其他内容查找它们。这与其他选项类似,但将关注点分开一点,并且只公开一个参数化的getter而不是一堆特定的getter。缺点:这会添加一个每个人都可以访问的全局对象。

  2. 在“主”用例中定义操作,比如主菜单,然后为其他人设置getter。这将它们定位在一个地方,并且意味着主窗口需要为主菜单提供单个getter功能。但它仍然公开了一大堆内部成员。

  3. 最好的方法是什么?我还没有在这里列出更好的东西吗?

4 个答案:

答案 0 :(得分:2)

它们是可选的,还是您的班级需要它们?

如果你的类要求他们将它们传递给构造函数,你在这里说参数列表会变长吗?那么为什么不首先将动作打包到对象中并使用工厂方法创建所述对象。

如果他们没有使用setter和getter传递它们。

您可以阅读并考虑的是依赖注入的模式,您可以在这里阅读更多信息:

What is dependency injection?

答案 1 :(得分:2)

让我们退后一步,看看正在制作的软件。拥有可自定义的UI通常是一种很好的做法,这样用户就可以创建/修改应用程序菜单和工具栏。这需要使用配置文件(.xml或.cfg)创建工具栏/菜单,该文件只是将菜单/工具栏项绑定到操作。

因此,要求动作具有唯一的名称/动作代码,使用它们可以被引用。

因此我建议1.4。您可以使用接收操作名称/代码作为参数的ActionFactory按需创建操作,也可以强制Actions使用ActionRegistry注册自己(全局! )他们可以从中查找。

PS:命名操作的另一个用例是,如果您的软件有SDK,您可以轻松提供自动化API(例如ApiExecuteAction(Actions.COPY))。

答案 2 :(得分:1)

在某些对话框中重复使用QAction的问题是重新连接信号。

您可以避免此问题创建一组类来存储一组信号。像这样:

template < class T >
class EditionSet
{
    T* parent;

  public:

    EditionSet( T* parent )
      : parent( parent )
    {
      cutAction = new QAction( "Cut", parent );
      copyAction = new QAction( "Copy", parent );
      pasteAction = new QAction( "Paste", parent );

      QObject::connect( cutAction, SIGNAL( triggered( ) ),
                        parent, SLOT( CutActionTriggered( ) ) );

      QObject::connect( copyAction, SIGNAL( triggered( ) ),
                        parent, SLOT( CopyActionTriggered( ) ) );

      QObject::connect( pasteAction, SIGNAL( triggered( ) ),
                        parent, SLOT( PasteActionTriggered( ) ) );
    }

    ~EditionSet( )
    {
      QObject::disconnect( cutAction, SIGNAL( triggered( ) ),
                           parent, SLOT( CutActionTriggered( ) ) );

      QObject::disconnect( copyAction, SIGNAL( triggered( ) ),
                           parent, SLOT( CopyActionTriggered( ) ) );

      QObject::disconnect( pasteAction, SIGNAL( triggered( ) ),
                           parent, SLOT( PasteActionTriggered( ) ) );

      delete cutAction;
      delete copyAction;
      delete pasteAction;
    }

    QAction* cutAction;
    QAction* copyAction;
    QAction* pasteAction;
};

class dialog : public QDialog
{
    Q_OBJECT

  public:

    dialog::dialog( QWidget* parent )
      : QDialog( parent ),
        ui( new Ui::dialog ),
        editionSet( EditionSet< dialog >( this ) )
    {
      // ...

      ui->mainToolBar->addAction( editionSet.cutAction );
      ui->mainToolBar->addAction( editionSet.copyAction );
      ui->mainToolBar->addAction( editionSet.pasteAction );
    }

  private:

    EditionSet< dialog > editionSet;
};

如果始终以相同的顺序插入操作,则可以改进此类以允许“自动插入”。

template < class T >
class EditionSet
{
    T* parent;
    QAction* cutAction;
    QAction* copyAction;
    QAction* pasteAction;

  public:

    EditionSet( T* parent )
      : parent( parent )
    {
      cutAction = new QAction( "Cut", parent );
      copyAction = new QAction( "Copy", parent );
      pasteAction = new QAction( "Paste", parent );

      QObject::connect( cutAction, SIGNAL( triggered( ) ),
                        parent, SLOT( CutActionTriggered( ) ) );

      QObject::connect( copyAction, SIGNAL( triggered( ) ),
                        parent, SLOT( CopyActionTriggered( ) ) );

      QObject::connect( pasteAction, SIGNAL( triggered( ) ),
                        parent, SLOT( PasteActionTriggered( ) ) );

    }

    ~EditionSet( )
    {
      QObject::disconnect( cutAction, SIGNAL( triggered( ) ),
                           parent, SLOT( CutActionTriggered( ) ) );

      QObject::disconnect( copyAction, SIGNAL( triggered( ) ),
                           parent, SLOT( CopyActionTriggered( ) ) );

      QObject::disconnect( pasteAction, SIGNAL( triggered( ) ),
                           parent, SLOT( PasteActionTriggered( ) ) );

      delete cutAction;
      delete copyAction;
      delete pasteAction;
    }

    void AddActionsTo( QWidget* container )
    {
      container->addAction( cutAction );
      container->addAction( copyAction );
      container->addAction( pasteAction );
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

  public:
    MainWindow(QWidget *parent = 0)
      : QMainWindow( parent ),
        ui( new Ui::MainWindow )
        editionSet( EditionSet< MainWindow >( this ) )
    {
      ui->setupUi(this);

      editionSet.AddActionsTo( ui->mainToolBar );
      editionSet.AddActionsTo( ui->menuBar );
    }

  private:

    EditionSet< MainWindow > editionSet;
};

答案 3 :(得分:1)

我强烈建议1.1或1.2作为最不可靠的解决方案。

1.3以非常繁琐的方式极大地扩展了窗口类的公共接口。

1.4将全局唯一性问题卸载到新的“名称空间” - 命令存储库所在的位置。

2.0暴露了很多私人信息,对我来说似乎是最糟糕的。

顺便说一句,如果你还没有读过Command模式,我推荐它。