# 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
    """

    # This module needs a total cleanup. Both re. code conventions and algorithms.
    #   --cpascual 20140827

    __docformat__ = "restructuredtext"

    import re
    from queue import Queue

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

    import taurus
    from taurus.qt.qtcore.util.emitter import (
        modelSetter,
        SingletonWorker,
        MethodModel,
    )
    from taurus.core.taurusmanager import TaurusManager
    from taurus.qt.qtgui.base import TaurusBaseWidget
    from taurus.qt.qtgui.panel import TaurusValue

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

    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
        """
        if isinstance(expressions, str):
            expressions = expressions.split(",")

        elif isinstance(expressions, (list, tuple, dict)):
            # self.debug( 'expressions converted from list ...')
            expressions = list(str(e) for e in expressions)

        taurus_db = taurus.Authority()
        # taurus_db = taurus.Authority(os.environ['TANGO_HOST'])
        # WHAAAAAAT????? Someone should get beaten for this line
        if "SimulationAuthority" 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]

                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 = (
                                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:
                            pass
                    else:
                        targets.append(dev + "/" + attribute)
                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.
        """
        if isinstance(expressions, str):
            if any(
                re.match(s, expressions) for s in (r"\{.*\}", r"\(.*\)", r"\[.*\]")
            ):
                # self.trace( 'evaluating expressions ....')
                expressions = list(eval(expressions))
            else:
                # self.trace( 'expressions as string separated by commas ...')
                expressions = expressions.split(",")

        elif isinstance(expressions, (list, tuple, dict)):
            expressions = list(str(e) for e in expressions)

        taurus_db = taurus.Authority()
        # WHAAAT???? At least check instances...
        if "SimulationAuthority" 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 = (
                                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:
                            pass
                    else:
                        targets.append(dev + "/" + attribute)
                models.extend(targets)
        models = models[:limit]
        return models

    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::

            python taurusgrid.py "model=lt.*/VC.*/.*/((C*)|(P*)|(I*))" \
                                  cols=IP,CCG,PNV rows=LT01,LT02
        """

        # 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
        #

        itemSelected = Qt.pyqtSignal("QString")
        itemClicked = Qt.pyqtSignal("QString")

        _TAGS = [
            "DOMAIN",
            "FAMILY",
            "HOST",
            "LEVEL",
            "CLASS",
            "ATTRIBUTE",
            "DEVICE",
        ]

        class _TaurusGridCell(Qt.QFrame):

            itemClicked = Qt.pyqtSignal("QString")

            # Done in this way as TaurusValue.mousePressEvent is never called
            def mousePressEvent(self, event):
                targets = set(
                    str(child.getModelName())
                    for child in self.children()
                    if hasattr(child, "underMouse")
                    and child.underMouse()
                    and hasattr(child, "getModelName")
                )
                for t in targets:
                    self.itemClicked.emit(t)

        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)
            # It was needed to avoid exceptions in TaurusDesigner!
            if isinstance(parent, TaurusBaseWidget):
                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()
            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

        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, "wb")
            pickle.dump(d, f)
            f.close()

        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, "rb")
                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

        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()

        def sizeHint(self):
            return QtGui.QFrame.sizeHint(self)

        def minimumSizeHint(self):
            return QtGui.QFrame.minimumSizeHint(self)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # TaurusBaseWidget over writing
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def getModelClass(self, **kwargs):
            # --------------------------------------------------------------
            # [MANDATORY]
            # Replace your own code here
            # ex.: return taurus.core.taurusattribute.Attribute
            raise RuntimeError("Forgot to overwrite %s.getModelClass" % str(self))
            return list

        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

            ret = TaurusBaseWidget.attach(self)

            # by default enable/disable widget according to attach state
            self.setEnabled(ret)
            return ret

        def detach(self):
            """Detaches the widget from the model"""

            TaurusBaseWidget.detach(self)

            # ---------------------------------------------------------------
            # Write your own code here after detaching the widget from the model

            # 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

        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

        def setModel(
            self,
            model,
            devsInRows=False,
            delayed=False,
            append=False,
            load=True,
            **kwargs
        ):
            """The model can be initialized as a list of devices or hosts or
            dictionary or ..."""

            # self._setModelCheck(model) # It must be included
            # differentiate if the model is a RegExp
            if isinstance(model, dict):
                self.load(model)
            else:
                model = isinstance(model, str) 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)

                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():
                            self.trace("<" * 80)
                            # self.modelsThread.IdlePriority)
                            self.modelsThread.start()
                        else:
                            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

        def getModel(self, **kwargs):
            return self._modelNames

        def resetModel(self, **kwargs):
            self._modelNames = []
            self.updateFromList(self._modelNames)
            return

        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()

        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 as 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

        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 as e:
                self.debug("setRowLabels(): Exception! %s" % e)

                # self.create_widgets_table(self._columnsNames)

        def getRowLabels(self):
            return ",".join(":".join(c) for c in self.row_labels)

        def resetRowLabels(self):
            self.row_labels = []
            return

        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 as e:
                self.debug("setColumnLabels(): Exception! %s" % e)

                # self.create_widgets_table(self._columnsNames)

        def getColumnLabels(self):
            return ",".join(":".join(c) for c in self.column_labels)

        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.

        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()

        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()

        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 Exception:
                    pass
            return self._show_attr_labels

        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 Exception:
                    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
        )

        #: (deprecated))
        useParentModel = QtCore.pyqtProperty(
            "bool",
            TaurusBaseWidget.getUseParentModel,
            TaurusBaseWidget.setUseParentModel,
            TaurusBaseWidget.resetUseParentModel,
        )

        # -------------------------------------------------------------------------
        # Write your own code here for your own widget properties

        def create_widgets_dict(self, models):
            from collections import defaultdict

            # recursive dictionary with 2 levels
            values = defaultdict(lambda: defaultdict(list))
            # 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 = list(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 = list(
                    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

        def create_frame_with_gridlayout(self):
            """Just a 'macro' to create the layouts that seem to fit better."""
            frame = TaurusGrid._TaurusGridCell()
            frame.setLayout(QtGui.QGridLayout())
            frame.layout().setContentsMargins(2, 2, 2, 2)
            frame.layout().setSpacing(0)
            frame.layout().setSpacing(0)
            return frame

        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()
            # list(set(reduce(operator.add,v.keys()) for v in dct.values()))
            self.columns = [c[0] for c in self.column_labels]

            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)
                )

            use_scroll = False
            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)

                # -------------------------------------------------------
                # Work around for https://bugs.kde.org/show_bug.cgi?id=345023
                # TODO: make better solution for this
                checkbox._id = section  # <-- ugly monkey-patch!
                # -------------------------------------------------------

                if section == "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
                checkbox.toggled.connect(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)

                # -------------------------------------------------------
                # Work around for https://bugs.kde.org/show_bug.cgi?id=345023
                # TODO: make better solution for this
                checkbox._id = column  # <-- ugly monkey-patch!
                # -------------------------------------------------------

                if column == "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
                checkbox.toggled.connect(self.show_hide_columns)
            self.show_hide_columns()

            self.layout().addWidget(self.checkboxes_frame, 2, 0)

            # self.resize(800,600)

        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):
                    # -------------------------------------------------------
                    # Work around for https://bugs.kde.org/show_bug.cgi?id=345023
                    # TODO: make better solution for this
                    # checkbox.text()  <-- fails due to added "&
                    table_row = self.rows.index(checkbox._id)
                    # -------------------------------------------------------
                    if checkbox.isChecked():
                        self.table.showRow(table_row)
                    else:
                        self.table.hideRow(table_row)

        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):
                    # -------------------------------------------------------
                    # Work around for https://bugs.kde.org/show_bug.cgi?id=345023
                    # TODO: make better solution for this
                    # checkbox.text()  <-- fails due to added "&
                    table_col = self.columns.index(checkbox._id)
                    # -------------------------------------------------------
                    if checkbox.isChecked():
                        self.table.showColumn(table_col)
                    else:
                        self.table.hideColumn(table_col)

        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)
                        # -------------------------------------------------------
                        # Workaround https://bugs.kde.org/show_bug.cgi?id=345023
                        # TODO: make better solution for this
                        # checkbox.text()  <-- fails due to added "&
                        and checkbox._id == "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)
                        # -------------------------------------------------------
                        # Workaround https://bugs.kde.org/show_bug.cgi?id=345023
                        # TODO: make better solution for this
                        # checkbox.text()  <-- fails due to added "&
                        and checkbox._id == "Others"
                    ):
                        # -------------------------------------------------------
                        if self._show_others:
                            checkbox.show()
                        else:
                            checkbox.hide()

        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()
            hh = table.horizontalHeader()
            if hh.length() > 0:
                hh.setSectionResizeMode(hh.Stretch)
            vh = table.verticalHeader()
            if vh.length() > 0:
                vh.setSectionResizeMode(vh.ResizeToContents)

            return table

        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()
                    cell_frame.itemClicked.connect(self.onItemClicked)
                    count = 0
                    for synoptic in sorted(cell):
                        self.debug("processing synoptic %s" % synoptic)
                        model = synoptic

                        self.debug("Creating TaurusValue with model =  %s" % model)
                        synoptic_value = TaurusValue(cell_frame)
                        self.modelsQueue.put((synoptic_value, model))

                        if self.hideLabels:
                            synoptic_value.setLabelWidgetClass(None)
                        else:
                            # DO NOT DELETE THIS LINE!!!
                            synoptic_value.setLabelConfig("label")
                        cell_frame.layout().addWidget(synoptic_value, count, 0)
                        self._widgets_list.append(synoptic_value)
                        count += 1

                    widgets_row.append(cell_frame)
                widgets_matrix.append(widgets_row)
            return widgets_matrix

        def onItemClicked(self, item_name):
            self.trace("In TaurusGrid.itemClicked(%s)" % item_name)
            self.setItemSelected(item_name)
            self.itemClicked.emit(str(item_name))

        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

        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
        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
        from taurus.qt.qtgui.application import TaurusApplication

        if len(sys.argv) < 2:
            print(
                """\
    The format of the call is something like:
    \t/usr/bin/python taurusgrid.py grid.pickle.file
    \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 = TaurusApplication(sys.argv[0:1], cmd_line_parser=None)
        gui = TaurusGrid()

        try:  # first try if argument is a file to be opened
            filename = sys.argv[1]
            open(filename, "r")
            gui.load(filename)
        except Exception:
            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_())