Source code for taurus.qt.qtgui.table.taurusgrid

#!/usr/bin/env python

#############################################################################
##
## This file is part of Taurus
## 
## http://taurus-scada.org
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
## 
## Taurus is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## 
## Taurus 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.
## 
## You should have received a copy of the GNU Lesser General Public License
## along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################

"""
taurusgrid.py: module containing the Taurus Widget: TaurusGrid 
original idea and development by gcuni
integrated with taurus and regular expressions by srubio
alba, 2009
"""

__all__ = ["TaurusGrid"]

__docformat__ = 'restructuredtext'

import re
import operator
import traceback
import Queue
from functools import partial

from taurus.external.qt import Qt, QtGui, QtCore

import taurus
from taurus.qt.qtcore.util.emitter import modelSetter,TaurusEmitterThread,SingletonWorker,MethodModel
from taurus.core.taurusmanager import TaurusManager
from taurus.core.util.log import Logger
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.panel import TaurusValue
from taurus.qt.qtgui.display import TaurusValueLabel,TaurusStateLabel
from taurus.qt.qtgui.input import TaurusValueLineEdit

metachars = re.compile('([.][*])|([.][^*])|([$^+\-?{}\[\]|()])')

def re_search_low(regexp,target): return re.search(regexp.lower(),target.lower())
def re_match_low(regexp,target): return re.match(regexp.lower(),target.lower())

def get_all_models(expressions,limit=1000):
    '''
    All devices matching expressions must be obtained.
    For each device only the good attributes are read.
    
    It practically equals to fandango.get_matching_attributes; check which is better!
    Move this method to taurus.core.tango.search 
    '''
    #print( 'In TaurusGrid.get_all_models(%s:"%s") ...' % (type(expressions),expressions))
    if isinstance(expressions,str):
        #if any(re.match(s,expressions) for s in ('\{.*\}','\(.*\)','\[.*\]')):
            ##self.debug( 'evaluating expressions ....')
            #expressions = list(eval(expressions))
        #else:
            ##self.debug( 'expressions as string separated by commas ...')
        expressions = expressions.split(',')
            
    elif any(isinstance(expressions,klass) for klass in (QtCore.QStringList,list,tuple,dict)):
        #self.debug( 'expressions converted from list ...')
        expressions = list(str(e) for e in expressions)
        
    #self.debug( 'In TaurusGrid.get_all_models(%s:"%s") ...' % (type(expressions),expressions))
    taurus_db = taurus.core.taurusmanager.TaurusManager().getFactory()().getDatabase()
    #taurus_db = taurus.Database(os.environ['TANGO_HOST'])
    if 'SimulationDatabase' in str(type(taurus_db)):
        #self.trace( 'Using a simulated database ...')
        models = expressions
    else:
        all_devs = taurus_db.get_device_exported('*')
        models = []
        for exp in expressions:
            #self.trace( 'evaluating exp = "%s"' % exp)
            exp = str(exp)
            devs = []
            targets = []
            if exp.count('/')==3: device,attribute = exp.rsplit('/',1)
            else: device,attribute = exp,'State'
            
            if any(c in device for c in '.*[]()+?'):
                if '*' in device and '.*' not in device: device = device.replace('*','.*')
                devs = [s for s in all_devs if re_match_low(device,s)]
            else:
                devs = [device]
                
            #self.trace( 'TaurusGrid.get_all_models(): devices matched by %s / %s are %d:' % (device,attribute,len(devs)))
            #self.debug( '%s' % (devs))
            for dev in devs:
                if any(c in attribute for c in '.*[]()+?'):
                    if '*' in attribute and '.*' not in attribute: attribute = attribute.replace('*','.*')
                    try:
                        #taurus_dp = taurus.core.taurusdevice.TaurusDevice(dev)
                        taurus_dp = taurus.core.taurusmanager.TaurusManager().getFactory()().getDevice(dev)
                        #self.debug( "taurus_dp = %s"%taurus_dp.getFullName())
                        attrs = [att.name for att in taurus_dp.attribute_list_query() if re_match_low(attribute,att.name)]
                        targets.extend(dev+'/'+att for att in attrs)
                    except Exception,e: 
                        #self.warning( 'ERROR! TaurusGrid.get_all_models(): Unable to get attributes for device %s: %s' % (dev,str(e)))
                        pass
                else: targets.append(dev+'/'+attribute)
            #print 'TaurusGrid.get_all_models(): targets added by %s are: %s' % (exp,targets)
            models.extend(targets)
    models = models[:limit]
    #print( 'Out of TaurusGrid.get_all_models(...)')
    return models   
                    
