""" qtgui/guiUtils.py: CCP4MG Molecular Graphics Program Copyright (C) 2001-2008 University of York, CCLRC Copyright (C) 2009-2010 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ import os,types,copy from PyQt4 import QtGui,QtCore LOADED_PIXMAPS = {} ICON_EXTENSIONS= ['.svg','.png'] class mgMenu(QtGui.QMenu): def __init__(self,parent=None): QtGui.QMenu.__init__(self,parent) def mouseReleaseEvent(self,event): action = self.actionAt(QtCore.QPoint(event.x(),event.y())) if action: action.emit(QtCore.SIGNAL('mouseRelease')) #if event.key()!=QtCore.Qt.Key_Return: QtGui.QMenu.mouseReleaseEvent(self,event) # bummer - need python 2.5 #from functools import partial """ # kludge .. def partial(func,arg): #print "partial func,arg",func,arg def callme(): return func(arg) return callme """ class partial(object): def __init__(*args, **kw): self = args[0] self.fn, self.args, self.kw = (args[1], args[2:], kw) def __call__(self, *args, **kw): if kw and self.kw: d = self.kw.copy() d.update(kw) else: d = kw or self.kw return self.fn(*(self.args), **d) class partialWithSignal(object): def __init__(*args, **kw): self = args[0] self.fn, self.args, self.kw = (args[1], args[2:], kw) def __call__(self, *args, **kw): if kw and self.kw: d = self.kw.copy() d.update(kw) else: d = kw or self.kw return self.fn(*(self.args + args), **d) def checkState(inp): if inp: return QtCore.Qt.Checked else: return QtCore.Qt.Unchecked def fromCheckState(inp): if inp == QtCore.Qt.Unchecked: return 0 else: return 1 #---------------------------------------------------------------------- def fromMenu(text,menu_list,alias_list): #---------------------------------------------------------------------- # return the short alias for a given long menu text if menu_list.count(text): return alias_list[menu_list.index(text)] else: return text #---------------------------------------------------------------------- def createAction(name='',parent=None,definition={},icon_path='',default_icon='',existing_action=None): #---------------------------------------------------------------------- if definition: adef = definition else: adef = parent.getActionDef(name) #print "createAction",name,adef # Is it a 'build-in' action if adef.has_key('action'): #print "createAction build-in",name,adef['action'] return adef['action'] text = '' if adef.has_key('text'): if callable(adef['text']): text = adef['text']() else: text = adef['text'] # Is it grouped with other actions if existing_action is None: if adef.has_key('group'): group=parent.findChild(QtGui.QActionGroup,adef['group']) if not group: group = QtGui.QActionGroup(parent) group.setObjectName(adef['group']) a = group.addAction(text) else: a = QtGui.QAction(text,parent) else: a = existing_action a.setObjectName(name) if adef.has_key('shortcut') and adef['shortcut']: if isinstance(adef['shortcut'],types.IntType): a.setShortcut(adef['shortcut']) else: a.setShortcut(QtGui.QKeySequence(adef['shortcut'])) icon = createIcon(name,adef,icon_path=icon_path,default_icon=default_icon) if icon: a.setIcon(icon) a.setIconVisibleInMenu(True); tip = adef.get('toolTip','') if tip: a.setToolTip(tip) if adef.has_key('checkable') and adef['checkable']: a.setCheckable(1) if adef.has_key('checked') and callable(adef["checked"]): res = adef["checked"]() if type(res)==types.IntType or type(res)==types.BooleanType: a.setChecked(res) if adef.has_key('enabled'): if callable(adef["enabled"]): res = adef["enabled"]() else: res = adef["enabled"] if type(res)==types.IntType or type(res)==types.BooleanType: a.setEnabled(res) if adef.has_key('deleteLater') and adef['deleteLater']: actions.append(a) a.deleteLater() # Sort out a signal if adef.has_key('signal'): signal = adef['signal'] else: signal = 'triggered()' if adef.get('slot','') and existing_action is None: if isinstance(adef['slot'],types.ListType) and len(adef['slot'])>1: parent.connect(a,QtCore.SIGNAL(signal),partial(adef['slot'][0],adef['slot'][1])) else: parent.connect(a,QtCore.SIGNAL(signal),adef['slot']) return a #---------------------------------------------------------------------- def createIcon(name='',adef={},icon_path='',default_icon='unknown'): #---------------------------------------------------------------------- if adef.has_key('icon') and adef['icon']: icon_name = str(adef['icon']) else: icon_name = str(name) #print "Try making icon with name",icon_name,"with path",icon_path if os.path.exists(icon_name) and os.path.basename(icon_name) != icon_name : return QtGui.QIcon(icon_name) if not icon_path: icon_path = os.path.join(os.path.abspath(os.environ['CCP4MG']),'qticons','actions') #print 'guiUtils.createIcon',icon_path,icon_name for ext in ICON_EXTENSIONS: file = os.path.join(icon_path,icon_name+ext) if os.path.exists(file): return QtGui.QIcon(file) if default_icon: file = os.path.join(os.path.abspath(os.environ['CCP4MG']),'qticons',default_icon+'.png') return QtGui.QIcon(file) else: return None #---------------------------------------------------------------------- def addMenuEntry(name='',definition={},parent=None,icon_path=''): #---------------------------------------------------------------------- action = createAction(name,parent,definition,icon_path) parent.addAction(action) if definition.has_key('slot') and definition.has_key('signal'): if type(definition['slot']) == tuple: # Hmm, I need to know how to have arbitrary number of arguments. parent.connect(action,QtCore.SIGNAL(definition['signal']),partial(definition['slot'][0],definition['slot'][1])) else: parent.connect(action,QtCore.SIGNAL(definition['signal']),definition['slot']) return action #---------------------------------------------------------------------- def clearMenu(menu=None): #---------------------------------------------------------------------- submenus = menu.findChildren(QtGui.QMenu) menu.clear() for sm in submenus: sm.clear() actions = [] #---------------------------------------------------------------------- def populateMenu(parent=None,menuWidget=None,definition=[], getActionDef=None,default_icon='',info={}): #---------------------------------------------------------------------- #print "populateMenu",definition #menuWidget.connect(menuWidget, QtCore.SIGNAL("hovered(QAction *)"),handleMenuHover) if not definition: menuWidget.addAction(createAction('dummy',parent,{ 'text' : ' -- '} ,default_icon=default_icon)) elif not isinstance(definition,types.ListType): parent.connect(menuWidget,QtCore.SIGNAL("aboutToShow()"),partial(definition,menuWidget)) else: for item in definition: a = None if item == 'sep': menuWidget.addSeparator() if isinstance(item,types.ListType) and len(item)>0: sub_menu=menuWidget.addMenu(item[0]) if len(item) == 1: # This is a sub without actions defined sub_menu.setEnabled(0) elif isinstance(item[1],types.StringType): populateMenu(parent=parent,menuWidget=sub_menu,definition = item[1:],getActionDef=getActionDef,default_icon=default_icon,info=info) elif isinstance(item[1],types.MethodType): parent.connect(sub_menu,QtCore.SIGNAL("aboutToShow()"),partial(item[1],(sub_menu,item[0]))) else: if getActionDef: adef = apply(getActionDef,[item],info) else: adef = apply(parent.getActionDef,[item],info) if adef: #print "createAction adef",adef a = parent.findChild(QtGui.QAction,item) #print "populateMenu find action",item,a if a: existing_action = a else: existing_action = None if not a: if adef.has_key("icon_path"): a = createAction(item,parent,adef,icon_path=adef["icon_path"],default_icon=default_icon) else: a = createAction(item,parent,adef,default_icon=default_icon) else: if adef.has_key("icon_path"): a = createAction(item,parent,adef,icon_path=adef["icon_path"],default_icon=default_icon,existing_action=existing_action) else: a = createAction(item,parent,adef,default_icon=default_icon,existing_action=existing_action) # The action exists but lets update the checked status if adef.has_key('text') and callable(adef["text"]): a.setText(adef['text']()) if adef.has_key('text') and not callable(adef["text"]): a.setText(adef['text']) if adef.has_key('checked') and callable(adef["checked"]): res = adef["checked"]() #print "populateMenu checked",item,res if type(res)==types.IntType or type(res)==types.BooleanType: a.setChecked(res) if adef.has_key('enabled'): #print 'reload adef enabled',item,adef["enabled"] if callable(adef["enabled"]): res = adef["enabled"]() else: res = adef["enabled"] #print 'reload adef enabled',item,res if type(res)==types.IntType or type(res)==types.BooleanType: a.setEnabled(res) if a: menuWidget.addAction(a) def handleMenuHover(action): tip = action.toolTip() if not tip: return QtGui.QToolTip.showText(QtGui.QCursor.pos(),tip) #------------------------------------------------------------------- def loadPixmap(name='',group='actions',icon_path='',width=32,height=0): #------------------------------------------------------------------- if not height: height = width size_key = str(width)+'_'+str(height) if not (LOADED_PIXMAPS.has_key(name) and LOADED_PIXMAPS[name].has_key(size_key)): if not icon_path: icon_path = os.path.join(os.path.abspath(os.environ['CCP4MG']),'qticons') file = '' for ext in ICON_EXTENSIONS: f = os.path.join(icon_path,group,name+ext) if os.path.exists(f): file = f break if not file: file = os.path.join(os.environ['CCP4MG'],'qticons','unknown.png') if not os.path.exists(file): return None if not LOADED_PIXMAPS.has_key(name): LOADED_PIXMAPS[name] = {} LOADED_PIXMAPS[name][size_key] = QtGui.QPixmap(file).scaled(width,height) return LOADED_PIXMAPS[name][size_key] #------------------------------------------------------------------- def loadResource(self,url='',mode=QtGui.QTextDocument.StyleSheetResource): #------------------------------------------------------------------- qu = QtCore.QUrl(url) self.style = self.textBrowser.loadResource(mode, qu)