diff --git a/modules/gui/pymod/CMakeLists.txt b/modules/gui/pymod/CMakeLists.txt index 7a627db27285d8914a5857cdbdbe170dda7cdb69..a09c9c1e7cfb691ec37c5d894bc3803dd6f930f8 100644 --- a/modules/gui/pymod/CMakeLists.txt +++ b/modules/gui/pymod/CMakeLists.txt @@ -91,6 +91,7 @@ set(OST_GUI_PYMOD_DNG_MODULES __init__.py termuse.py init.py + menu.py ) pymod(NAME gui CPP ${OST_GUI_PYMOD_SOURCES} diff --git a/modules/gui/pymod/__init__.py b/modules/gui/pymod/__init__.py index 500c31ece281ed8968dc3b5dc34ffcf6f77e580c..e8571e14d88621c5f993088c3d826f082f7b6462 100644 --- a/modules/gui/pymod/__init__.py +++ b/modules/gui/pymod/__init__.py @@ -42,6 +42,7 @@ def ClearMessageWidget(): from PyQt4.QtGui import * +from PyQt4.QtCore import * from ost import gfx def PickColor(default=gfx.WHITE): @@ -59,4 +60,145 @@ def PickColor(default=gfx.WHITE): if not qt_color.isValid(): return None return gfx.Color(qt_color.red()/256.0, qt_color.green()/256.0, - qt_color.blue()/256.0) \ No newline at end of file + qt_color.blue()/256.0) + +def GetMenu(menu_name, create=False): + persp=GostyApp.Instance().perspective + if isinstance(menu_name[0], QAction): + return menu_name[0] + else: + node=persp.GetMenu(menu_name[0]) + for name in menu_name[1:]: + found=False + for action in node.actions(): + if str(action.text())==str(name): + found=True + node=action + break + if not found: + if create: + node=node.addMenu(name) + else: + raise ValueError("Menu '%s' doesn't exist" % ', '.join(menu_name)) + return node + + + +def AddMenuAction(*args, **kwargs): + """ + Add menu action to main menu. + + This function lets you conveniently add actions to the main menu. To add a new + new action "Background Color" to the "Scene" menu, simply use + + .. code-block:: python + + def SetBackgroundColor(): + scene.bg=gfx.PickColor(scene.bg) + + AddMenuAction('Scene', "Background Color", SetBackgroundColor) + + This will add the menu "Scene" if it does not exist yet, register the action + "Background Color" and execute the function SetBackgroundColor whenever the + action is triggered. + + To assign a keyboard shortcut to the action, you can use the shortcut + argument: + + .. code-block:: python + + AddMenuAction('Scene', 'Background Color', SetBackgroundColor, + shortcut='Ctrl+B') + + Whenever you press Ctrl+B (Cmd+B on MacOS X), the action will be executed. + + Very often menu actions are coupled to the current selected objects in the + scene menu. These menu actions are either enabled or disabled depending on the + type of the selected objects. To easily support this scenario, the "enable" + state of the menu action can be tightly coupled to the scene menu by providing + a callable to the enabled argument. As an example, the following menu action + is only enabled when exactly one gfx.Entity is selected. + + .. code-block:: python + + AddMenuAction('Scene', 'PrintAtomCount', PrintAtomCount, + enabled=OneOf(gfx.Entity)) + + OneOf is a simple function object that takes any number of classes and returns + true when the selected object is an instance. Alterantively, you might want to + use ManyOf or supply a custom function that evaluates the state of the menu + action to suit your needs. + """ + class MenuActionEnabler(QObject): + def __init__(self, predicate, action): + QObject.__init__(self, action) + self.predicate=predicate + self.action=action + app=GostyApp.Instance() + QObject.connect(app.scene_win.qobject, SIGNAL('ActiveNodesChanged()'), + self.TestEnable) + self.TestEnable() + + def TestEnable(self): + self.action.setEnabled(self.predicate()) + persp=GostyApp.Instance().perspective + menu_name=args[:-1] + function=args[-1] + if isinstance(menu_name[0], QMenu): + node=menu_name[0] + else: + node=persp.GetMenu(args[0]) + for name in menu_name[1:-1]: + found=False + for action in node.actions(): + if str(action.text())==str(name): + node=action + break + if not found: + node=node.addMenu(name) + action=node.addAction(str(menu_name[-1])) + if 'shortcut' in kwargs: + action.setShortcut(QKeySequence(kwargs['shortcut'])) + if 'checkable' in kwargs: + action.setCheckable(kwargs['checkable']) + if 'checked' in kwargs: + action.setChecked(kwargs['checked']) + if 'enabled' in kwargs: + if callable(kwargs['enabled']): + enabler=MenuActionEnabler(kwargs['enabled'], action) + else: + action.setEnabled(kwargs['enabled']) + QObject.connect(action, SIGNAL('triggered()'), function) + return action + + +class OneOf: + def __init__(self, *classes): + self.classes=classes + def __call__(self): + sel=SceneSelection.Instance() + if sel.GetActiveNodeCount()!=1: + return False + node=sel.GetActiveNode(0) + for cl in self.classes: + if isinstance(node, cl): + return True + return False + +class ManyOf: + def __init__(self, *classes): + self.classes=classes + def __call__(self): + sel=SceneSelection.Instance() + if sel.GetActiveNodeCount()==0: + return False + for i in range(sel.GetActiveNodeCount()): + node=sel.GetActiveNode(i) + found=False + for cl in self.classes: + if isinstance(node, cl): + found=True + break + if not found: + return False + return True diff --git a/modules/gui/pymod/dng/init.py b/modules/gui/pymod/dng/init.py index de1d423e9a7637e1ee5d30d9ab1f7b8cee422997..b56ce5ab2f83692720e1d2f2b8be96300adbf7bb 100644 --- a/modules/gui/pymod/dng/init.py +++ b/modules/gui/pymod/dng/init.py @@ -22,7 +22,7 @@ from ost.gui.init_context_menu import _InitContextMenu from ost.gui.init_splash import _InitSplash from ost.gui.dng import termuse from ost.gui.helpwidget import help - +import ost.gui.dng.menu from PyQt4.QtGui import * def _my_exit(code): QtGui.QApplication.instance().quit() @@ -78,18 +78,20 @@ def _InitFrontEnd(try_stereo): app.SetAppTitle("DNG") app.TryStereo(try_stereo) main_area=app.perspective.main_area - _InitMenuBar(app) _InitSpaceNav(app) _InitContextMenu(app) - main_area.AddPersistentWidget("3D Scene", "gl_win" , app.gl_win, int(QtCore.Qt.WindowMaximized)) + main_area.AddPersistentWidget("3D Scene", "gl_win" , app.gl_win, + int(QtCore.Qt.WindowMaximized)) + _InitInspector(app) + ost.gui.dng.menu._InitMenu() app.perspective.Restore() additional_modules=getattr(__main__, 'ADDITIONAL_GUI_MODULES', []) for module_name in additional_modules: __import__(module_name) app.ProcessEvents() - - _InitInspector(app) + + if not _InitPanels(app): _InitSplash() diff --git a/modules/gui/pymod/dng/menu.py b/modules/gui/pymod/dng/menu.py new file mode 100644 index 0000000000000000000000000000000000000000..40509ed71d3fa7435eecf6acbef6b0fa6f4a058b --- /dev/null +++ b/modules/gui/pymod/dng/menu.py @@ -0,0 +1,148 @@ +from PyQt4.QtCore import * +from PyQt4.QtGui import * +from ost import gui, gfx, io +from ost.gui.scene.loader_manager_widget import LoaderManagerWidget +from ost.gui.init_splash import _InitSplash +from ost.gui.dng import termuse +import sys +class FileMenu(QMenu): + def __init__(self, parent=None): + QMenu.__init__(self, parent) + self.setTitle('File') + gui.AddMenuAction(self, 'Open', self._Open, shortcut='Ctrl+O') + gui.AddMenuAction(self, 'Save As...', self._SaveAs, + shortcut='Ctrl+Shift+S', + enabled=gui.OneOf(gfx.Entity)) + def _Open(self): + filename=QFileDialog.getOpenFileName(None, 'Open file','') + if(QFileInfo(filename).isFile()): + gui.FileLoader.LoadObject(str(filename)) + + def _SaveAs(self): + sel_node=gui.SceneSelection.Instance().GetActiveNode(0) + filename=QFileDialog.getSaveFileName(None, 'Save As', + '%s.pdb' % sel_node.name) + io.SavePDB(sel_node.view, str(filename)) + + + +class SceneMenu(QMenu): + def __init__(self, parent=None): + QMenu.__init__(self, parent) + self.setTitle('Scene') + QObject.connect(self, SIGNAL('aboutToShow()'), self._AboutToShow) + gui.AddMenuAction(self, 'Background Color', self._SetSceneBackground) + self.fog_action=gui.AddMenuAction(self, 'Depth Cueing', self._ToggleFog, + shortcut='Ctrl+Shift+F', checkable=True, + checked=gfx.Scene().fog) + gui.AddMenuAction(self, 'Center', self._Center, + enabled=gui.ManyOf(gfx.GfxObj)) + gui.AddMenuAction(self, 'Fit To Screen', self._FitToScreen, + enabled=gui.OneOf(gfx.Entity)) + def _AboutToShow(self): + self.fog_action.setChecked(gfx.Scene().fog) + + def _Center(self): + sel=gui.SceneSelection.Instance() + gfx.Scene().center=sel.GetActiveNode(0).center + + def _SetSceneBackground(self): + new_color=gui.PickColor(gfx.Scene().bg) + if new_color: + gfx.Scene().bg=new_color + + def _ToggleFog(self): + gfx.Scene().fog=not gfx.Scene().fog + self.fog_action.setChecked(gfx.Scene().fog) + + def _FitToScreen(self): + sel=gui.SceneSelection.Instance() + gfx.FitToScreen(sel.GetActiveNode(0)) + +class WindowMenu(QMenu): + def __init__(self, parent=None): + QMenu.__init__(self, parent) + self.setTitle('Window') + self.loader_manager=LoaderManagerWidget() + gosty=gui.GostyApp.Instance() + QObject.connect(self, SIGNAL('aboutToShow()'), self._AboutToShow) + inspector_visible=gosty.GetWidget('InspectorDialog').isVisible() + self._gl_win_action=gui.AddMenuAction(self, 'GL Window', + self._ToggleShowGLWindow, checkable=True, + checked=gosty.gl_win.qobject.isVisible(), + shortcut='Ctrl+G') + self._inspector_action=gui.AddMenuAction(self, 'Inspector', + self._ToggleShowInspector, + checkable=True, + checked=inspector_visible, + shortcut='Ctrl+I') + self.addSeparator() + self.addMenu(gosty.perspective.panels.menu) + gui.AddMenuAction(self, 'Reset View', self._ResetView) + def _AboutToShow(self): + gosty=gui.GostyApp.Instance() + self._gl_win_action.setChecked(gosty.gl_win.qobject.isVisible()) + inspector_visible=gosty.GetWidget('InspectorDialog').isVisible() + self._inspector_action.setChecked(inspector_visible) + + def _ToggleShowGLWindow(self): + gosty=gui.GostyApp.Instance() + gl_win=gosty.GetGLWin() + if gl_win and gl_win.qobject.isHidden(): + gl_win.Show() + else: + gl_win.Hide() + + def _ToggleShowInspector(self): + gosty=gui.GostyApp.Instance() + inspector=gosty.GetWidget('InspectorDialog') + if inspector and inspector.isHidden(): + inspector.show() + else: + inspector.hide() + + def _ResetView(self): + msg_box = QMessageBox() + msg_box.setWindowTitle("Reset the Panels and Widget"); + msg_box.setIcon(QMessageBox.Question) + msg_box.setText("Do you really want to reset the Panels and Widgets?"); + msg_box.setStandardButtons(QMessageBox.Yes | + QMessageBox.Cancel); + msg_box.setDefaultButton(QMessageBox.Cancel); + ret = msg_box.exec_(); + if(ret == QMessageBox.Yes): + settings = QSettings() + settings.setValue("restore_settings",QVariant(False)) + info_box = QMessageBox() + info_box.setStandardButtons(QMessageBox.Ok) + info_box.setIcon(QMessageBox.Information) + info_box.setWindowTitle("Restart OpenStructure") + info_box.setText("You must restart OpenStructure for the changes to take effect!"); + info_box.exec_(); + +class HelpMenu(QMenu): + def __init__(self, parent=None): + QMenu.__init__(self, parent) + self.setTitle('Help') + gui.AddMenuAction(self, 'Documentation', self._VisitDocs) + gui.AddMenuAction(self, 'About', self._ShowAboutDialog) + if sys.platform=='darwin': + gui.AddMenuAction(self, 'Install Command Line Tool', + termuse.InstallTerminalPrograms) + def _VisitDocs(self): + QDesktopServices.openUrl(QUrl("http://www.openstructure.org/docs/")) + + def _ShowAboutDialog(self): + _InitSplash() + + +def _InitMenu(): + mbar=gui.GostyApp.Instance().perspective.GetMenuBar() + file_menu=FileMenu(mbar) + scene_menu=SceneMenu(mbar) + win_menu=WindowMenu(mbar) + help_menu=HelpMenu(mbar) + mbar.addMenu(file_menu) + mbar.addMenu(scene_menu) + mbar.addMenu(win_menu) + mbar.addMenu(help_menu) \ No newline at end of file diff --git a/modules/gui/pymod/export_gosty.cc b/modules/gui/pymod/export_gosty.cc index b0307e91ec44d3ab4bac5583ef7720468e9296d5..5a9ee652d658d2fe7364ddc2e491777d0279ce58 100644 --- a/modules/gui/pymod/export_gosty.cc +++ b/modules/gui/pymod/export_gosty.cc @@ -79,6 +79,11 @@ void app_add_widget_to_app_b(GostyApp* app, const QString& ident, } } +object get_widget(GostyApp* app, const QString& ident) +{ + return get_py_qobject<QWidget>(app->GetWidget(ident)); +} + void export_Gosty() { class_<GostyApp, boost::noncopyable>("GostyApp", no_init) @@ -104,6 +109,7 @@ void export_Gosty() return_value_policy<reference_existing_object>())) .def("GetToolOptionsWin", &GostyApp::GetToolOptionsWin, return_value_policy<reference_existing_object>()) + .def("GetWidget", &get_widget) .add_property("tool_options_win", make_function(&GostyApp::GetToolOptionsWin, return_value_policy<reference_existing_object>())) .def("GetMessageWidget", &GostyApp::GetMessageWidget, diff --git a/modules/gui/pymod/init_menubar.py b/modules/gui/pymod/init_menubar.py index ac5c23e23dd0aebcc57f297ea3bd42fa6d11386a..e0c05c620dfda0dd08a6f3f7bb3fb30c58e7049e 100644 --- a/modules/gui/pymod/init_menubar.py +++ b/modules/gui/pymod/init_menubar.py @@ -127,8 +127,8 @@ class InitMenuBar(QtCore.QObject): info_box.exec_(); def _InitMenuBar(app): - InitMenuBar(app.perspective.menubar) - + #InitMenuBar(app.perspective.menubar) + pass ## \example menubar_example.py # # Shows how to use PyQt to add a menu from within Python and interact diff --git a/modules/gui/pymod/scene/init_inspector.py b/modules/gui/pymod/scene/init_inspector.py index 462ef4b3b04121a7d42e11f17c86d0f45262ff11..5bd17ba3db87180376bb62c85df7275ebf07f3a2 100644 --- a/modules/gui/pymod/scene/init_inspector.py +++ b/modules/gui/pymod/scene/init_inspector.py @@ -23,28 +23,6 @@ from ost import gui from ost import gfx from PyQt4 import QtCore, QtGui from ost.gui.scene.inspector_widget import InspectorDialog - -class InitInspectorMenu(QtCore.QObject): - def __init__(self, inspectordialog, menu_bar=None): - QtCore.QObject.__init__(self, menu_bar) - - self.inspector_ = inspectordialog - persp=gui.GostyApp.Instance().perspective - options=persp.GetMenu("Options") - - self.show_ = options.addAction("&Inspector gadget") - self.show_.setShortcut('Ctrl+I') - self.show_.setCheckable(True) - self.show_.setChecked(not self.inspector_.isHidden()) - self.connect(self.show_, QtCore.SIGNAL('triggered()'), self.Toggle) - self.connect(inspectordialog, QtCore.SIGNAL('visible'), self.UpdateCheckbox) - def Toggle(self): - self.inspector_.setVisible(not self.inspector_.isVisible()) - - def UpdateCheckbox(self, visibility): - self.disconnect(self.show_, QtCore.SIGNAL('triggered()'), self.Toggle) - self.show_.setChecked(visibility) - self.connect(self.show_, QtCore.SIGNAL('triggered()'), self.Toggle) def _InitInspector(app): mywidget = InspectorDialog(app.gl_win.qobject) @@ -52,5 +30,4 @@ def _InitInspector(app): mywidget.show() app.AddWidgetToApp("InspectorDialog", mywidget) menu_bar=app.perspective.GetMenuBar() - InitInspectorMenu(mywidget,menu_bar) diff --git a/modules/gui/src/gosty_app.cc b/modules/gui/src/gosty_app.cc index 3136284c24e3c084f92a62a9757af0868db61fd1..9042605a2bda008f7ac0aeed8e55ae6e378d636d 100644 --- a/modules/gui/src/gosty_app.cc +++ b/modules/gui/src/gosty_app.cc @@ -169,6 +169,14 @@ void GostyApp::AddWidgetToApp(const QString& ident, QWidget* widget) external_widgets_[ident]->LoadGeom("ui/external_widgets/"); } +QWidget* GostyApp::GetWidget(const QString& ident) +{ + if (external_widgets_.contains(ident)) { + return external_widgets_[ident]->GetWidget(); + } + return NULL; +} + void GostyApp::RemoveWidgetFromApp(const QString& ident){ if(external_widgets_.contains(ident)){ external_widgets_[ident]->SaveGeom("ui/external_widgets/"); diff --git a/modules/gui/src/gosty_app.hh b/modules/gui/src/gosty_app.hh index 001777372d82a37f0bab252b7d75fe3ef8f9dcfd..b419fd7da841965c79c087ab37538fa1fc0da10d 100644 --- a/modules/gui/src/gosty_app.hh +++ b/modules/gui/src/gosty_app.hh @@ -144,7 +144,8 @@ public: /// \param ident the ident is used to identify a custom widget. It must be unique. Otherwise there might occur an unexpected behaviour. /// \param widget the widget which will be added to the GostyApp void AddWidgetToApp(const QString& ident, QWidget* widget); - + + QWidget* GetWidget(const QString& ident); /// \brief remove a custom QWidget from the gosty_app /// /// This method removes a custom widget from OpenStructure. If the given ident is not known, nothing happens. Read more about custom widgets at \ref #AddWidgetToApp() . diff --git a/modules/gui/src/perspective.cc b/modules/gui/src/perspective.cc index 2df134c5a6c5d54dfd2a582baca5012f17b37d88..bf6745b743f7f0b47a8e7e8f690c7642039616f1 100644 --- a/modules/gui/src/perspective.cc +++ b/modules/gui/src/perspective.cc @@ -120,15 +120,17 @@ QMenuBar* Perspective::GetMenuBar() QMenu* Perspective::GetMenu(const QString& name) { - QMenu* menu; - if(!menus_.contains(name)){ - menu = menu_bar_->addMenu(name); - menus_.insert(name,menu); + QMenu* menu=NULL; + QList<QAction *> actions=menu_bar_->actions(); + for (QList<QAction*>::iterator i=actions.begin(), + e=actions.end(); i!=e; ++i) { + if ((*i)->text()==name) { + return (*i)->menu(); + } } - else{ - menu = menus_[name]; - } - return menu; + QMenu* new_menu=new QMenu(name, menu_bar_); + menu_bar_->addMenu(new_menu); + return new_menu; } PanelManager* Perspective::GetPanels() diff --git a/modules/gui/src/perspective.hh b/modules/gui/src/perspective.hh index 74cc10b810f81d91c228b31b2e1175aff39a4e1b..84a594981775670ee607a8c4b303c41ffe4dd414 100644 --- a/modules/gui/src/perspective.hh +++ b/modules/gui/src/perspective.hh @@ -98,7 +98,6 @@ private: void SetupQuickAccessBar(); QWidget* central_; QMenuBar* menu_bar_; - QMap<QString,QMenu*> menus_; MainArea* main_area_; PanelManager* panels_; QWidget* quick_access_bar_; diff --git a/modules/gui/src/widget_geom_handler.hh b/modules/gui/src/widget_geom_handler.hh index 164719ba4ae35222ceb4b60992a8465d7deed7bc..191a4c992e0f89cfc95760b71b93b3bba81a3bb2 100644 --- a/modules/gui/src/widget_geom_handler.hh +++ b/modules/gui/src/widget_geom_handler.hh @@ -34,7 +34,7 @@ class DLLEXPORT_OST_GUI WidgetGeomHandler: public QObject{ void LoadGeom(const QString& prefix); void SaveGeom(const QString& prefix); - + QWidget* GetWidget() { return widget_; } private: QWidget* widget_; };