我有几组操作(例如,复制,粘贴,撤销,重做,显示可停靠窗口XYZ,缩放等),我不想在多个位置复制,但GUI的不同部分共享,如主菜单,工具栏和右键菜单。
分享它们的最佳方式是什么?我使用Qt 5.3和c++,但这主要是独立于任何特定的GUI框架或语言。
一些可能性:指定一个中心位置,例如主窗口,以创建所有的文本,图标和回调。然后:
在创建GUI的子组件时将操作传递给构造函数。这可能使构造函数参数列表相当长。
在子组件构造完成后调用GUI子组件上的setter,并传入所有必要的操作。这使得构造函数更短,但最终并没有漂亮得多。
从主窗口提供getter,并让子组件获得它们想要的操作。子组件通常已经有一个指向主窗口的指针。这使得主窗口不知道谁关心哪个操作,但它也暴露了一群公共成员(除非我使用律师-客户端习语或类似的)。
将它们添加到一个单独的全局存储库中,在主窗口中添加它们,用户根据需要通过名称或键或其他东西查找它们。这与其他选项类似,但更好地分离了关注点,并且只公开了一个参数化的getter,而不是一堆特定的getter。缺点:这会增加一个全局对象,每个人都可以访问。
定义"主"用例中的操作,例如,主菜单,然后为其他所有人提供getter。这将它们放在一个地方,这意味着主窗口将需要为主菜单提供单个getter函数。但是它仍然将一堆内部成员公开。
最好的方法是什么?有没有更好的东西我没有在这里列出?
让我们退后一步,看看正在制作的软件。拥有可定制的UI通常是一种很好的做法,这样用户就可以创建/修改应用程序菜单和工具栏。这需要使用配置文件(.xml或.cfg)创建工具栏/菜单,它只是将菜单/工具栏项绑定到操作。
因此,action需要有唯一的名称/动作代码,使用它们可以被引用。
因此我推荐1.4。您可以使用接收操作名称/代码作为参数的ActionFactory
按需创建操作,或者您可以强制Actions
向ActionRegistry
注册自己(全局!),可以从中查找它们。
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模式,我建议你读一下