def __del__(self):
     print 'AttributesPanel.__del__'
     QGridTable.__del__(self)
 def setupUi(self,USE_SCROLL=False,SHOW_OPTIONS=True,USE_TREND=False):
     self.setWindowTitle('Tango Finder : Search Attributes and Archiving')
     self.setLayout(Qt.QVBoxLayout())
     self.setMinimumWidth(950)#550)
     #self.setMinimumHeight(700)
     self.layout().setAlignment(Qt.Qt.AlignTop)
     self.chooser = Qt.QFrame()
     self.chooser.setLayout(Qt.QHBoxLayout())
     self.combo = Qt.QComboBox() #(self)
     #self.chooser.layout().addWidget(Qt.QLabel('Choose a domain to see temperatures status:'))
     #self.chooser.layout().addWidget(self.combo)
     #self.layout().addWidget(self.chooser)
     if True:
         self.searchbar = Qt.QFrame()
         self.searchbar.setLayout(Qt.QGridLayout()) 
         self.label = Qt.QLabel('Type a part of device name and a part of attribute name, use "*" or " " as wildcards:')
         #self.search = Qt.QLineEdit()
         self.ServerFilter = Qt.QLineEdit()
         self.ServerFilter.setMaximumWidth(250)
         self.DeviceFilter = fandango.qt.Dropable(Qt.QLineEdit)()
         self.DeviceFilter.setSupportedMimeTypes(fandango.qt.TAURUS_DEV_MIME_TYPE)
         self.AttributeFilter = fandango.qt.Dropable(Qt.QLineEdit)()
         self.AttributeFilter.setSupportedMimeTypes([fandango.qt.TAURUS_ATTR_MIME_TYPE,fandango.qt.TEXT_MIME_TYPE])
         self.update = Qt.QPushButton('Update')
         self.archivecheck = Qt.QCheckBox("Show archived attributes only")
         self.archivecheck.setChecked(False)
         self.layout().addWidget(self.label)
         [self.searchbar.layout().addWidget(o,x,y,h,w) for o,x,y,h,w in (
             (Qt.QLabel("Device or Alias:"),0,0,1,1),(self.DeviceFilter,0,1,1,4),
             (Qt.QLabel("Attribute:"),0,5,1,1),(self.AttributeFilter,0,6,1,4),
             (self.update,0,10,1,1),(self.archivecheck,0,11,1,2),
             )]
         self.searchbar.layout().addWidget(Qt.QLabel('Enter Device and Attribute filters using wildcards (e.g. li/ct/plc[0-9]+ / ^stat*$ & !status ) and push Update'),1,0,4,13)
         if SHOW_OPTIONS:
             self.options = Qt.QWidget() #self.searchbar
             self.options.setLayout(Qt.QGridLayout())
             separator = lambda x:Qt.QLabel(' '*x)
             row = 1
             [self.options.layout().addWidget(o,x,y,h,w) for o,x,y,h,w in (
                 #separator(120),Qt.QLabel("Options: "),separator(5),
                 (Qt.QLabel("Server: "),row,0,1,1),(self.ServerFilter,row,1,1,4),(Qt.QLabel(''),row,2,1,11)
                 )]
             #self.panel = generate_table(load_all_thermocouples('SR14')[-1])
             self.optiontab = Qt.QTabWidget()
             self.optiontab.addTab(self.searchbar,'Filters')
             self.optiontab.addTab(self.options,'Options')
             self.optiontab.setMaximumHeight(100)
             self.optiontab.setTabPosition(self.optiontab.North)
             self.layout().addWidget(self.optiontab)
         else: self.layout().addWidget(self.searchbar)
         
     self.toppan = Qt.QWidget(self)
     self.toppan.setLayout(Qt.QVBoxLayout())
     if True:
         self.header = QGridTable(self.toppan)
         self.header.setHorizontalHeaderLabels('Label/Value Device Attribute Alias Archiving'.split())
         self.header.setColumnWidth(0,350)
         self.toppan.layout().addWidget(self.header)
     
     if USE_SCROLL:
         print '*'*30 + ' USE_SCROLL=True '+'*'*30
         self._scroll = MyScrollArea(self.toppan)#Qt.QScrollArea(self)
         self._background = AttributesPanel(self._scroll) #At least a panel should be kept (never deleted) in background to not crash the worker!
         self.panel = None
         self._scroll.setChildrenPanel(self.panel)
         self._scroll.setWidget(self.panel)
         self._scroll.setMaximumHeight(700)
         self.toppan.layout().addWidget(self._scroll)
     else:
         self.panel = AttributesPanel(self.toppan)
         self.toppan.layout().addWidget(self.panel)
         
     self.toppan.layout().addWidget(Qt.QLabel('Drag any attribute from the first column into the trend or any taurus widget you want:'))
     
     if USE_TREND:
         self.split = Qt.QSplitter(Qt.Qt.Vertical)
         self.split.setHandleWidth(10)
         self.split.addWidget(self.toppan)
         
         from taurus.qt.qtgui.plot import TaurusTrend
         from PyTangoArchiving.widget.trend import ArchivingTrend,ArchivingTrendWidget
         self.trend = ArchivingTrendWidget() #TaurusArchivingTrend()
         self.trend.setUseArchiving(True)
         self.trend.showLegend(True)
         self.split.addWidget(self.trend)
         self.layout().addWidget(self.split)
     else:
         self.layout().addWidget(self.toppan)
     type(self)._persistent_ = self
 def __del__(self):
     print 'AttributesPanel.__del__'
     QGridTable.__del__(self)