def get_readwrite_models(expressions,limit=1000):
    '''
    All devices matching expressions must be obtained.
    For each device only the good attributes are read.
    '''
    #self.debug( 'In TaurusGrid.get_all_models(%s:"%s") ...' % (type(expressions),expressions))
    if isinstance(expressions,str):
        if any(re.match(s,expressions) for s in ('\{.*\}','\(.*\)','\[.*\]')):
            #self.trace( 'evaluating expressions ....')
            expressions = list(eval(expressions))
        else:
            #self.trace( 'expressions as string separated by commas ...')
            expressions = expressions.split(',')
            
    elif any(isinstance(expressions,klass) for klass in (QtCore.QStringList,list,tuple,dict)):
        expressions = list(str(e) for e in expressions)
        
    taurus_db = taurus.core.taurusmanager.TaurusManager().getFactory()().getDatabase()
    if 'SimulationDatabase' in str(type(taurus_db)):
      models = expressions
    else:
      all_devs = taurus_db.get_device_exported('*')
      models = []
      for exp in expressions:
          exp = str(exp)
          devs = []
          targets = []
          if exp.count('/')==3: device,attribute = exp.rsplit('/',1)
          else: device,attribute = exp,'State'
          
          if any(c in device for c in '.*[]()+?'):
              if '*' in device and '.*' not in device: device = device.replace('*','.*')
              devs = [s for s in all_devs if re_match_low(device,s)]
          else:
              devs = [device]
              
          for dev in devs:
              if any(c in attribute for c in '.*[]()+?'):
                  if '*' in attribute and '.*' not in attribute: attribute = attribute.replace('*','.*')
                  try: 
                      taurus_dp = taurus.core.taurusmanager.TaurusManager().getFactory()().getDevice(dev)
                      attrs = [att.name for att in taurus_dp.attribute_list_query() if re_match_low(attribute,att.name) and att.isReadOnly()]
                      targets.extend(dev+'/'+att for att in attrs)
                  except Exception,e: 
                    pass
              else: targets.append(dev+'/'+attribute)
          models.extend(targets)
    models = models[:limit]
    return models

