在单独的 GUI 类(菜单、工具栏等)之间共享操作的最佳方式是什么



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

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

一些可能性:

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

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

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

    3. 从主窗口提供getter,并让子组件获得它们想要的操作。子组件通常已经有一个指向主窗口的指针。这使得主窗口不知道谁关心哪个操作,但它也暴露了一群公共成员(除非我使用律师-客户端习语或类似的)。

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

  2. 定义"主"用例中的操作,例如,主菜单,然后为其他所有人提供getter。这将它们放在一个地方,这意味着主窗口将需要为主菜单提供单个getter函数。但是它仍然将一堆内部成员公开。

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

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

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

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

PS:命名动作的另一个用例是,如果你的软件有一个SDK,你可以很容易地提供自动化api(例如ApiExecuteAction(Actions.COPY))。

它们是可选的,还是你的类需要它们?

如果你的类要求他们传递给构造函数,你说这里的参数列表会变得很长?那么,为什么不先将动作打包到一个对象中,然后使用工厂方法来创建该对象呢?

如果没有,则用with setter和getter传递。

你可以阅读和考虑的是依赖注入的模式,你可以在这里了解更多:

什么是依赖注入?

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

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

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;
};

我强烈推荐1.1或1.2作为最简单的解决方案。

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

1.4将全局唯一性问题转移到一个新的"名称空间"——命令存储库所在的空间。

2.0暴露了很多私人信息,在我看来是最糟糕的。

顺便说一句,如果你还没有读过Command模式,我建议你读一下

相关内容

  • 没有找到相关文章

最新更新