class ArchivingBrowser(Qt.QWidget):
    _persistent_ = None #It prevents the instances to be destroyed if not called explicitly
    MAX_DEVICES = 500
    MAX_ATTRIBUTES = 1500
    
    def __init__(self,parent=None,domains=None,regexp='*pnv-*',USE_SCROLL=True,USE_TREND=False):
        Qt.QWidget.__init__(self,parent)
        self.setupUi(USE_SCROLL=USE_SCROLL,USE_TREND=USE_TREND)
        self.load_all_devices()
        try:
            import PyTangoArchiving
            self.reader = PyTangoArchiving.Reader()
            #self.hreader = PyTangoArchiving.Reader('hdb')
            #self.treader = PyTangoArchiving.Reader('tdb')
            #self.archattrs = sorted(set(map(str.lower,(a for l in (self.hreader.get_attributes(),self.hreader.alias.keys(),self.treader.get_attributes(active=True)) for a in l))))
            self.archattrs = sorted(set(self.reader.get_attributes()))
            self.archdevs = list(set(a.rsplit('/',1)[0] for a in self.archattrs))
        except:
            traceback.print_exc()
        self.extras = []
        self.domains = domains if domains else ['MAX','ANY','LI/LT','BO/BT']+['SR%02d'%i for i in range(1,17)]+['FE%02d'%i for i in (1,2,4,9,11,13,22,24,29,34)]
        self.combo.addItems((['Choose...']+self.domains) if len(self.domains)>1 else self.domains)
        self.connectSignals()
        
    def load_all_devices(self,filters='*'):
        import fandango
        self.tango = fandango.get_database()
        self.alias_devs = fandango.defaultdict_fromkey(
                lambda k,s=self: str(s.tango.get_device_alias(k)))
        self.archattrs = []
        self.archdevs = []
        #print('In load_all_devices(%s)...'%str(filters))
        devs = fandango.tango.get_all_devices()
        if filters!='*': 
            devs = [d for d in devs if fandango.matchCl(
                        filters.replace(' ','*'),d,extend=True)]
        self.all_devices = devs
        self.all_domains = sorted(set(a.split('/')[0] for a in devs))
        self.all_families = sorted(set(a.split('/')[1] for a in devs))

        members = []
        for a in devs:
            try:
                members.append(a.split('/')[2])
            except:
                # Wrong names in DB? yes, they are
                pass #print '%s is an invalid name!'%a
        members = sorted(set(members))
        
        self.all_members = sorted(set(e for m in members 
                for e in re.split('[-_0-9]',m) 
                if not fandango.matchCl('^[0-9]+([ABCDE][0-9]+)?$',e)))

        #print 'Loading alias list ...'
        self.all_alias = self.tango.get_device_alias_list('*')
        #self.alias_devs =  dict((str(self.tango.get_device_alias(a)).lower(),a) for a in self.all_alias)
        #print('Loading (%s) finished.'%(filters))
        
    def load_attributes(self,servfilter,devfilter,attrfilter,warn=True,exclude = ('dserver','tango*admin','sys*database','tmp','archiving')):
        #print 'In load_attributes(%s,%s,%s)'%(servfilter,devfilter,attrfilter)
        servfilter,devfilter,attrfilter = servfilter.replace(' ','*').strip(),devfilter.replace(' ','*'),attrfilter.replace(' ','*')
        attrfilter = attrfilter or 'state'
        devfilter = devfilter or attrfilter
        archive = self.archivecheck.isChecked()
        all_devs = self.all_devices if not archive else self.archdevs
        all_devs = [d for d in all_devs if not any(d.startswith(e) for e in exclude) or any(d.startswith(e) and fun.matchCl(e,devfilter) for e in exclude)]
        if servfilter.strip('.*'):
            sdevs = map(str.lower,fandango.Astor(servfilter).get_all_devices())
            all_devs = [d for d in all_devs if d in sdevs]
        #print('In load_attributes(%s,%s,%s): Searching through %d %s names'
              #%(servfilter,devfilter,attrfilter,len(all_devs),
                #'server' if servfilter else 'device'))
        if devfilter.strip().strip('.*'):
            devs = [d for d in all_devs if (fandango.searchCl(devfilter,d,extend=True))]
            print('\tFound %d devs, Checking alias ...'%(len(devs)))
            alias,alias_devs = [],[]
            if '&' in devfilter:
                alias = self.all_alias
            else:
                for df in devfilter.split('|'):
                    alias.extend(self.tango.get_device_alias_list('*%s*'%df.strip()))
            if alias: 
                print('\t%d alias found'%len(alias))
                alias_devs.extend(self.alias_devs[a] for a in alias if fun.searchCl(devfilter,a,extend=True))
                print('\t%d alias_devs found'%len(alias_devs))
                #if not self.alias_devs:
                    #self.alias_devs =  dict((str(self.tango.get_device_alias(a)).lower(),a) for a in self.all_alias)
                #devs.extend(d for d,a in self.alias_devs.items() if fandango.searchCl(devfilter,a) and (not servfilter or d in all_devs))
                devs.extend(d for d in alias_devs if not servfilter.strip('.*') or d in all_devs)
        else:
            devs = all_devs
            
        devs = sorted(set(devs))
        self.matching_devs = devs
        print('In load_attributes(%s,%s,%s): %d devices found'%(servfilter,devfilter,attrfilter,len(devs)))

        if False and not len(devs) and not archive:
            #Devices do not actually exist, but may exist in archiving ...
            #Option disabled, was mostly useless
            self.archivecheck.setChecked(True)
            return self.load_attributes(servfilter,devfilter,attrfilter,warn=False)
        
        if len(devs)>self.MAX_DEVICES and warn:
            Qt.QMessageBox.warning(self, "Warning" , "Your search (%s,%s) matches too many devices!!! (%d); please refine your search\n\n%s\n..."%(devfilter,attrfilter,len(devs),'\n'.join(devs[:30])))
            return {}
        elif warn and len(devs)>5:
            r = Qt.QMessageBox.warning(self, "Message" , "Your search (%s,%s) matches %d devices."%(devfilter,attrfilter,len(devs)),Qt.QMessageBox.Ok|Qt.QMessageBox.Cancel)
            if r==Qt.QMessageBox.Cancel:
                return {}
        
        self.matching_attributes = {} #{attribute: (device,alias,attribute,label)}
        failed_devs = []
        for d in sorted(devs):
            try:
                dp = taurus.Device(d)
                if not archive:
                    dp.ping()
                    tcs = [t for t in dp.get_attribute_list()]
                else:
                    tcs = [a.split('/')[-1] for a in self.archattrs if a.startswith(d+'/')]
                matches = [t for t in tcs if fandango.searchCl(attrfilter,t,extend=True)]
                for t in sorted(tcs):
                    if not self.archivecheck.isChecked() or not matches: label = dp.get_attribute_config(t).label
                    else: label = t
                    if t in matches or fandango.searchCl(attrfilter,label,extend=True):
                        if d in self.alias_devs: alias = self.alias_devs[d]
                        else:
                            try: alias = str(self.tango.get_alias(d))
                            except: alias = ''
                        self.matching_attributes['%s/%s'%(d,t)] = (d,alias,t,label)
                        if warn and len(self.matching_attributes)>self.MAX_ATTRIBUTES:
                            Qt.QMessageBox.warning(self, "Warning" , 
                                "Your search (%s,%s) matches too many attributes!!! (%d); please refine your search\n\n%s\n..."%(
                                devfilter,attrfilter,len(self.matching_attributes),'\n'.join(sorted(self.matching_attributes.keys())[:30])))
                            return {}
            except:
                print('load_attributes(%s,%s,%s => %s) failed!'%(servfilter,devfilter,attrfilter,d))
                failed_devs.append(d)
                if attrfilter in ('state','','*','**'):
                    self.matching_attributes[d+'/state'] = (d,d,'state',None) #A None label means device-not-readable
                    
        if warn and len(self.matching_attributes)>16:
            r = Qt.QMessageBox.warning(self, "Message" , "(%s) matches %d attributes."%(attrfilter,len(self.matching_attributes)),Qt.QMessageBox.Ok|Qt.QMessageBox.Cancel)
            if r==Qt.QMessageBox.Cancel:
                return {}
        if not len(self.matching_attributes):
            Qt.QMessageBox.warning(self, "Warning", "No matching attribute has been found in %s." % ('Archiving DB' if archive else 'Tango DB (try Archiving option)'))
        if failed_devs:
            print('\t%d failed devs!!!: %s'%(len(failed_devs),failed_devs))
            if warn:
                Qt.QMessageBox.warning(self, "Warning" , 
                    "%d devices were not running:\n"%len(failed_devs) +'\n'.join(failed_devs[:10]+(['...'] if len(failed_devs)>10 else []) ))
        
        #print('\t%d attributes found'%len(self.matching_attributes))
        return self.matching_attributes
        
    def setupUi(self,USE_SCROLL=False,SHOW_OPTIONS=True,USE_TREND=False):
        self.setWindowTitle('Tango Finder : Search Attributes and Archiving')
        self.setLayout(Qt.QVBoxLayout())
        self.setMinimumWidth(950)#550)
        #self.setMinimumHeight(700)
        self.layout().setAlignment(Qt.Qt.AlignTop)
        self.chooser = Qt.QFrame()
        self.chooser.setLayout(Qt.QHBoxLayout())
        self.combo = Qt.QComboBox() #(self)
        #self.chooser.layout().addWidget(Qt.QLabel('Choose a domain to see temperatures status:'))
        #self.chooser.layout().addWidget(self.combo)
        #self.layout().addWidget(self.chooser)
        if True:
            self.searchbar = Qt.QFrame()
            self.searchbar.setLayout(Qt.QGridLayout()) 
            self.label = Qt.QLabel('Type a part of device name and a part of attribute name, use "*" or " " as wildcards:')
            #self.search = Qt.QLineEdit()
            self.ServerFilter = Qt.QLineEdit()
            self.ServerFilter.setMaximumWidth(250)
            self.DeviceFilter = fandango.qt.Dropable(Qt.QLineEdit)()
            self.DeviceFilter.setSupportedMimeTypes(fandango.qt.TAURUS_DEV_MIME_TYPE)
            self.AttributeFilter = fandango.qt.Dropable(Qt.QLineEdit)()
            self.AttributeFilter.setSupportedMimeTypes([fandango.qt.TAURUS_ATTR_MIME_TYPE,fandango.qt.TEXT_MIME_TYPE])
            self.update = Qt.QPushButton('Update')
            self.archivecheck = Qt.QCheckBox("Show archived attributes only")
            self.archivecheck.setChecked(False)
            self.layout().addWidget(self.label)
            [self.searchbar.layout().addWidget(o,x,y,h,w) for o,x,y,h,w in (
                (Qt.QLabel("Device or Alias:"),0,0,1,1),(self.DeviceFilter,0,1,1,4),
                (Qt.QLabel("Attribute:"),0,5,1,1),(self.AttributeFilter,0,6,1,4),
                (self.update,0,10,1,1),(self.archivecheck,0,11,1,2),
                )]
            self.searchbar.layout().addWidget(Qt.QLabel('Enter Device and Attribute filters using wildcards (e.g. li/ct/plc[0-9]+ / ^stat*$ & !status ) and push Update'),1,0,4,13)
            if SHOW_OPTIONS:
                self.options = Qt.QWidget() #self.searchbar
                self.options.setLayout(Qt.QGridLayout())
                separator = lambda x:Qt.QLabel(' '*x)
                row = 1
                [self.options.layout().addWidget(o,x,y,h,w) for o,x,y,h,w in (
                    #separator(120),Qt.QLabel("Options: "),separator(5),
                    (Qt.QLabel("Server: "),row,0,1,1),(self.ServerFilter,row,1,1,4),(Qt.QLabel(''),row,2,1,11)
                    )]
                #self.panel = generate_table(load_all_thermocouples('SR14')[-1])
                self.optiontab = Qt.QTabWidget()
                self.optiontab.addTab(self.searchbar,'Filters')
                self.optiontab.addTab(self.options,'Options')
                self.optiontab.setMaximumHeight(100)
                self.optiontab.setTabPosition(self.optiontab.North)
                self.layout().addWidget(self.optiontab)
            else: self.layout().addWidget(self.searchbar)
            
        self.toppan = Qt.QWidget(self)
        self.toppan.setLayout(Qt.QVBoxLayout())
        if True:
            self.header = QGridTable(self.toppan)
            self.header.setHorizontalHeaderLabels('Label/Value Device Attribute Alias Archiving'.split())
            self.header.setColumnWidth(0,350)
            self.toppan.layout().addWidget(self.header)
        
        if USE_SCROLL:
            print '*'*30 + ' USE_SCROLL=True '+'*'*30
            self._scroll = MyScrollArea(self.toppan)#Qt.QScrollArea(self)
            self._background = AttributesPanel(self._scroll) #At least a panel should be kept (never deleted) in background to not crash the worker!
            self.panel = None
            self._scroll.setChildrenPanel(self.panel)
            self._scroll.setWidget(self.panel)
            self._scroll.setMaximumHeight(700)
            self.toppan.layout().addWidget(self._scroll)
        else:
            self.panel = AttributesPanel(self.toppan)
            self.toppan.layout().addWidget(self.panel)
            
        self.toppan.layout().addWidget(Qt.QLabel('Drag any attribute from the first column into the trend or any taurus widget you want:'))
        
        if USE_TREND:
            self.split = Qt.QSplitter(Qt.Qt.Vertical)
            self.split.setHandleWidth(10)
            self.split.addWidget(self.toppan)
            
            from taurus.qt.qtgui.plot import TaurusTrend
            from PyTangoArchiving.widget.trend import ArchivingTrend,ArchivingTrendWidget
            self.trend = ArchivingTrendWidget() #TaurusArchivingTrend()
            self.trend.setUseArchiving(True)
            self.trend.showLegend(True)
            self.split.addWidget(self.trend)
            self.layout().addWidget(self.split)
        else:
            self.layout().addWidget(self.toppan)
        type(self)._persistent_ = self
        
    def connectSignals(self):
        #self.combo.connect(self.combo, Qt.SIGNAL("currentIndexChanged (const QString&)"), self.comboIndexChanged)
        self.connect(self.combo, Qt.SIGNAL("currentIndexChanged (const QString&)"), self.comboIndexChanged)
        self.connect(self.update, Qt.SIGNAL("pressed ()"), self.updateSearch)
        if len(self.domains)==1: self.emit(Qt.SIGNAL("currentIndexChanged (const QString&)"),Qt.QString(self.domains[0]))
        
    def open_new_trend(self):
        from taurus.qt.qtgui.plot import TaurusTrend
        tt = TaurusTrend()
        tt.show()
        self.extras.append(tt)
        tt.setUseArchiving(True)
        tt.showLegend(True)
        return tt
        
    def closeEvent(self,evt):
        Qt.QWidget.closeEvent(self,evt)
        type(self)._persistent_ = None
    
    #def __del__(self):
        #print 'In ValvesChooser.del()'
        ##try: Qt.QWidget.__del__(self)
        ##except: pass
        #type(self)._persistent_ = None
        
    def comboIndexChanged(self,text=None):
        #print 'In comboIndexChanged(...)'
        pass
        
    def splitFilters(self,filters):
        if filters.count(',')>1: filters.replace(',',' ')
        if ',' in filters: filters = filters.split(',')
        elif ';' in filters: filters = filters.split(';')
        elif filters.count('/') in (1,3): filters = filters.rsplit('/',1)
        elif ' ' in filters: filters = filters.rsplit(' ',1)
        else: filters = [filters,'^state$'] #'*']
        return filters
        
    def setModel(self,model):
        model = str(model).strip()
        if model: self.updateSearch(model)
    
    def updateSearch(self,*filters):
        #Text argument applies only to device/attribute filter; not servers
        try:
            #print('In updateSearch(%s[%d])'%(filters,len(filters)))
            if len(filters)>2:
                filters = [' '.join(filters[:-1]),filters[-1]]
            if len(filters)==1: 
                filters = ['']+self.splitFilters(filters[0])
            elif len(filters)==2:
                filters = ['']+list(filters)
            elif len(filters)==3:
                filters = list(filters)
            else:
                filters = (self.ServerFilter,self.DeviceFilter,self.AttributeFilter)
                filters = [str(f.text()).strip() for f in filters]
            
            #Texts are rewritten to show format as it is really used
            self.ServerFilter.setText(filters[0])
            self.DeviceFilter.setText(filters[1])
            self.AttributeFilter.setText(filters[2])
            
            if not any(filters):
                Qt.QMessageBox.warning(self, "Warning" , "you must type a text to search")
                return
            if not any (f.strip('.*') for f in filters): #Empty or too wide filters not allowed
                Qt.QMessageBox.warning(self, "Warning" , "you must reduce your filtering!")
                return
            
            wildcard = '*' if not '.*' in str(filters) else '.*'
            for i,f in enumerate(filters):
                if not (f.startswith('*') or f.startswith('.')):
                    filters[i] = '^state$' if (i==2 and not f) else f #'%s%s%s'%(wildcard,f,wildcard)
                    
            if self.panel and filters==self.panel.filters:
                return
            else:
                old = self.panel
                if self.panel:
                  if hasattr(self,'_scroll'): self._scroll.setWidget(None)
                  self.panel.setParent(None)
                  self.panel = None
                if not self.panel: 
                    self.panel = AttributesPanel(self._scroll,devices=self.all_devices)
                else: self.panel.clear()
                if old: 
                  old.clear()
                  old.deleteLater() #Must be done after creating the new one!!
                table = [] #model,device,attribute,alias,archived,ok
                #ATTRIBUTES ARE FILTERED HERE!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                for k,v in self.load_attributes(*filters).items():
                    try: 
                        archived = self.reader.is_attribute_archived(k)
                    except Exception,e: 
                        print('Archiving not available!:\n %s'
                              %traceback.format_exc())
                        archived = []
                    #print(k,v,archived)
                    table.append((k,v[0],v[2],v[1],archived,v[3] is not None))
                self.panel.setValues(sorted(table))
                if hasattr(self,'_scroll'): 
                    self._scroll.setWidget(self.panel)
                    #self.panel.setParent(self._scroll) #IT DOESNT WORK
                    self._scroll.setChildrenPanel(self.panel)
        except Exception,e:
            #traceback.print_exc()
            Qt.QMessageBox.warning(self, "Warning" , "There's something wrong in your search (%s), please simplify the string"%traceback.format_exc())
        return