[docs]class TaurusGrid(QtGui.QFrame, TaurusBaseWidget): """ TaurusGrid is a Taurus widget designed to represent a set of attributes distributed in columns and rows. The Model will be a list with attributes or device names (for devices the State attribute will be shown). Each setModel(*) execution will be able to modify the attribute list. An example of execution:<pre> /usr/bin/python taurusgrid.py "model=lt.*/VC.*/.*/((C*)|(P*)|(I*))" cols=IP,CCG,PNV rows=LT01,LT02 </pre> @author originally developed by gcuni, extended by srubio and sblanch @todo Future releases should allow a list of filters as argument @todo names/widgets should be accessible as a caselessdict dictionary (e.g. for adding custom context menus) @todo refactoring to have methods that add/remove new widgets one by one, not only the whole dictionary @todo _TAGS property should allow to change row/columns meaning and also add new Custom tags based on regexp """ #--------------------------------------------------------------------------- # Write your own code here to define the signals generated by this widget # __pyqtSignals__ = ("modelChanged(const QString &)","itemSelected(QString)") _TAGS = ['DOMAIN','FAMILY','HOST','LEVEL','CLASS','ATTRIBUTE','DEVICE'] def __init__(self, parent = None, designMode = False): name = self.__class__.__name__ self.call__init__wo_kw(QtGui.QFrame, parent) #self.call__init__(TaurusBaseWidget, name, parent, designMode=designMode) if isinstance(parent,TaurusBaseWidget): #It was needed to avoid exceptions in TaurusDesigner! self.call__init__(TaurusBaseWidget, name, parent, designMode=designMode) else: self.call__init__(TaurusBaseWidget, name, designMode=designMode) self.title = '' self.showLabels = True self.filter = '' self._modelNames = [] self.row_labels = [] self.column_labels = [] self._widgets_list = [] self._last_selected = None self._show_frames = True self._show_row_frame = True self._show_column_frame = True self._show_others = False self._show_attr_labels = True self._show_attr_units = True self.hideLabels=False self.defineStyle() self.modelsQueue = Queue.Queue() self.__modelsThread = None if not designMode: self.modelsThread @property def modelsThread(self): modelsThread = self.__modelsThread if modelsThread is None: modelsThread = SingletonWorker(parent=self, name='TaurusGrid', queue=self.modelsQueue, method=modelSetter, cursor=True) self.__modelsThread = modelsThread return modelsThread
[docs] def save(self,filename): import pickle d = { 'model':self.filter, 'row_labels':self.row_labels,'column_labels':self.column_labels, 'frames':self._show_row_frame or self._show_column_frame,'labels':self._show_attr_labels, 'units':self._show_attr_units,'others':self._show_others } f = open(filename,'w') pickle.dump(d,f) f.close()
[docs] def load(self,filename,delayed=False): self.trace('In TauGrid.load(%s,%s)'%(filename,delayed)) if not isinstance(filename,dict): manual = False import pickle f = open(filename) d = pickle.load(f) f.close() else: manual = True d = filename self.setRowLabels(d['row_labels']) self.setColumnLabels(d['column_labels']) self.showAttributeLabels(d.get('labels',True)) self.showAttributeUnits(d.get('units',True)) self.showOthers(d.get('others',True)) self.showRowFrame(d.get('frames',True)) if manual: self.showColumnFrame(d.get('frames',True)) self.setModel(d['model'],delayed=d.get('delayed',delayed)) return self._modelNames
[docs] def defineStyle(self): """ Defines the initial style for the widget """ #----------------------------------------------------------------------- # Write your own code here to set the initial style of your widget # self.setLayout(QtGui.QGridLayout()) #self.layout().setContentsMargins(0,0,0,0) self.updateStyle()
[docs] def sizeHint(self): return QtGui.QFrame.sizeHint(self)
[docs] def minimumSizeHint(self): return QtGui.QFrame.minimumSizeHint(self)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # TaurusBaseWidget over writing #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def getModelClass(self): #----------------------------------------------------------------------- # [MANDATORY] # Replace your own code here # ex.: return taurus.core.taurusattribute.Attribute raise RuntimeError("Forgot to overwrite %s.getModelClass" % str(self)) return list
[docs] def attach(self): """Attaches the widget to the model""" if self.isAttached(): return True #----------------------------------------------------------------------- # Write your own code here before attaching widget to attribute connect # the proper signal so that the first event is correctly received by the # widget # # Typical code is: #self.connect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'), # self.setTextValue) ret = TaurusBaseWidget.attach(self) # by default enable/disable widget according to attach state self.setEnabled(ret) return ret
[docs] def detach(self): """Detaches the widget from the model""" TaurusBaseWidget.detach(self) #----------------------------------------------------------------------- # Write your own code here after detaching the widget from the model # # Typical code is: #self.emit(QtCore.SIGNAL('valueChangedDueToEvent(QString)'), # QtCore.QString(value_str)) #self.disconnect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'), # self.setTextValue) # by default disable widget when dettached self.setEnabled(False)
#--------------------------------------------------------------------------- # [MANDATORY] # Uncomment the following method if your superclass does not provide with a # isReadOnly() method or if you need to change its behavior #def isReadOnly(self): # return True
[docs] def updateStyle(self): #----------------------------------------------------------------------- # Write your own code here to update your widget style self.trace('@'*80) self.trace('In TaurusGrid.updateStyle() ....... It seems never called!!!!') self.trace('@'*80) #It was showing an annoying "True" in the widget #value = self.getShowText() or '' #if self._setText: self._setText(value) ##It must be included #update tooltip self.setToolTip(self.getFormatedToolTip()) ##It must be included # send a repaint in the end if hasattr(self,'title_widget'): if self.title: self.title_widget.show() else: self.title_widget.hide() self.update()
#--------------------------------------------------------------------------- # Write your own code here for your own widget properties
[docs] def setModel(self,model,devsInRows=False,delayed=False,append=False,load=True): '''The model can be initialized as a list of devices or hosts or dictionary or ...''' #self.setModelCheck(model) ##It must be included #differenciate if the model is a RegExp if isinstance(model,dict): self.load(model) else: model = isinstance(model,(str,QtCore.QString)) and [model] or list(model) self.trace('#'*80) self.trace('In TaurusGrid.setModel(%s)'%str(model)[:100]) self.delayed = delayed self.filter = model if any('*' in m for m in model): model = get_all_models(model) self.debug('model was a RegExp, done the query and converted to an attr list') if not self._modelNames == []:#clean to start from scratch for widget in self._widgets_list: del widget #here we always have the reals model list, even if it comes from a regexp if append: self._modelNames = self._modelNames+model else: self._modelNames = model self.debug(('In TaurusGrid.setModel(...): modelNames are %s'%(self._modelNames))[:100]+'...') if load: self.trace('In TaurusGrid.setModel(%s,load=True): modelNames are %d'%(str(model)[:100]+'...',len(self._modelNames)))#,self._modelNames)) if devsInRows: self.setRowLabels(','.join(set(d.rsplit('/',1)[0] for d in self._modelNames))) self.create_widgets_table(self._modelNames) self.modelsQueue.put((MethodModel(self.showRowFrame),self._show_row_frame)) self.modelsQueue.put((MethodModel(self.showColumnFrame),self._show_column_frame)) self.modelsQueue.put((MethodModel(self.showOthers),self._show_others)) self.modelsQueue.put((MethodModel(self.showAttributeLabels),self._show_attr_labels)) self.modelsQueue.put((MethodModel(self.showAttributeUnits),self._show_attr_units)) self.updateStyle() if not self.delayed: self.trace('In setModel(): not delayed loading of models') if not self.modelsThread.isRunning(): #print 'In setModel(): Starting Thread! (%d objs in queue)'%(self.modelsThread.queue.qsize()) self.trace('<'*80) self.modelsThread.start()#self.modelsThread.IdlePriority) else: #print 'In setModel(): Thread already started! (%d objs in queue)'%(self.modelsThread.queue.qsize()) self.modelsThread.next() else: self.trace('In setModel(): models loading delayed!') pass self.trace('Out of TaurusGrid.setModel(%s)'%str(model)[:100]) self.updateStyle() return
[docs] def getModel(self): return self._modelNames
[docs] def resetModel(self): self._modelNames = [] self.updateFromList(self._modelNames) return
[docs] def setTitle(self,title): self.title = str(title) if hasattr(self,'title_widget'): if title: self.title_widget.setText(self.title) self.title_widget.show() else: self.title_widget.hide()
[docs] def parse_labels(self,text): if any(text.startswith(c[0]) and text.endswith(c[1]) for c in [('{','}'),('(',')'),('[',']')]): try: labels = eval(text) return labels except Exception,e: self.warning('ERROR! Unable to parse labels property: %s'%str(e)) return [] else: exprs = [t.strip() for t in text.split(',')] labels = [(':' in e and (e.split(':',1)[0].strip(),e.split(':',1)[-1].strip()) or (e,e)) for e in exprs] return labels
[docs] def setRowLabels(self,rows): '''The model can be initialized as a list of devices or hosts or ...''' #self.setModelCheck(model) ##It must be included self.row_labels = self.parse_labels(str(rows)) try: self.rows = [r[0] for r in self.row_labels] for i in range(len(self.rows)): section = self.rows[i] self.table.setVerticalHeaderItem(i,QtGui.QTableWidgetItem(section)) except Exception,e: self.debug("setRowLabels(): Exception! %s"%e)
#self.create_widgets_table(self._columnsNames)
[docs] def getRowLabels(self): return ','.join(':'.join(c) for c in self.row_labels)
[docs] def resetRowLabels(self): self.row_labels = [] return
[docs] def setColumnLabels(self,columns): '''The model can be initialized as a list of devices or hosts or ...''' #self.setModelCheck(model) ##It must be included self.column_labels = self.parse_labels(str(columns)) try: self.columns = [c[0] for c in self.column_labels] for i in range(len(self.columns)): equipment = self.columns[i] self.table.setHorizontalHeaderItem(i,QtGui.QTableWidgetItem(equipment)) except Exception,e: self.debug("setColumnLabels(): Exception! %s"%e)
#self.create_widgets_table(self._columnsNames)
[docs] def getColumnLabels(self): return ','.join(':'.join(c) for c in self.column_labels)
[docs] def resetColumnLabels(self): self.column_labels = [] return
#FIXME: when they are called before setModel they fails because # frames are not yet created, and it doesn't has memoty about this.
[docs] def showRowFrame(self,boolean): self._show_row_frame = boolean if hasattr(self,'rows_frame'): if boolean: self.rows_frame.show() else: self.rows_frame.hide()
[docs] def showColumnFrame(self,boolean): self._show_column_frame = boolean if hasattr(self,'columns_frame'): if boolean: self.columns_frame.show() else: self.columns_frame.hide()
[docs] def showAttributeLabels(self,boolean): self.trace('In showAttributeLabels(%s)'%boolean) self._show_attr_labels = boolean for tv in self._widgets_list: try: if tv and tv.labelWidget: if boolean: tv.labelWidget().show() else: tv.labelWidget().hide() except: pass return self._show_attr_labels
[docs] def showAttributeUnits(self,boolean): self.trace('In showAttributeUnits(%s)'%boolean) self._show_attr_units = boolean for tv in self._widgets_list: try: if tv and tv.unitsWidget: if boolean: tv.unitsWidget().show() else: tv.unitsWidget().hide() except: pass return self._show_attr_units
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # QT properties #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- model = QtCore.pyqtProperty("QStringList", getModel, setModel, resetModel) rowlabels = QtCore.pyqtProperty("QString", getRowLabels, setRowLabels, resetRowLabels) columnlabels = QtCore.pyqtProperty("QString", getColumnLabels, setColumnLabels, resetColumnLabels) useParentModel = QtCore.pyqtProperty("bool", TaurusBaseWidget.getUseParentModel, TaurusBaseWidget.setUseParentModel, TaurusBaseWidget.resetUseParentModel) #--------------------------------------------------------------------------- # Write your own code here for your own widget properties
[docs] def create_widgets_dict(self,models): from collections import defaultdict values = defaultdict(lambda: defaultdict(list)) #recursive dictionary with 2 levels #domains = list(set(m.split('/')[0].upper())) #families = list(set(m.split('/')[1].upper())) if not self.row_labels: #Domains used by default self.row_labels = sorted(list(set(m.split('/')[0].upper() for m in models if m.count('/')>=2))) self.row_labels = zip(self.row_labels,self.row_labels) if not self.column_labels: #Families used by default self.column_labels = sorted(list(set(m.split('/')[1].upper() for m in models if m.count('/')>=2))) self.column_labels = zip(self.column_labels,self.column_labels) #for m in models: #if m.count('/')<2: #self.warning('Wrong model cannot be added: %s'%m) #else: #domain,family = m.upper().split('/')[:2] #values[domain][family].append(m) row_not_found, col_not_found = False, False for m in models: row,column = 'Others','Others' for label,rexp in self.row_labels: if '*' in rexp and '.*' not in rexp: rexp = rexp.replace('*','.*') if re_search_low(rexp,m): row = label break for label,rexp in self.column_labels: if '*' in rexp and '.*' not in rexp: rexp = rexp.replace('*','.*') if re_search_low(rexp,m): column = label break if 'Others' == row: row_not_found = True if 'Others' == column: col_not_found = True self.debug('Model %s added to row %s , column %s' % (m,row,column)) values[row][column].append(m) if row_not_found: self.row_labels.append(('Others','.*')) if col_not_found: self.column_labels.append(('Others','.*')) return values
[docs] def create_frame_with_gridlayout(self): """ Just a 'macro' to create the layouts that seem to fit better. """ frame = QtGui.QFrame() frame.setLayout(QtGui.QGridLayout()) frame.layout().setContentsMargins(2,2,2,2) frame.layout().setSpacing(0) frame.layout().setSpacing(0) return frame
[docs] def create_widgets_table(self,models): ##Added a title to the panel self.title_widget = QtGui.QLabel() self.layout().addWidget(self.title_widget,0,0) self.setTitle(self.title) dct = self.create_widgets_dict(models) ##Assignments modified to keep the order of labels as inserted in properties self.rows = [r[0] for r in self.row_labels]#dct.keys() self.columns = [c[0] for c in self.column_labels]#list(set(reduce(operator.add,v.keys()) for v in dct.values())) values = [] for row in self.rows: line = [] for col in self.columns: #line.append(dct[row][col] if col in dct[row] else []) if col in dct[row]: line.append(dct[row][col]) else: line.append([]) values.append(line) ## Here is where the table is created! self.table = self.build_table(values) # SET COLUMN HEADERS (self.columns) for i in range(len(self.columns)): equipment = self.columns[i] self.table.setHorizontalHeaderItem(i,QtGui.QTableWidgetItem(equipment)) # SOMEDAY THIS WILL BE ICONS # SET ROW HEADERS (self.rows) for i in range(len(self.rows)): section = self.rows[i] self.table.setVerticalHeaderItem(i,QtGui.QTableWidgetItem(section)) # table.setAutoScroll(True) # resize_mode = QtGui.QHeaderView.Stretch # table.horizontalHeader().setResizeMode(resize_mode) #table.verticalHeader().setResizeMode(resize_mode) # for row in range(len(self.rows)): # table.setRowHeight(row,5+25*sum(len(dct[self.rows[row]][col]) for col in self.columns)) #for col in range(len(self.columns)): # table.setColumnWidth(col,300) use_scroll = False # It didn't work ... it doesn't allow the table widget to resize if use_scroll: scrollable = QtGui.QScrollArea(self) scrollable.setWidget(self.table) self.layout().addWidget(scrollable,1,0) else: self.layout().addWidget(self.table,1,0) #------------------------------------------------------------------------------------- # SECTION CHECKBOXES self.checkboxes_frame = self.create_frame_with_gridlayout() self.rows_frame = self.create_frame_with_gridlayout() self.rows_frame.setFrameStyle(QtGui.QFrame.Box) if not self._show_row_frame: self.rows_frame.hide() self.checkboxes_frame.layout().addWidget(self.rows_frame,0,0) self.columns_frame = self.create_frame_with_gridlayout() self.columns_frame.setFrameStyle(QtGui.QFrame.Box) if not self._show_column_frame: self.columns_frame.hide() self.checkboxes_frame.layout().addWidget(self.columns_frame,0,1) layout_row = 0 layout_col = 0 # For all rows, create rows of three checkboxes in order to # show or hide the corresponding rows in the table for i in range(len(self.rows)): section = self.rows[i] checkbox = QtGui.QCheckBox(section) if checkbox.text() == 'Others': checkbox.setChecked(False) if not self._show_others: checkbox.hide() else: checkbox.setChecked(True) self.rows_frame.layout().addWidget(checkbox,layout_row,layout_col) layout_col += 1 if layout_col == 3: layout_col = 0 layout_row += 1 QtCore.QObject.connect(checkbox,QtCore.SIGNAL('toggled(bool)'),self.show_hide_rows) self.show_hide_rows() layout_row = 0 layout_col = 0 # For all self.columns, create rows of three checkboxes in order to # show or hide the corresponding columns in the table for i in range(len(self.columns)): column = self.columns[i] checkbox = QtGui.QCheckBox(column) if checkbox.text() == 'Others': checkbox.setChecked(False) if not self._show_others: checkbox.hide() else: checkbox.setChecked(True) self.columns_frame.layout().addWidget(checkbox,layout_row,layout_col) layout_col += 1 if layout_col == 3: layout_col = 0 layout_row += 1 QtCore.QObject.connect(checkbox,QtCore.SIGNAL('toggled(bool)'),self.show_hide_columns) self.show_hide_columns() self.layout().addWidget(self.checkboxes_frame,2,0)
#self.resize(800,600)
[docs] def show_hide_rows(self): """ This needs refactoring to be together with the show_hide_columns method """ for checkbox in self.rows_frame.children(): if isinstance(checkbox,QtGui.QCheckBox): table_row = self.rows.index(checkbox.text()) if checkbox.isChecked(): self.table.showRow(table_row) else: self.table.hideRow(table_row)
[docs] def show_hide_columns(self): """ This needs refactoring to be together with the show_hide_rows method """ for checkbox in self.columns_frame.children(): if isinstance(checkbox,QtGui.QCheckBox): table_col = self.columns.index(checkbox.text()) if checkbox.isChecked(): self.table.showColumn(table_col) else: self.table.hideColumn(table_col)
[docs] def showOthers(self,boolean): self._show_others = boolean if hasattr(self,'rows_frame'): for checkbox in self.rows_frame.children(): if isinstance(checkbox,QtGui.QCheckBox) and checkbox.text() == 'Others': if self._show_others: checkbox.show() else: checkbox.hide() if hasattr(self,'columns_frame'): for checkbox in self.columns_frame.children(): if isinstance(checkbox,QtGui.QCheckBox) and checkbox.text() == 'Others': if self._show_others: checkbox.show() else: checkbox.hide()
[docs] def build_table(self,values): """ This is a builder. For all the elements in widgets matrix, just set the corresponding cells of the QTableWidget. """ self.trace('In TaurusGrid.build_table(%s)'%values) widgets_matrix = self.build_widgets(values,self.showLabels) rows = len(widgets_matrix) cols = rows and len(widgets_matrix[0]) or 0 table = QtGui.QTableWidget() table.setItemDelegate(Delegate(table)) # This example replaces the blue background of selected cells in tables palette = Qt.QPalette() palette.setBrush(palette.Active,palette.Highlight,Qt.QBrush(Qt.Qt.white)) table.setPalette(palette) table.setRowCount(rows) table.setColumnCount(cols) for row in range(len(widgets_matrix)): for col in range(len(widgets_matrix[row])): table.setCellWidget(row,col,widgets_matrix[row][col]) #table.resizeColumnsToContents() #table.resizeRowsToContents() table.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) #table.verticalHeader().setResizeMode(QtGui.QHeaderView.Stretch) #table.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) table.verticalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) return table
[docs] def build_widgets(self,values,show_labels=False,width=240,height=20,value_width=120): widgets_matrix = [] for row in values: widgets_row = [] for cell in row: cell_frame = self.create_frame_with_gridlayout() count = 0 for synoptic in sorted(cell): self.debug("processing synoptic %s"%synoptic) name = model = synoptic self.debug('Creating TaurusValue with model = %s'%model) synoptic_value = TaurusValue(cell_frame) self.modelsQueue.put((synoptic_value,model)) QtCore.QObject.connect(synoptic_value, QtCore.SIGNAL("itemClicked(QString)"), self.itemClicked) if self.hideLabels: synoptic_value.setLabelWidgetClass(None) else: synoptic_value.setLabelConfig('label') ## DO NOT DELETE THIS LINE!!! cell_frame.layout().addWidget(synoptic_value,count,0) self._widgets_list.append(synoptic_value) count += 1 #Done in this way as TauValue.mousePressEvent are never called def mousePressEvent(event,obj): #print 'In cell clicked' targets = set(str(child.getModelName()) for child in obj.children() if hasattr(child,'underMouse') and child.underMouse() and hasattr(child,'getModelName')) [obj.emit(Qt.SIGNAL("itemClicked(QString)"),t) for t in targets] cell_frame.mousePressEvent = partial(mousePressEvent,obj=cell_frame) QtCore.QObject.connect(cell_frame, QtCore.SIGNAL("itemClicked(QString)"), self.itemClicked) widgets_row.append(cell_frame) widgets_matrix.append(widgets_row) return widgets_matrix
[docs] def itemClicked(self,item_name): self.trace('In TaurusGrid.itemClicked(%s)'%item_name) self.setItemSelected(item_name) self.emit(QtCore.SIGNAL("itemClicked(QString)"),str(item_name))
[docs] def setItemSelected(self,item_name='',selected=True): """ it adds a blue frame around a clicked item. """ if isinstance(item_name,TaurusValue): self.trace('In TaurusGrid.setItemSelected(%s,%s)'%(str(item_name.getModel()),selected)) item = item_name else: self.trace('In TaurusGrid.setItemSelected(%s,%s)'%(str(item_name),selected)) if item_name: item = self.getItemByModel(item_name) else: item = self._last_selected if item: if selected: item._labelWidget.setStyleSheet('border-style: solid ; border-width: 1px; border-color: blue; color: blue; border-radius:4px;') if self._last_selected and self._last_selected!=item: self.setItemSelected(self._last_selected,False) self._last_selected = item else: item._labelWidget.setStyleSheet('border-style: solid; border-width: 1px; border-color: transparent; color: black; border-radius:4px;') self._last_selected = None else: return None
[docs] def getItemByModel(self, model, index=0): #a particular model can be many times, index means which one of them model = str(model).lower() for widget in self._widgets_list: if str(widget.getModel()).lower() == model: if index <= 0: return widget else: index -= 1
@classmethod
[docs] def getQtDesignerPluginInfo(cls): ret = TaurusBaseWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.table' ret['group'] = 'Taurus Views' ret['icon'] = ":/designer/grid.png" return ret
class Delegate(QtGui.QItemDelegate): def __init__(self, parent=None): QtGui.QItemDelegate.__init__(self,parent) def sizeHint(self, option, index): table = self.parent() widget = table.cellWidget(index.row(),index.column()) size = widget.sizeHint() return size def sysargs_to_dict(defaults=[]): import sys i,result = 0,{} for a in sys.argv[1:]: if '=' in a: bar = a.split('=') if bar[1] in ['True','False']:#convert string to boolean bar[1] = eval(bar[1]) result[bar[0].replace('--','')] = bar[1] else: result[defaults[i]] = a i+=1 return result if __name__ == '__main__': import sys if len(sys.argv)<2: print "The format of the call is something like:" print '\t/usr/bin/python taurusgrid.py grid.pickle.file' print '\t/usr/bin/python taurusgrid.py "model=lt.*/VC.*/.*/((C*)|(P*)|(I*))" cols=IP,CCG,PNV rows=LT01,LT02 others=False rowframe=True colframe=False' exit() app = QtGui.QApplication(sys.argv[0:1]) gui = TaurusGrid() try: #first try if argument is a file to be opened filename = sys.argv[1] open(filename,'r') gui.load(filename) except: args = sysargs_to_dict(['model','rows','cols','others','rowframe','colframe']) print "args = %s"%args if args.get('rows'): gui.setRowLabels(args['rows']) if args.get('cols'): gui.setColumnLabels(args['cols']) if args.get('model'): gui.setModel(args['model']) gui.showRowFrame('rowframe' in args and args['rowframe'] and True) gui.showColumnFrame('colframe' in args and args['colframe'] and True) gui.showOthers('others' in args and args['others'] or True) print "current TaurusGrid model= %s"%(gui.getModel()) gui.show() sys.exit(app.exec_())