# Source code for taurus.qt.qtgui.panel.taurusdevicepanel

    #!/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/>.
    #
    # ###########################################################################

    """
    TaurusDevicePanel.py:
    """

    import re
    import traceback
    import click

    import taurus
    from taurus.external.qt import Qt

    from taurus import tauruscustomsettings
    from taurus.core.taurusbasetypes import TaurusElementType
    from taurus.core.taurusattribute import TaurusAttribute
    from taurus.core.taurusdevice import TaurusDevice
    from taurus.qt.qtgui.container import TaurusWidget
    from taurus.qt.qtgui.taurusgui import TaurusGui
    from taurus.qt.qtgui.display import TaurusLabel
    from taurus.qt.qtgui.panel.taurusform import TaurusForm
    from taurus.qt.qtgui.panel.taurusform import TaurusCommandsForm
    from taurus.qt.qtgui.util.ui import UILoadable
    from taurus.qt.qtgui.icon import getCachedPixmap
    import taurus.cli.common

    __docformat__ = "restructuredtext"

    # ###########################################################################
    # TaurusDevicePanel (from Vacca)

    # Variables that control TaurusDevicePanel shape

    STATUS_HEIGHT = 170
    SPLIT_SIZES = [15, 65, 20]
    IMAGE_SIZE = (200, 100)  # (width,height)

    # Helper methods

    def matchCl(m, k):  # TODO: Tango-centric
        return re.match(m.lower(), k.lower())

    def searchCl(m, k):  # TODO: Tango-centric
        if m.startswith("^") or m.startswith("(^") or "(?!^" in m:
            return matchCl(m, k)
        return re.search(m.lower(), k.lower())

    def get_regexp_dict(dct, key, default=None):  # TODO: Tango-centric
        for k, v in dct.items():  # Trying regular expression match
            if matchCl(k, key):
                return v
        for k, v in dct.items():  # If failed, trying if key is contained
            if k.lower() in key.lower():
                return v
        if default is not None:
            return default
        else:
            raise Exception("KeyNotFound:%s" % key)

    def get_eqtype(dev):  # TODO: Tango-centric
        """It extracts the eqtype from a device name like
        domain/family/eqtype-serial
        """
        try:
            eq = str(dev).split("/")[-1].split("-", 1)[0].upper()
        except Exception:
            eq = ""
        return eq

    def str_to_filter(seq):  # TODO: Tango-centric
        try:
            f = eval(seq)
        except Exception:
            f = seq
        if isinstance(f, str):
            return {".*": [f]}
        elif isinstance(f, list):
            return {".*": f}
        else:
            return f

    # Stacked palette

    def get_White_palette():
        palette = Qt.QPalette()

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Active, Qt.QPalette.Base, brush)

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Active, Qt.QPalette.Window, brush)

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Inactive, Qt.QPalette.Base, brush)

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Inactive, Qt.QPalette.Window, brush)

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Disabled, Qt.QPalette.Base, brush)

        brush = Qt.QBrush(Qt.QColor(255, 255, 255))
        brush.setStyle(Qt.Qt.SolidPattern)
        palette.setBrush(Qt.QPalette.Disabled, Qt.QPalette.Window, brush)
        return palette

    # TaurusDevicePanel class

    class TaurusDevicePanel(TaurusWidget):
        """
        TaurusDevPanel is a Taurus Application inspired in Jive and Atk Panel.

        It Provides a Device selector and a panel for displaying information from
        the selected device.
        """

        # TODO: Tango-centric (This whole class should be called TangoDevicePanel)
        # TODO: a scheme-agnostic base class should be implemented

        READ_ONLY = False
        # A dictionary like {device_regexp:[attribute_regexps]}
        _attribute_filter = {}
        # A dictionary like {device_regexp:[(command_regexp,default_args)]}
        _command_filter = {}
        _icon_map = {}  # A dictionary like {device_regexp:pixmap_url}

        @classmethod
        def setIconMap(klass, filters):
            """A dictionary like {device_regexp:pixmap_url}"""
            klass._icon_map = filters

        @classmethod
        def getIconMap(klass):
            return klass._icon_map

        @classmethod
        def setAttributeFilters(klass, filters):
            """
            It will set the attribute filters
            filters will be like: {device_regexp:[attribute_regexps]}
            example: {'.*/VGCT-.*': ['ChannelState','p[0-9]']}
            """
            klass._attribute_filter.update(filters)

        @classmethod
        def getAttributeFilters(klass):
            return klass._attribute_filter

        @classmethod
        def setCommandFilters(klass, filters):
            """
            It will set the command filters
            filters will be like: {device_regexp:[command_regexps]}
            example::

              {'.*/IPCT-.*': (
                  ('setmode',('SERIAL','LOCAL','STEP','FIXED','START','PROTECT')),
                  ('onhv1',()), ('offhv1',()), ('onhv2',()), ('offhv2',()),
                  ('sendcommand',())
                  ),
              }
            """
            klass._command_filter.update(filters)

        @classmethod
        def getCommandFilters(klass):
            return klass._command_filter

        ###########################################################################

        def __init__(self, parent=None, model=None, palette=None, bound=True):
            TaurusWidget.__init__(self, parent)
            if palette:
                self.setPalette(palette)
            self.setLayout(Qt.QGridLayout())
            self.bound = bound
            self._dups = []

            self.setWindowTitle("TaurusDevicePanel")
            self._label = Qt.QLabel()
            self._label.font().setBold(True)

            self._stateframe = TaurusWidget(self)
            self._stateframe.setLayout(Qt.QGridLayout())
            self._stateframe.layout().addWidget(
                Qt.QLabel("State"), 0, 0, Qt.Qt.AlignCenter
            )
            self._statelabel = TaurusLabel(self._stateframe)
            self._statelabel.setMinimumWidth(100)
            self._statelabel.setBgRole("rvalue")
            self._stateframe.layout().addWidget(
                self._statelabel, 0, 1, Qt.Qt.AlignCenter
            )

            self._statusframe = Qt.QScrollArea(self)
            self._status = TaurusLabel(self._statusframe)
            self._status.setBgRole("none")
            self._status.setAlignment(Qt.Qt.AlignLeft)
            self._status.setFixedHeight(2000)
            self._status.setFixedWidth(5000)
            # self._statusframe.setFixedHeight(STATUS_HEIGHT)
            self._statusframe.setHorizontalScrollBarPolicy(Qt.Qt.ScrollBarAlwaysOn)
            self._statusframe.setVerticalScrollBarPolicy(Qt.Qt.ScrollBarAlwaysOn)
            self._statusframe.setWidget(self._status)
            self._statusframe.setPalette(get_White_palette())

            self._attrsframe = Qt.QTabWidget(self)

            # Horizontal will not allow to show labels of attributes!
            self._splitter = Qt.QSplitter(Qt.Qt.Vertical, self)

            self._attrs, self._comms = None, None

            self.layout().addWidget(self._splitter, 0, 0)
            self._header = Qt.QFrame()
            self._header.setFixedHeight(int(1.1 * IMAGE_SIZE[1]))
            self._header.setLayout(Qt.QGridLayout())

            self._dup = Qt.QPushButton()
            qpixmap = Qt.QIcon("actions:window-new.svg")
            self._dup.setIcon(Qt.QIcon(qpixmap))
            self._dup.setIconSize(Qt.QSize(15, 15))
            self._dup.pressed.connect(self.duplicate)

            self._image = Qt.QLabel()

            self._header.layout().addWidget(
                self._image, 0, 0, 2, 1, Qt.Qt.AlignCenter
            )
            self._header.layout().addWidget(self._label, 0, 1, Qt.Qt.AlignLeft)
            self._header.layout().addWidget(
                self._stateframe, 1, 1, 1, 2, Qt.Qt.AlignLeft
            )
            self._header.layout().addWidget(self._dup, 0, 2, Qt.Qt.AlignRight)

            self._splitter.insertWidget(0, self._header)
            self._splitter.insertWidget(1, self._attrsframe)
            self._splitter.insertWidget(2, self._statusframe)
            self._splitter.setSizes(SPLIT_SIZES)
            [
                self._splitter.setStretchFactor(i, v)
                for i, v in enumerate(SPLIT_SIZES)
            ]
            self._splitter.setCollapsible(0, False)
            self._splitter.setCollapsible(1, False)

            if model:
                self.setModel(model)

        def loadConfigFile(self, ifile=None):
            self.debug("In TaurusDevicePanel.loadConfigFile(%s)" % ifile)
            if isinstance(ifile, str) and ifile.endswith(".py"):
                import importlib

                spec = importlib.util.spec_from_file_location("config_file", ifile)
                config_file = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(config_file)
                af, cf, im = [
                    getattr(config_file, x, None)
                    for x in ("AttributeFilters", "CommandFilters", "IconMap")
                ]
                if af is not None:
                    self.setAttributeFilters(af)
                if cf is not None:
                    self.setCommandFilters(cf)
                if im is not None:
                    self.setIconMap(im)
            else:
                TaurusWidget.loadConfigFile(self, ifile)
            self.debug("AttributeFilters are:\n%s" % self.getAttributeFilters())

        def duplicate(self):
            self._dups.append(TaurusDevicePanel(bound=False))
            self._dups[-1].setModel(self.getModel())
            self._dups[-1].show()

        @Qt.pyqtSlot("QString")
        def setModel(self, model, pixmap=None, **kwargs):
            model, modelclass, raw = str(model).strip(), "", model
            if model:
                model = model and model.split()[0] or ""
                scheme = taurus.Manager().getScheme(model)
                modelclass = taurus.Factory(scheme).findObjectClass(model)
            self.trace(
                "In TaurusDevicePanel.setModel(%s(%s),%s)"
                % (raw, modelclass, pixmap)
            )
            if model == self.getModel():
                return
            elif raw is None or not model or not modelclass:
                if self.getModel():
                    self.detach()
                return
            elif issubclass(modelclass, TaurusAttribute):
                # if model.lower().endswith('/state'):
                model = model.rsplit("/", 1)[0]  # TODO: Tango-centric
            elif not issubclass(modelclass, TaurusDevice):
                self.warning("TaurusDevicePanel accepts only Device models")
                return
            try:
                taurus.Device(model).ping()  # TODO: Tango-centric
                if self.getModel():
                    # Do not dettach previous model before pinging the new one
                    # (fail message will be shown at except: clause)
                    self.detach()
                TaurusWidget.setModel(self, model)
                model_obj = self.getModelObj()

                simple_name = model_obj.getSimpleName().upper()
                self.setWindowTitle(simple_name)
                model = self.getModel()
                self._label.setToolTip(model_obj.getFullName().upper())
                self._label.setText(simple_name)

                font = self._label.font()
                font.setPointSize(15)
                self._label.setFont(font)
                if pixmap is None and self.getIconMap():
                    for k, v in self.getIconMap().items():
                        if searchCl(k, model):
                            pixmap = v
                if pixmap is not None:
                    qpixmap = Qt.QPixmap(pixmap)
                    if qpixmap.height() > 0.9 * IMAGE_SIZE[1]:
                        qpixmap = qpixmap.scaledToHeight(0.9 * IMAGE_SIZE[1])
                    if qpixmap.width() > 0.9 * IMAGE_SIZE[0]:
                        qpixmap = qpixmap.scaledToWidth(0.9 * IMAGE_SIZE[0])
                else:
                    logo = getattr(
                        tauruscustomsettings,
                        "ORGANIZATION_LOGO",
                        "logos:taurus.png",
                    )
                    qpixmap = getCachedPixmap(logo)

                self._image.setPixmap(qpixmap)
                if hasattr(self, "_statelabel"):
                    self._statelabel.setModel(
                        model + "/state"
                    )  # TODO: Tango-centric
                self._status.setModel(model + "/status")  # TODO: Tango-centric
                try:
                    self._attrsframe.clear()
                    filters = get_regexp_dict(
                        TaurusDevicePanel._attribute_filter, model, [".*"]
                    )
                    if hasattr(filters, "keys"):
                        filters = list(filters.items())  # Dictionary!
                    if filters and isinstance(
                        filters[0], (list, tuple)
                    ):  # Mapping
                        self._attrs = []
                        for tab, attrs in filters:
                            self._attrs.append(
                                self.get_attrs_form(
                                    device=model, filters=attrs, parent=self
                                )
                            )
                            self._attrsframe.addTab(self._attrs[-1], tab)
                    else:
                        if self._attrs and isinstance(self._attrs, list):
                            self._attrs = self._attrs[0]
                        self._attrs = self.get_attrs_form(
                            device=model,
                            form=self._attrs,
                            filters=filters,
                            parent=self,
                        )
                        if self._attrs:
                            self._attrsframe.addTab(self._attrs, "Attributes")
                    if not TaurusDevicePanel.READ_ONLY:
                        self._comms = self.get_comms_form(model, self._comms, self)
                        if self._comms:
                            self._attrsframe.addTab(self._comms, "Commands")
                    if SPLIT_SIZES:
                        self._splitter.setSizes(SPLIT_SIZES)
                except Exception:
                    self.warning(traceback.format_exc())
                    qmsg = Qt.QMessageBox(
                        Qt.QMessageBox.Critical,
                        "%s Error" % model,
                        "%s not available" % model,
                        Qt.QMessageBox.Ok,
                        self,
                    )
                    qmsg.setDetailedText(traceback.format_exc())
                    qmsg.show()
            except Exception:
                self.warning(traceback.format_exc())
                qmsg = Qt.QMessageBox(
                    Qt.QMessageBox.Critical,
                    "%s Error" % model,
                    "%s not available" % model,
                    Qt.QMessageBox.Ok,
                    self,
                )
                qmsg.show()

            return

        def detach(self):
            self.trace("In TaurusDevicePanel(%s).detach()" % self.getModel())
            _detached = []
            # long imports to avoid comparison problems in the isinstance below
            import taurus.qt.qtgui.container
            import taurus.qt.qtgui.base

            def detach_recursive(obj):
                if obj in _detached:
                    return
                if isinstance(obj, taurus.qt.qtgui.container.TaurusBaseContainer):
                    for t in obj.taurusChildren():
                        detach_recursive(t)
                if obj is not self and isinstance(
                    obj, taurus.qt.qtgui.base.TaurusBaseWidget
                ):
                    try:
                        if getattr(obj, "model", None):
                            # self.debug('detaching %s from %s'%(obj,obj.model))
                            obj.setModel([] if isinstance(obj, TaurusForm) else "")
                    except Exception:
                        self.warning("detach of %s failed!" % obj)
                        self.warning(traceback.format_exc())
                _detached.append(obj)

            detach_recursive(self)
            try:
                self._label.setText("")
                self._label.setToolTip("")
                if hasattr(self, "_statelabel"):
                    self._statelabel.setModel("")
                self._status.setModel("")
                self._image.setPixmap(Qt.QPixmap())
            except Exception:
                self.warning(traceback.format_exc())

        def get_attrs_form(self, device, form=None, filters=None, parent=None):
            filters = filters or get_regexp_dict(
                TaurusDevicePanel._attribute_filter, device, [".*"]
            )
            self.trace(
                "In TaurusDevicePanel.get_attrs_form(%s,%s)" % (device, filters)
            )
            allattrs = sorted(
                str(a)
                for a in taurus.Device(device).get_attribute_list()
                if str(a).lower() not in ("state", "status")
            )
            attrs = []
            for a in filters:
                for t in allattrs:
                    if a and searchCl(a.strip(), t.strip()):
                        aname = "%s/%s" % (device, t)
                        if aname not in attrs:
                            attrs.append(aname)
            if attrs:
                # self.trace( 'Matching attributes are: %s' % str(attrs)[:100])
                if form is None:
                    form = TaurusForm(parent)
                elif hasattr(form, "setModel"):
                    form.setModel([])
                # Configuring the TauForm:
                form.setWithButtons(False)
                form.setWindowTitle(device)
                try:
                    form.setModel(attrs)
                except Exception:
                    self.warning(
                        "TaurusDevicePanel.ERROR: Unable to setModel "
                        + "for TaurusDevicePanel.attrs_form!!: %s",
                        traceback.format_exc(),
                    )
                return form
            else:
                return None

        def get_comms_form(self, device, form=None, parent=None):
            self.trace("In TaurusDevicePanel.get_comms_form(%s)" % device)
            params = get_regexp_dict(TaurusDevicePanel._command_filter, device, [])
            # If filters are defined only listed devices will show commands
            if TaurusDevicePanel._command_filter and not params:
                self.debug(
                    "TaurusDevicePanel.get_comms_form(%s): "
                    + "By default an unknown device type will display no commands",
                    device,
                )
                return None
            if not form:
                form = TaurusCommandsForm(parent)
            elif hasattr(form, "setModel"):
                form.setModel("")
            try:
                form.setModel(device)
                if params:
                    form.setSortKey(
                        lambda x, vals=[s[0].lower() for s in params]: (
                            vals.index(x.cmd_name.lower())
                            if str(x.cmd_name).lower() in vals
                            else 100
                        )
                    )
                    form.setViewFilters(
                        [
                            lambda c: str(c.cmd_name).lower()
                            not in ("state", "status")
                            and any(
                                searchCl(s[0], str(c.cmd_name)) for s in params
                            )
                        ]
                    )
                    form.setDefaultParameters(
                        dict(
                            (k, v)
                            for k, v in (
                                params
                                if not hasattr(params, "items")
                                else list(params.items())
                            )
                            if v
                        )
                    )
                for wid in form._cmdWidgets:
                    if not hasattr(wid, "getCommand") or not hasattr(
                        wid, "setDangerMessage"
                    ):
                        continue
                    if re.match(
                        ".*(on|off|init|open|close).*",
                        str(wid.getCommand().lower()),
                    ):
                        wid.setDangerMessage(
                            "This action may affect other systems!"
                        )
                # form._splitter.setStretchFactor(1,70)
                # form._splitter.setStretchFactor(0,30)
                form._splitter.setSizes([80, 20])
            except Exception:
                self.warning(
                    "Unable to setModel for TaurusDevicePanel.comms_form!!: %s"
                    % traceback.format_exc()
                )
            return form

    def filterNonExported(obj):
        # TODO: Tango-centric
        from taurus.core.tango.tangodatabase import TangoDevInfo

        if not isinstance(obj, TangoDevInfo) or obj.exported():
            return obj
        return None

    @UILoadable(with_ui="_ui")
    class TaurusDevPanel(TaurusGui):
        """
        TaurusDevPanel is a Taurus Application inspired in Jive and Atk Panel.

        It Provides a Device selector and several dockWidgets for interacting and
        displaying information from the selected device.
        """

        HELP_MENU_ENABLED = False

        def __init__(self, parent=None, designMode=False):
            TaurusGui.__init__(self, parent)
            self.loadUi()

            # setting up the device Tree.
            # @todo: This should be done in the ui file when the
            #        TaurusDatabaseTree Designer plugin is available
            import taurus.qt.qtgui.tree

            TaurusDbTreeWidget = taurus.qt.qtgui.tree.TaurusDbTreeWidget

            self.deviceTree = TaurusDbTreeWidget(
                perspective=TaurusElementType.Device
            )
            self.deviceTree.getQModel().setSelectables(
                [TaurusElementType.Member]
            )  # TODO: Tango-centric
            # self.deviceTree.insertFilter(filterNonExported)
            self.setCentralWidget(self.deviceTree)

            # register subwidgets for configuration purposes
            # self.registerConfigDelegate(self.taurusAttrForm)
            # self.registerConfigDelegate(self.deviceTree)
            self.registerConfigDelegate(self._ui.taurusCommandsForm)

            self.loadSettings()
            self.createActions()

            # self.addToolBar(self.basicTaurusToolbar())

            self.deviceTree.currentItemChanged.connect(self.onItemSelectionChanged)

            self.updatePerspectivesMenu()
            if not designMode:
                self.splashScreen().finish(self)

        def createActions(self):
            """create actions"""
            # View Menu
            self.showAttrAction = self.viewMenu.addAction(
                self._ui.attrDW.toggleViewAction()
            )
            self.showCommandsAction = self.viewMenu.addAction(
                self._ui.commandsDW.toggleViewAction()
            )

        def setTangoHost(self, host):
            """extended from :class:setTangoHost"""
            TaurusGui.setTangoHost(self, host)
            self.deviceTree.setModel(host)

            # self.deviceTree.insertFilter(filterNonExported)

        def onItemSelectionChanged(self, current, previous):
            itemData = current.itemData()

            from taurus.core.tango.tangodatabase import TangoDevInfo

            if isinstance(itemData, TangoDevInfo):  # TODO: Tango-centric
                self.onDeviceSelected(itemData)

        def onDeviceSelected(self, devinfo):
            devname = devinfo.name()
            msg = 'Connecting to "%s"...' % devname
            self.statusBar().showMessage(msg)
            # abort if the device is not exported
            if not devinfo.exported():  # TODO: Tango-centric
                msg = 'Connection to "%s" failed (not exported)' % devname
                self.statusBar().showMessage(msg)
                self.info(msg)
                Qt.QMessageBox.warning(self, "Device unreachable", msg)
                self.setModel("")
                return
            self.setDevice(devname)

        def setDevice(self, devname):
            """set the device to be shown by the commands and attr forms"""
            self.setModel(devname)
            self._ui.attrDW.setWindowTitle("Attributes - %s" % devname)
            self._ui.commandsDW.setWindowTitle("Commands - %s" % devname)

        def setModel(self, name, **kwargs):
            """Reimplemented to delegate model to the commands and attrs forms"""
            TaurusGui.setModel(self, name)
            self._ui.taurusAttrForm.setModel(name)
            self._ui.taurusCommandsForm.setModel(name)

        @classmethod
        def getQtDesignerPluginInfo(cls):
            ret = TaurusGui.getQtDesignerPluginInfo()
            ret["module"] = "taurus.qt.qtgui.panel"
            return ret

    # =========================================================================
    # Launchers
    # =========================================================================

    @click.command("device")
    @click.argument("dev", nargs=1, default=None, required=False)
    @click.option(
        "-f",
        "--filter",
        help=(
            "regexp to filter for attributes to show "
            + "(it can be passed more than once)"
        ),
        multiple=True,
    )
    @taurus.cli.common.config_file
    def device_cmd(dev, filter, config_file):
        """Show a Device Panel"""
        import sys
        from taurus.qt.qtgui.application import TaurusApplication

        app = TaurusApplication(cmd_line_parser=None, app_name="TaurusDevicePanel")

        w = TaurusDevicePanel()
        w.show()

        if dev is None:
            from taurus.qt.qtgui.panel import TaurusModelChooser

            models, ok = TaurusModelChooser.modelChooserDlg(
                w, selectables=[TaurusElementType.Member], singleModel=True
            )
            dev = models[0] if ok and models else None

        if config_file is not None:
            w.loadConfigFile(config_file)
        elif dev and filter:
            w.setAttributeFilters({dev: list(filter)})

        w.setModel(dev)

        sys.exit(app.exec_())

    @click.command("panel")
    @click.option(
        "--tango-host",
        "tango_host",
        default=None,
        help="Tango Host name (the system's default if not given)",
    )
    @click.option("-d", "--dev", default=None, help="pre-selected device")
    @click.option(
        "-t", "--trend", is_flag=True, help="Create a temporal trend widget"
    )
    def panel_cmd(tango_host, dev, trend):
        """
        Show a TaurusPanel (a Taurus application inspired in Jive and Atk Panel)
        """
        from taurus.qt.qtgui.application import TaurusApplication
        import sys

        app = TaurusApplication(cmd_line_parser=None, app_name="tauruspanel")

        w = TaurusDevPanel()

        if tango_host is None:
            from taurus import Factory

            tango_host = Factory("tango").getAuthority().getFullName()
        w.setTangoHost(tango_host)

        if dev is not None:
            w.setDevice(dev)

        if trend is True:
            # TODO: Allow to select TaurusTrend back-end
            try:
                from taurus_pyqtgraph import TaurusTrend

                w.info("Using tpg back-end")
            except Exception:
                TaurusTrend = None
                w.warning("TaurusTrend widget is not available")

            if TaurusTrend is not None:
                plot = TaurusTrend()
                w.createPanel(plot, "TaurusTrend", permanent=False)

        w.show()

        sys.exit(app.exec_())