Beispiel #1
0
class TemplateManagementTab(QWidget):
    def __init__(self, obj_mng: ObjectManager, tpl_mng: TemplateManager,
                 sgn_mng: SignageManager, mtm_mng: MultimediaManager,
                 chn_mng: ChannelManager):
        super().__init__()

        self._obj_mng = obj_mng
        self._tpl_mng = tpl_mng
        self._sgn_mng = sgn_mng
        self._mtm_mng = mtm_mng
        self._chn_mng = chn_mng
        self._multimedia_list = QTreeWidget()

        self._res = ResourceManager()

        def template_change_handler(change_type: utils.ChangeType,
                                    tpl_text: str = ''):
            get_selected = self._multimedia_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.DELETE:
                    parent = item.parent()
                    parent.removeChild(item)

        self._template_widget = TemplateWidget(self._tpl_mng,
                                               template_change_handler)
        self.init_ui()

    def template_to_tree_item(self) -> [QTreeWidgetItem]:
        frame_label_item = QTreeWidgetItem([self._res['frameLabel']])
        for frame_tpl_id in self._tpl_mng.frame_templates.keys():
            frame_tpl = self._tpl_mng.get_frame_template(frame_tpl_id)
            frame_text = utils.gen_ui_text(frame_tpl.definition.name,
                                           frame_tpl.id)
            frame_item = QTreeWidgetItem([frame_text])
            frame_label_item.addChild(frame_item)

        scene_label_item = QTreeWidgetItem([self._res['sceneLabel']])
        for scene_tpl_id in self._tpl_mng.scene_templates.keys():
            scene_tpl = self._tpl_mng.get_scene_template(scene_tpl_id)
            scene_text = utils.gen_ui_text(scene_tpl.definition.name,
                                           scene_tpl.id)
            scene_item = QTreeWidgetItem([scene_text])
            scene_label_item.addChild(scene_item)
        return [frame_label_item, scene_label_item]

    def init_ui(self) -> None:
        # Left side of screen
        self._multimedia_list.setHeaderLabel(self._res['templateListLabel'])
        self._multimedia_list.addTopLevelItems(self.template_to_tree_item())
        self._multimedia_list.expandAll()
        self._multimedia_list.itemSelectionChanged.connect(
            self.list_item_clicked)

        # Gather altogether
        hbox_outmost = QHBoxLayout()
        hbox_outmost.addWidget(self._multimedia_list, 1)
        hbox_outmost.addWidget(self._template_widget, 4)
        self.setLayout(hbox_outmost)

    def list_item_clicked(self) -> None:
        get_selected = self.sender().selectedItems()
        if get_selected:
            item = get_selected[0]
            item_text = item.text(0)
            if item.parent() is None:
                # It is at topmost level
                # Selected one is Scene/Frame
                self._template_widget.clear_data_on_ui()
            else:
                # Selected one is frame/scene
                tpl_id = utils.ui_text_to_id(item_text)
                if item.parent().text(0) == self._res['frameLabel']:
                    # Selected one is frame template
                    tpl = self._tpl_mng.get_frame_template(tpl_id)
                else:
                    # Selected one is scene template
                    tpl = self._tpl_mng.get_scene_template(tpl_id)
                self._template_widget.load_data_on_ui(tpl)
Beispiel #2
0
class DataManagementTab(QWidget):
    def __init__(self, obj_mng: ObjectManager, tpl_mng: TemplateManager,
                 sgn_mng: SignageManager, mtm_mng: MultimediaManager,
                 chn_mng: ChannelManager):
        super().__init__()

        self._obj_mng = obj_mng
        self._tpl_mng = tpl_mng
        self._sgn_mng = sgn_mng
        self._mtm_mng = mtm_mng
        self._chn_mng = chn_mng

        self._data_list = QTreeWidget()

        self._res = ResourceManager()
        self._stacked_widget = QStackedWidget()
        self._widget_idx = dict()
        self.init_ui()

    def data_to_tree_item(self):
        data_type_items = []
        # For all data type
        for data_type_id in self._obj_mng.object_types.keys():
            data_type = self._obj_mng.get_object_type(data_type_id)
            data_type_text = utils.gen_ui_text(data_type.name, data_type.id)
            data_type_item = QTreeWidgetItem([data_type_text])
            # Add data
            for data_id in self._obj_mng.get_object_values(data_type).keys():
                data_item = QTreeWidgetItem([data_id])
                data_type_item.addChild(data_item)
            data_addition_item = QTreeWidgetItem(["+"])
            data_type_item.addChild(data_addition_item)
            data_type_items.append(data_type_item)
        return data_type_items

    def init_ui(self):
        # Left side of screen
        self._data_list.setHeaderLabel(self._res['dataTypeListLabel'])
        self._data_list.addTopLevelItems(self.data_to_tree_item())
        self._data_list.expandAll()
        self._data_list.itemSelectionChanged.connect(self.list_item_clicked)

        def data_type_change_handler(change_type: utils.ChangeType,
                                     data_text: str = '') -> None:
            # Get selected signage item
            get_selected = self._data_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.DELETE:
                    # Remove QTreeWidgetItem
                    self._data_list.removeItemWidget(item)

        data_type_widget = DataTypeWidget(self._obj_mng,
                                          data_type_change_handler)
        self._widget_idx['type'] = self._stacked_widget.addWidget(
            data_type_widget)

        def data_change_handler(change_type: utils.ChangeType,
                                data_text: str = '') -> None:
            # Get selected signage item
            get_selected = self._data_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.SAVE:
                    # Update QTreeWidgetItem
                    item.setText(0, data_text)
                elif change_type == utils.ChangeType.DELETE:
                    # Remove QTreeWidgetItem
                    parent = item.parent()
                    parent.removeChild(parent.child(parent.childCount() - 1))
                    parent.removeChild(item)
                    parent.addChild(QTreeWidgetItem(['+']))

        data_widget = DataWidget(self._obj_mng, self._mtm_mng,
                                 data_change_handler)
        self._widget_idx['data'] = self._stacked_widget.addWidget(data_widget)

        # Gather altogether
        hbox_outmost = QHBoxLayout()
        hbox_outmost.addWidget(self._data_list, 1)
        hbox_outmost.addWidget(self._stacked_widget, 4)
        self.setLayout(hbox_outmost)

    def list_item_clicked(self):
        get_selected = self.sender().selectedItems()
        if get_selected:
            item = get_selected[0]
            item_text = item.text(0)
            if item.parent() is None:
                # It is at topmost level
                # Selected one is data type
                idx = self._widget_idx['type']
                data_type_id = utils.ui_text_to_id(item_text)
                data_type = self._obj_mng.get_object_type(data_type_id)
                self._stacked_widget.widget(idx).load_data_on_ui(data_type)
                self._stacked_widget.setCurrentIndex(idx)
            else:
                if item_text == '+':
                    item.setSelected(False)
                    text, ok = QInputDialog.getText(
                        self, self._res['idDialogTitle'],
                        self._res['idDialogDescription'])
                    if ok:
                        try:
                            utils.validate_id(text)
                        except AttributeError:
                            QMessageBox.warning(
                                self, self._res['idInvalidTitle'],
                                self._res['idInvalidDescription'],
                                QMessageBox.Ok, QMessageBox.Ok)
                            return  # Invalid ID. Do not create signage
                        data_type_id = utils.ui_text_to_id(
                            item.parent().text(0))
                        data_type = self._obj_mng.get_object_type(data_type_id)
                        self._obj_mng.add_object_value(
                            ObjectValue(text, data_type, self._obj_mng,
                                        self._mtm_mng))
                        item.setText(0, text)
                        item.parent().addChild(QTreeWidgetItem(['+']))
                else:
                    # Selected one is data
                    idx = self._widget_idx['data']
                    data_type_id = utils.ui_text_to_id(item.parent().text(0))
                    data_type = self._obj_mng.get_object_type(data_type_id)
                    data = self._obj_mng.get_object_value(data_type, item_text)
                    self._stacked_widget.widget(idx).load_data_on_ui(data)
                    self._stacked_widget.setCurrentIndex(idx)
Beispiel #3
0
class visualizarImprimirExportar(QDialog):
    def __init__(self, parent=None):
        super(visualizarImprimirExportar, self).__init__()

        self.setWindowTitle("Просмотр, печать и экспорт данных в PDF")
        self.setWindowIcon(QIcon("Qt.png"))
        self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.MSWindowsFixedSizeDialogHint)
        self.setFixedSize(612, 408)

        self.initUI()

    def initUI(self):
        self.documento = QTextDocument()

        buttonBuscar = QPushButton("Найти пользователей", self)
        buttonBuscar.setFixedSize(426, 26)
        buttonBuscar.move(20, 20)

        buttonLimpiar = QPushButton("Очистить", self)
        buttonLimpiar.setFixedSize(140, 26)
        buttonLimpiar.move(452, 20)

        self.treeWidgetUsuarios = QTreeWidget(self)

        self.treeWidgetUsuarios.setFont(QFont(self.treeWidgetUsuarios.font().family(), 10, False))
        self.treeWidgetUsuarios.setRootIsDecorated(False)
        self.treeWidgetUsuarios.setHeaderLabels(("Id", "Имя", "Фамилия", "Пол", "Дата рождения", "Страна",
                                                 "Телефон"))

        self.model = self.treeWidgetUsuarios.model()

        for indice, ancho in enumerate((110, 150, 150, 160), start=0):
            self.model.setHeaderData(indice, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
            self.treeWidgetUsuarios.setColumnWidth(indice, ancho)

        self.treeWidgetUsuarios.setAlternatingRowColors(True)

        self.treeWidgetUsuarios.setFixedSize(572, 300)
        self.treeWidgetUsuarios.move(20, 56)

        buttonVistaPrevia = QPushButton("предварительный просмотр", self)
        buttonVistaPrevia.setFixedSize(180, 26)
        buttonVistaPrevia.move(116, 364)

        buttonImprimir = QPushButton("печать", self)
        buttonImprimir.setFixedSize(140, 26)
        buttonImprimir.move(304, 364)

        buttonExportarPDF = QPushButton("Экспорт в PDF", self)
        buttonExportarPDF.setFixedSize(140, 26)
        buttonExportarPDF.move(452, 364)

        buttonBuscar.clicked.connect(self.Buscar)
        buttonLimpiar.clicked.connect(self.limpiarTabla)

        buttonVistaPrevia.clicked.connect(self.vistaPrevia)
        buttonImprimir.clicked.connect(self.Imprimir)
        buttonExportarPDF.clicked.connect(self.exportarPDF)

    def Buscar(self):
        conexionDB = connect("DB_SIACLE/DB_SIACLE.db")
        cursor = conexionDB.cursor()

        cursor.execute("SELECT NOMBRE, APELLIDO, SEXO, FECHA_NACIMIENTO, PAIS, TELEFONO_CELULAR FROM CLIENTES ")
        datosDB = cursor.fetchall()

        conexionDB.close()

        if datosDB:
            self.documento.clear()
            self.treeWidgetUsuarios.clear()

            datos = ""
            item_widget = []
            for dato in datosDB:
                datos += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % dato
                item_widget.append(QTreeWidgetItem((str(dato[0]), dato[1], dato[2], dato[3], dato[4], dato[5])))

            reporteHtml = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
h3 {
    font-family: Helvetica-Bold;
    text-align: center;
   }

table {
       font-family: arial, sans-serif;
       border-collapse: collapse;
       width: 100%;
      }

td {
    text-align: left;
    padding-top: 4px;
    padding-right: 6px;
    padding-bottom: 2px;
    padding-left: 6px;
   }

th {
    text-align: left;
    padding: 4px;
    background-color: black;
    color: white;
   }

tr:nth-child(even) {
                    background-color: #dddddd;
                   }
</style>
</head>
<body>

<h3>Клиенты<br/></h3>

<table align="left" width="100%" cellspacing="0">
  <tr>
    <th>Имя</th>
    <th>Фамилия</th>
    <th>Пол</th>
    <th>Дата рождения</th>
    <th>Страна</th>
    <th>Телефон</th>
  </tr>
  [DATOS]
</table>

</body>
</html>
""".replace("[DATOS]", datos)

            datos = QByteArray()
            datos.append(str(reporteHtml))
            codec = QTextCodec.codecForHtml(datos)
            unistr = codec.toUnicode(datos)

            if Qt.mightBeRichText(unistr):
                self.documento.setHtml(unistr)
            else:
                self.documento.setPlainText(unistr)

            self.treeWidgetUsuarios.addTopLevelItems(item_widget)
        else:
            QMessageBox.information(self, "Найти пользователей", "Результатов не найдено.      ",
                                    QMessageBox.Ok)

    def limpiarTabla(self):
        self.documento.clear()
        self.treeWidgetUsuarios.clear()

    def vistaPrevia(self):
        if not self.documento.isEmpty():
            impresion = QPrinter(QPrinter.HighResolution)

            vista = QPrintPreviewDialog(impresion, self)
            vista.setWindowTitle("предварительный просмотр")
            vista.setWindowFlags(Qt.Window)
            vista.resize(800, 600)

            exportarPDF = vista.findChildren(QToolBar)
            exportarPDF[0].addAction(QIcon("exportarPDF.png"), "Экспорт в PDF", self.exportarPDF)

            vista.paintRequested.connect(self.vistaPreviaImpresion)
            vista.exec_()
        else:
            QMessageBox.critical(self, "предварительный просмотр", "Нет данных для отображения.   ",
                                 QMessageBox.Ok)

    def vistaPreviaImpresion(self, impresion):
        self.documento.print_(impresion)

    def Imprimir(self):
        if not self.documento.isEmpty():
            impresion = QPrinter(QPrinter.HighResolution)

            dlg = QPrintDialog(impresion, self)
            dlg.setWindowTitle("Распечатать документ")

            if dlg.exec_() == QPrintDialog.Accepted:
                self.documento.print_(impresion)

            del dlg
        else:
            QMessageBox.critical(self, "печать", "Нет данных для печати.   ",
                                 QMessageBox.Ok)

    def exportarPDF(self):
        if not self.documento.isEmpty():
            nombreArchivo, _ = QFileDialog.getSaveFileName(self, "Экспорт в PDF", "Список пользователей ",
                                                           "PDF файлы  (*.doc);;All Files (*)",
                                                           options=QFileDialog.Options())

            if nombreArchivo:
                impresion = QPrinter(QPrinter.HighResolution)
                impresion.setOutputFormat(QPrinter.PdfFormat)
                impresion.setOutputFileName(nombreArchivo)
                self.documento.print_(impresion)

                QMessageBox.information(self, "Экспорт в PDF", "Данные успешно экспортированы.   ",
                                        QMessageBox.Ok)
        else:
            QMessageBox.critical(self, "Экспорт в PDF", "Нет данных для экспорта.   ",
                                 QMessageBox.Ok)
Beispiel #4
0
class visualizarImprimirExportar(QDialog):
    def __init__(self, parent=None):
        super(visualizarImprimirExportar, self).__init__()

        self.setWindowTitle(
            "Visualizar, imprimir y exportar datos a PDF con PyQt5")
        self.setWindowIcon(QIcon("Qt.png"))
        self.setWindowFlags(Qt.WindowCloseButtonHint
                            | Qt.MSWindowsFixedSizeDialogHint)
        self.setFixedSize(612, 408)

        self.initUI()

    def initUI(self):
        self.documento = QTextDocument()

        # =================== WIDGETS QPUSHBUTTON ==================

        buttonBuscar = QPushButton("Buscar usuarios", self)
        buttonBuscar.setFixedSize(426, 26)
        buttonBuscar.move(20, 20)

        buttonLimpiar = QPushButton("Limpiar tabla", self)
        buttonLimpiar.setFixedSize(140, 26)
        buttonLimpiar.move(452, 20)

        # =================== WIDGET QTREEWIDGET ===================

        self.treeWidgetUsuarios = QTreeWidget(self)

        self.treeWidgetUsuarios.setFont(
            QFont(self.treeWidgetUsuarios.font().family(), 10, False))
        self.treeWidgetUsuarios.setRootIsDecorated(False)
        self.treeWidgetUsuarios.setHeaderLabels(
            ("D.N.I", "NOMBRE", "APELLIDO", "CORREO"))

        self.model = self.treeWidgetUsuarios.model()

        for indice, ancho in enumerate((110, 150, 150, 160), start=0):
            self.model.setHeaderData(indice, Qt.Horizontal, Qt.AlignCenter,
                                     Qt.TextAlignmentRole)
            self.treeWidgetUsuarios.setColumnWidth(indice, ancho)

        self.treeWidgetUsuarios.setAlternatingRowColors(True)

        self.treeWidgetUsuarios.setFixedSize(572, 300)
        self.treeWidgetUsuarios.move(20, 56)

        # =================== WIDGETS QPUSHBUTTON ==================

        buttonVistaPrevia = QPushButton("Vista previa", self)
        buttonVistaPrevia.setFixedSize(140, 26)
        buttonVistaPrevia.move(156, 364)

        buttonImprimir = QPushButton("Imprimir", self)
        buttonImprimir.setFixedSize(140, 26)
        buttonImprimir.move(304, 364)

        buttonExportarPDF = QPushButton("Exportar a PDF", self)
        buttonExportarPDF.setFixedSize(140, 26)
        buttonExportarPDF.move(452, 364)

        # =================== EVENTOS QPUSHBUTTON ==================

        buttonBuscar.clicked.connect(self.Buscar)
        buttonLimpiar.clicked.connect(self.limpiarTabla)

        buttonVistaPrevia.clicked.connect(self.vistaPrevia)
        buttonImprimir.clicked.connect(self.Imprimir)
        buttonExportarPDF.clicked.connect(self.exportarPDF)

# ======================= FUNCIONES ============================

    def Buscar(self):
        conexionDB = connect("Data/db_favan.db")
        cursor = conexionDB.cursor()

        cursor.execute(
            "SELECT ID_CLIENTE, NOM_CLIENTE, APE_CLIENTE, COR_CLIENTE FROM CLIENTE"
        )
        datosDB = cursor.fetchall()

        conexionDB.close()

        if datosDB:
            self.documento.clear()
            self.treeWidgetUsuarios.clear()

            datos = ""
            item_widget = []
            for dato in datosDB:
                datos += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % dato
                item_widget.append(
                    QTreeWidgetItem((str(dato[0]), dato[1], dato[2], dato[3])))

            reporteHtml = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
h3 {
    font-family: Helvetica-Bold;
    text-align: center;
   }
table {
       font-family: arial, sans-serif;
       border-collapse: collapse;
       width: 100%;
      }
td {
    text-align: left;
    padding-top: 4px;
    padding-right: 6px;
    padding-bottom: 2px;
    padding-left: 6px;
   }
th {
    text-align: left;
    padding: 4px;
    background-color: purple;
    color: white;
   }
tr:nth-child(even) {
                    background-color: #dddddd;
                   }
</style>
</head>
<body>
<h3>LISTADO DE USUARIOS<br/></h3>
<table align="left" width="100%" cellspacing="0">
  <tr>
    <th>D.N.I</th>
    <th>NOMBRE</th>
    <th>APELLIDO</th>
    <th>FECHA DE NACIMIENTO</th>
  </tr>
  [DATOS]
</table>
</body>
</html>
""".replace("[DATOS]", datos)

            datos = QByteArray()
            datos.append(str(reporteHtml))
            codec = QTextCodec.codecForHtml(datos)
            unistr = codec.toUnicode(datos)

            if Qt.mightBeRichText(unistr):
                self.documento.setHtml(unistr)
            else:
                self.documento.setPlainText(unistr)

            self.treeWidgetUsuarios.addTopLevelItems(item_widget)
        else:
            QMessageBox.information(self, "Buscar usuarios",
                                    "No se encontraron resultados.      ",
                                    QMessageBox.Ok)

    def limpiarTabla(self):
        self.documento.clear()
        self.treeWidgetUsuarios.clear()

    def vistaPrevia(self):
        if not self.documento.isEmpty():
            impresion = QPrinter(QPrinter.HighResolution)

            vista = QPrintPreviewDialog(impresion, self)
            vista.setWindowTitle("Vista previa")
            vista.setWindowFlags(Qt.Window)
            vista.resize(800, 600)

            exportarPDF = vista.findChildren(QToolBar)
            exportarPDF[0].addAction(QIcon("exportarPDF.png"),
                                     "Exportar a PDF", self.exportarPDF)

            vista.paintRequested.connect(self.vistaPreviaImpresion)
            vista.exec_()
        else:
            QMessageBox.critical(self, "Vista previa",
                                 "No hay datos para visualizar.   ",
                                 QMessageBox.Ok)

    def vistaPreviaImpresion(self, impresion):
        self.documento.print_(impresion)

    def Imprimir(self):
        if not self.documento.isEmpty():
            impresion = QPrinter(QPrinter.HighResolution)

            dlg = QPrintDialog(impresion, self)
            dlg.setWindowTitle("Imprimir documento")

            if dlg.exec_() == QPrintDialog.Accepted:
                self.documento.print_(impresion)

            del dlg
        else:
            QMessageBox.critical(self, "Imprimir",
                                 "No hay datos para imprimir.   ",
                                 QMessageBox.Ok)

    def exportarPDF(self):
        if not self.documento.isEmpty():
            nombreArchivo, _ = QFileDialog.getSaveFileName(
                self,
                "Exportar a PDF",
                "Listado de usuarios",
                "Archivos PDF (*.pdf);;All Files (*)",
                options=QFileDialog.Options())

            if nombreArchivo:
                # if QFileInfo(nombreArchivo).suffix():
                #     nombreArchivo += ".pdf"

                impresion = QPrinter(QPrinter.HighResolution)
                impresion.setOutputFormat(QPrinter.PdfFormat)
                impresion.setOutputFileName(nombreArchivo)
                self.documento.print_(impresion)

                QMessageBox.information(self, "Exportar a PDF",
                                        "Datos exportados con éxito.   ",
                                        QMessageBox.Ok)
        else:
            QMessageBox.critical(self, "Exportar a PDF",
                                 "No hay datos para exportar.   ",
                                 QMessageBox.Ok)
Beispiel #5
0
class Window(QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        grid = QGridLayout()
        grid.addWidget(self.create_top_group(), 0, 0)
        grid.addLayout(self.create_mid_group(), 1, 0)
        grid.addWidget(self.create_bottom_group(), 2, 0)
        self.setLayout(grid)

        self.setWindowTitle("UJ Editor")
        self.resize(1280, 640)
        # self.create_actions()

    # def __wiggets_to_layout(self, layout, *widgets):
    #     for widget in widgets:
    #         layout.addWidget(widget)

    def __mix_to_layout(self, layout, *mix):
        for item in mix:
            if isinstance(item, QWidget):
                layout.addWidget(item)
            if isinstance(item, QLayout):
                layout.addLayout(item)

    def create_top_group(self):
        group_box = QGroupBox()
        self.import_button = QPushButton('&Import UJ')
        self.import_button.clicked.connect(self.import_uj)
        export_button = QPushButton('&Export UJ')
        export_button.clicked.connect(self.export_uj)
        self.uj_name = LabelLineEdit('UJ Name')
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        self.__mix_to_layout(hbox, self.import_button, export_button,
                             self.uj_name.layout)
        group_box.setLayout(hbox)
        group_box.setMaximumHeight(60)
        return group_box

    def create_mid_group(self):
        self.ddi_tree = QTreeWidget()
        self.ddi_tree.itemSelectionChanged.connect(self.show_ddi_details)

        ddi_details = QGroupBox()
        ddi_details_layout = QGridLayout()
        ddi_details_layout.setContentsMargins(0, 0, 0, 0)
        ddi_details_layout.addWidget(self.create_common_ddi_details(), 0, 0, 1,
                                     1)
        ddi_details_layout.addWidget(self.create_specific_ddi_details(), 1, 0,
                                     3, 1)
        ddi_details.setLayout(ddi_details_layout)

        self.step_tree = QTreeWidget()
        self.step_tree.itemSelectionChanged.connect(self.show_step_details)
        step_details = QGroupBox()
        step_details_layout = QGridLayout()
        step_details_layout.setContentsMargins(0, 0, 0, 0)
        step_details_layout.addWidget(self.create_common_step_details(), 0, 0,
                                      1, 1)
        # step_details_layout.addWidget(self.create_specific_step_details(), 1, 0, 3, 1)
        step_details.setLayout(step_details_layout)

        splitter = QSplitter(self)
        splitter.addWidget(self.ddi_tree)
        splitter.addWidget(ddi_details)
        splitter.addWidget(self.step_tree)
        splitter.addWidget(step_details)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        # self.__mix_to_layout(hbox, self.ddi_tree, ddi_details, self.step_tree, step_details)
        hbox.addWidget(splitter)
        # group_box.setLayout(hbox)
        return hbox

    def create_bottom_group(self):
        group_box = QGroupBox()
        group_box.setMaximumHeight(200)
        self.debug_edit = QPlainTextEdit()
        self.debug_edit.setMaximumHeight(160)
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        # hbox.setSizeConstraint(200)
        hbox.addWidget(self.debug_edit)
        group_box.setLayout(hbox)
        return group_box

    def create_common_ddi_details(self):
        group_box = QGroupBox()
        vbox = QVBoxLayout()
        self.ddi_description = LabelLineEdit('Description')
        type_name_layout = QHBoxLayout()
        type_name_layout.setContentsMargins(0, 0, 0, 0)
        self.ddi_name = LabelLineEdit('DDI Name')
        self.ddi_type = LabelComboBox('Type', DDI_TYPES)
        self.__mix_to_layout(type_name_layout, self.ddi_type.layout,
                             self.ddi_name.layout)
        self.ddi_sharing = LabelButtonGroup('State Sharing', {
            'SCRIPT  ': '&Single User',
            'THREAD  ': '&All Run Users'
        })
        self.ddi_refresh = LabelButtonGroup(
            'Refresh Condition', {
                'C': 'Every Cycle',
                'R': 'Once per Run',
                'T': 'Every Time',
                'U': 'Once per User'
            })
        vbox.setContentsMargins(0, 0, 0, 0)
        self.__mix_to_layout(vbox, type_name_layout,
                             self.ddi_description.layout,
                             self.ddi_sharing.layout, self.ddi_refresh.layout)
        group_box.setLayout(vbox)
        return group_box

    # ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
    def create_specific_ddi_details(self):
        group_box = QGroupBox()
        self.ddi_specific_layout = QVBoxLayout()
        self.ddi_specific_layout.setContentsMargins(0, 0, 0, 0)
        # self.value_layout = self.create_ddi_value()
        self.ddi_value_widget = LabelLineEdit('Value')
        self.ddi_selector_widget = LabelComboBox('Selector', SELECTOR_TYPES)
        # self.ddi_specific_layout.addWidget(self.ddi_value_widget)
        self.ddi_specific_layout.addLayout(self.ddi_value_widget.layout)
        self.ddi_specific_layout.addLayout(self.ddi_selector_widget.layout)

        self.ddi_date = DateFieldsGroup()
        self.ddi_specific_layout.addLayout(self.ddi_date.layout)

        self.ddi_delimiter_character_widget = LabelLineEdit('Delimiter:')
        self.ddi_delimiter_character_widget.line_edit.setMaxLength(1)
        self.ddi_delimited_filename_widget = LabelLineEdit('File Name:')
        self.ddi_delimited_file_picker_button = QPushButton('&Load Data File')
        self.ddi_delimited_file_picker_button.clicked.connect(
            self.load_data_file)
        delimited_layout = QHBoxLayout()
        delimited_layout.setContentsMargins(0, 0, 0, 0)
        delimited_layout.addLayout(self.ddi_delimiter_character_widget.layout,
                                   stretch=0)
        delimited_layout.addLayout(self.ddi_delimited_filename_widget.layout,
                                   stretch=2)
        delimited_layout.addWidget(self.ddi_delimited_file_picker_button,
                                   stretch=1)
        self.ddi_specific_layout.addLayout(delimited_layout)

        self.ddi_column_index_widget = LabelLineEdit('Column Index:')
        self.ddi_column_index_widget.line_edit.setInputMask('999')
        self.ddi_specific_layout.addLayout(self.ddi_column_index_widget.layout)

        self.ddi_list_table = MyTableWidget()
        self.ddi_specific_layout.addLayout(self.ddi_list_table.layout)

        self.ddi_related_ddi = LabelComboBox('Related to:')
        self.ddi_specific_layout.addLayout(self.ddi_related_ddi.layout)

        self.ddi_response_source_step = LabelComboBox('Source Step:')
        self.ddi_specific_layout.addLayout(
            self.ddi_response_source_step.layout)

        self.ddi_siphon_table = RowControlTableWidget([
            ('type', list(SIPHON_TYPES.values())), ('start', ''), ('end', ''),
            ('index', '')
        ])
        self.ddi_specific_layout.addLayout(self.ddi_siphon_table.layout)

        self.ddi_auto_correlate_type = LabelComboBox(
            'Field Type:', {z: z
                            for z in ['Repeated Fields', 'Known Fields']})
        self.ddi_specific_layout.addLayout(self.ddi_auto_correlate_type.layout)
        self.ddi_auto_correlate_name = LabelComboBox('Field Name:')
        self.ddi_specific_layout.addLayout(self.ddi_auto_correlate_name.layout)
        self.ddi_auto_correlate_appears_in = LabelCheckboxesGroup(
            'Appears In:', ['URL', 'Post', 'Headers'])
        self.ddi_specific_layout.addLayout(
            self.ddi_auto_correlate_appears_in.layout)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        self.ddi_auto_increment_starting_value = LabelLineEdit(
            'Starting Value:')
        hbox.addLayout(self.ddi_auto_increment_starting_value.layout)
        self.ddi_auto_increment_increment = LabelLineEdit('Increment:')
        hbox.addLayout(self.ddi_auto_increment_increment.layout)
        self.ddi_auto_increment_prefix = LabelLineEdit('Prefix:')
        hbox.addLayout(self.ddi_auto_increment_prefix.layout)
        self.ddi_auto_increment_suffix = LabelLineEdit('Suffix:')
        hbox.addLayout(self.ddi_auto_increment_suffix.layout)
        self.ddi_auto_increment_min_lenght = LabelLineEdit('Minimum Length:')
        hbox.addLayout(self.ddi_auto_increment_min_lenght.layout)
        self.ddi_specific_layout.addLayout(hbox)

        group_box.setLayout(self.ddi_specific_layout)
        return group_box

    # ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()

    def create_common_step_details(self):
        group_box = QGroupBox()
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setAlignment(Qt.AlignTop)
        name_sleep_layout = QHBoxLayout()
        name_sleep_layout.setContentsMargins(0, 0, 0, 0)
        self.step_name = LabelLineEdit('Name')
        name_sleep_layout.addLayout(self.step_name.layout)
        self.step_pre_time = LabelLineEdit('Sleep Time (sec)')
        name_sleep_layout.addLayout(self.step_pre_time.layout)
        vbox.addLayout(name_sleep_layout)
        self.step_description = LabelLineEdit('Description')
        vbox.addLayout(self.step_description.layout)
        self.step_checkboxes = LabelCheckboxesGroup('', [
            'Run First Cycle Only', 'Run Last Cycle Only',
            'Count as Transaction', 'Process Response', 'Execute Separately'
        ])
        vbox.addLayout(self.step_checkboxes.layout)
        request_type_content_type_layout = QHBoxLayout()
        self.request_type = LabelComboBox('Type',
                                          {z: z
                                           for z in STEP_REQUEST_TYPES})
        request_type_content_type_layout.addLayout(self.request_type.layout)
        self.content_type = LabelComboBox('Content-Type',
                                          {z: z
                                           for z in STEP_CONTENT_TYPES})
        request_type_content_type_layout.addLayout(self.content_type.layout)
        # self.content_type.combo_box.currentIndexChanged[str].connect(self.update_content_subtypes)
        # # what's the point of having type and subtypes? the one sent with headers is the subtype - it should be the only selection
        # self.content_subtype = LabelComboBox('', { z:z for z in STEP_CONTENT_TYPES['form'] })
        # request_type_content_type_layout.addLayout(self.content_subtype.layout)
        vbox.addLayout(request_type_content_type_layout)
        self.step_url = LabelLineEdit('URL')
        vbox.addLayout(self.step_url.layout)
        self.step_url_params_table = RowControlTableWidget([('Parameter', ''),
                                                            ('Value', '')])
        vbox.addLayout(self.step_url_params_table.layout)
        self.step_post_params_table = RowControlTableWidget([('Parameter', ''),
                                                             ('Value', '')])
        vbox.addLayout(self.step_post_params_table.layout)
        self.step_post_block = QPlainTextEdit()
        vbox.addWidget(self.step_post_block)
        success_validation_layout = QHBoxLayout()
        success_validation_layout.setContentsMargins(0, 0, 0, 0)
        self.step_validation_type = LabelComboBox('Success Validation', {
            'PLAIN': 'Plain Text',
            'REGEX': 'Regular Expression'
        })
        success_validation_layout.addLayout(self.step_validation_type.layout)
        self.step_validation_text = LabelLineEdit('')
        success_validation_layout.addLayout(self.step_validation_text.layout)
        self.max_response = LabelLineEdit('Max acceptable response time')
        success_validation_layout.addLayout(self.max_response.layout)
        vbox.addLayout(success_validation_layout)
        self.step_dynamic_content = LabelCheckboxesGroup(
            'Select content types to be handled automatically',
            ['images', 'css', 'javaScript'])
        vbox.addLayout(self.step_dynamic_content.layout)

        flow_control_line1_layout = QHBoxLayout()
        self.step_flow_control_type = LabelComboBox(
            'Flow Control', {
                '': 'GoTo',
                'RESPONSE': 'Response Based',
                'CONDTITIONAL': 'Conditional',
                'PERCENTAGE': 'Percentage Based',
                'DDLOOP': 'Dynamic Data Loop',
                'VARIABLELOOP': 'Variable Loop',
                'FIXEDLOOP': 'Fixed Loop'
            })
        flow_control_line1_layout.addLayout(self.step_flow_control_type.layout)
        self.step_flow_target_plus = LabelComboBox(
            'Step', {
                'NEXT_STEP': 'Next Step',
                'END_CYCLE': 'End Cycle',
                'END_USER': '******'
            })
        flow_control_line1_layout.addLayout(self.step_flow_target_plus.layout)
        self.step_flow_sleep = LabelLineEdit('Sleep Time (sec)')
        flow_control_line1_layout.addLayout(self.step_flow_sleep.layout)
        vbox.addLayout(flow_control_line1_layout)

        flow_control_line2_layout = QHBoxLayout()
        self.step_flow_ddl_ddi = LabelComboBox('Dynamic Data Item', {})
        flow_control_line2_layout.addLayout(self.step_flow_ddl_ddi.layout)
        self.step_flow_varloop_start = LabelLineEdit('Minimum Iterations')
        flow_control_line2_layout.addLayout(
            self.step_flow_varloop_start.layout)
        self.step_flow_varloop_end = LabelLineEdit('Maximum Iterations')
        flow_control_line2_layout.addLayout(self.step_flow_varloop_end.layout)
        self.step_flow_fixloop = LabelLineEdit('Iterations')
        flow_control_line2_layout.addLayout(self.step_flow_fixloop.layout)

        self.step_flow_conditional_true = LabelComboBox(
            'If true, go to', {
                'NEXT_STEP': 'Next Step',
                'END_CYCLE': 'End Cycle',
                'END_USER': '******'
            })
        flow_control_line2_layout.addLayout(
            self.step_flow_conditional_true.layout)
        self.step_flow_conditional_false = LabelComboBox(
            'otherwise, go to', {
                'NEXT_STEP': 'Next Step',
                'END_CYCLE': 'End Cycle',
                'END_USER': '******'
            })
        flow_control_line2_layout.addLayout(
            self.step_flow_conditional_false.layout)
        vbox.addLayout(flow_control_line2_layout)

        self.step_flow_response_table = RowControlTableWidget([
            ('Response Step', []), ('Match Criteria', ''), ('Step', []),
            ('Sleep Time', '0.0')
        ])
        vbox.addLayout(self.step_flow_response_table.layout)
        self.step_flow_percentage_table = RowControlTableWidget([
            ('Percentage', ''), ('Step', []), ('Sleep Time', '0.0')
        ])
        self.step_flow_percentage_table.set_text([[
            '100', {
                'Next Step': ['Next Step', 'End Cycle', 'End User']
            }, '0.0'
        ]])
        vbox.addLayout(self.step_flow_percentage_table.layout)
        self.step_flow_conditional_table = RowControlTableWidget([
            ('Phrase 1', ''),
            ('Conditional', ['<', '<=', '=', '=>', '>', '!=', 'in', 'not in']),
            ('Phrase 2', ''), ('Operator', ['AND', 'OR'])
        ])
        vbox.addLayout(self.step_flow_conditional_table.layout)

        group_box.setLayout(vbox)
        return group_box

    def create_specific_step_details(self):
        pass


# ------------------------------------------------------ end of creations ----------------------------------------------------------

# def create_actions(self):
#     self.import_act = QAction("&Import...", self, shortcut="Ctrl+I", triggered=self.import_uj)
#     self.export_act = QAction("&Export...", self, shortcut="Ctrl+E", triggered=self.export_uj)

# def update_content_subtypes(self, item):
#     # item is supposed to be the text value of the combobox selection
#     print('reached function update_content_subtypes with item=', item)

    def debug__(self, message):
        self.debug_edit.appendPlainText(message)

    def load_data_file(self):
        filename = QFileDialog.getOpenFileName(self, 'Open File',
                                               os.getenv('HOME'))
        self.ddi_delimited_filename_widget.set_text(filename[0])
        self.selected_ddi.file_name = filename

    def import_uj(self, filename=[]):
        if not filename:
            filename = QFileDialog.getOpenFileName(self, 'Open File',
                                                   os.getenv('HOME'))
        self.uj = UserJourney('')
        self.uj.import_uj(filename[0])
        self.uj_name.set_text(self.uj.name)

        ddi_nodes = []
        for ddi in self.uj.dditems:
            new_ddi_node = QTreeWidgetItem()
            new_ddi_node.setText(0, ddi.name)
            ddi_nodes.append(new_ddi_node)

        self.ddi_tree.addTopLevelItems(ddi_nodes)

        date_type_ddis = self.uj.find_ddis_by_attribute('type', 'DATE    ')
        if date_type_ddis:
            self.ddi_date.related_ddi_box.reset_items(
                {z.name: z.name
                 for z in date_type_ddis})

        relatable_type_ddis = []
        for type_ in ['FLATFILE', 'LIST    ', 'RESPONSE']:
            relatable_type_ddis.extend(
                self.uj.find_ddis_by_attribute('type', type_))
        if relatable_type_ddis:
            self.ddi_related_ddi.reset_items(
                {z.name: z.name
                 for z in relatable_type_ddis})

        sourceable_step_names = []
        sourceable_steps = self.uj.find_steps_by_attribute(
            'name_user_defined', True)
        if sourceable_steps:
            self.ddi_response_source_step.reset_items(
                {str(z.id): z.name
                 for z in sourceable_steps})
            sourceable_step_names = [z.name for z in sourceable_steps]

        self.correlated_names = {
            z.field_name: z.field_name
            for z in self.uj.find_ddis_by_attribute('type', 'AUTOCORR')
        }  # if z.field_type == 'Repeated Fields' }
        self.ddi_auto_correlate_name.reset_items(self.correlated_names)

        groupnodes = []
        for stepgroup in self.uj.stepgroups:
            new_group_node = QTreeWidgetItem()
            new_group_node.setText(0, stepgroup.name)
            stepnodes = []
            for step in stepgroup.steps:
                new_step_node = QTreeWidgetItem(new_group_node)
                new_step_node.setText(0, step.name)
                stepnodes.append(new_step_node)

            groupnodes.append(new_group_node)

        self.step_tree.addTopLevelItems(groupnodes)

        self.step_flow_response_table.reset_row_template([
            ('Response Step', sourceable_step_names), ('Match Criteria', ''),
            ('Step', ['Next Step'] + sourceable_step_names +
             ['End Cycle', 'End User']), ('Sleep Time', '0.0')
        ])
        self.step_flow_percentage_table.reset_row_template([
            ('Percentage', ''),
            ('Step', ['Next Step'] + sourceable_step_names +
             ['End Cycle', 'End User']), ('Sleep Time', '0.0')
        ])
        # self.step_flow_percentage_table.set_text([['100', {'Next Step': ['Next Step', 'End Cycle', 'End User']}, '0.0']])

    def export_uj(self):
        pass
        # convert SIPHON_TYPES from full text to code before passing them to self.uj...

    def show_ddi_details(self):
        selected_ddi_name = self.ddi_tree.selectedItems()[0].text(0)
        self.selected_ddi = self.uj.find_ddi_by_name(selected_ddi_name)

        # Common
        self.ddi_name.set_text(selected_ddi_name)
        self.ddi_description.set_text(self.selected_ddi.description)
        self.ddi_type.set_text(DDI_TYPES[self.selected_ddi.type])
        self.ddi_sharing.set_text(self.selected_ddi.scope)
        self.ddi_refresh.set_text(self.selected_ddi.lifecycle)

        # Specific
        ddi_specific_fields = [
            self.ddi_value_widget,
            self.ddi_selector_widget,
            self.ddi_delimiter_character_widget,
            self.ddi_delimited_filename_widget,
            self.ddi_delimited_file_picker_button,
            self.ddi_column_index_widget,
            self.ddi_date,
            self.ddi_list_table,
            self.ddi_related_ddi,
            self.ddi_response_source_step,
            self.ddi_siphon_table,
            self.ddi_auto_correlate_type,
            self.ddi_auto_correlate_name,
            self.ddi_auto_correlate_appears_in,
            self.ddi_auto_increment_starting_value,
            self.ddi_auto_increment_increment,
            self.ddi_auto_increment_prefix,
            self.ddi_auto_increment_suffix,
            self.ddi_auto_increment_min_lenght,
        ]

        ddi_type_mappings = {
            ConstantDDI: {
                self.ddi_value_widget: 'value'
            },
            DateDDI: {
                self.ddi_date: [
                    'starting_point',
                    'starting_value',
                    'starting_value',
                    'offset_type',
                    'format',
                    'first_offset_sign',
                    'first_offset_value',
                    'first_offset_unit',
                    'second_offset_sign',
                    'second_offset_value',
                    'second_offset_unit',
                ]
            },
            DelimitedFileDDI: {
                self.ddi_delimiter_character_widget: 'delimiter',
                self.ddi_delimited_filename_widget: 'file_name',
                self.ddi_delimited_file_picker_button: '',
                self.ddi_column_index_widget: 'column',
                self.ddi_selector_widget: 'selection_type',
            },
            ListDDI: {
                self.ddi_selector_widget: 'selection_type',
                self.ddi_column_index_widget: 'column',
                self.ddi_list_table: ['table']
            },
            VariableDDI: {
                self.ddi_value_widget: 'value'
            },
            RelatedDDI: {
                self.ddi_column_index_widget: 'column',
                self.ddi_related_ddi: 'associated'
            },
            ResponseDDI: {
                self.ddi_selector_widget: 'selection_type',
                self.ddi_column_index_widget: 'column',
                self.ddi_response_source_step: 'source_step_id',
                self.ddi_siphon_table: 'dict_siphons'
            },
            AutoCorrelatedDDI: {
                self.ddi_auto_correlate_type:
                'field_type',
                self.ddi_auto_correlate_name:
                'field_name',
                self.ddi_auto_correlate_appears_in:
                ['find_in_url', 'find_in_post', 'find_in_headers']
            },
            AutoIncrementDDI: {
                self.ddi_auto_increment_starting_value: 'starting_value',
                self.ddi_auto_increment_increment: 'increment',
                self.ddi_auto_increment_prefix: 'prefix',
                self.ddi_auto_increment_suffix: 'suffix',
                self.ddi_auto_increment_min_lenght: 'minimum_length'
            },
        }

        object_attribute_pairs = ddi_type_mappings[type(self.selected_ddi)]
        # print('obj', object_attribute_pairs )
        # print('selected type', type(self.selected_ddi) ,'selected item expected attributes', object_attribute_pairs.values() )
        for field in ddi_specific_fields:
            debug_message = ''
            # print('field', field, 'values', object_attribute_pairs.values())
            if field in object_attribute_pairs.keys():
                field.show()
                target_attribute_name = object_attribute_pairs[field]
                # print('target attribute', target_attribute_name)
                if isinstance(target_attribute_name, str):
                    if target_attribute_name != '':
                        # print('ttt', type(getattr(self.selected_ddi, object_attribute_pairs[field])))
                        value = getattr(self.selected_ddi,
                                        object_attribute_pairs[field])
                        if callable(value):
                            value = value()
                        else:
                            value = str(value)
                        field.set_text(value)
                        # print('target attribute value', value)

                        # --- debug ---
                        if field == self.ddi_value_widget:
                            debug_message += 'field: ' + str(
                                field) + '; uj object value: ' + str(
                                    value) + '\n'
                else:
                    # currently this section covers for Date group & table widget
                    values = []
                    for attribute in target_attribute_name:
                        try:
                            # values.append(str(getattr(self.selected_ddi, attribute)))
                            values.append(getattr(self.selected_ddi,
                                                  attribute))
                        except AttributeError:
                            pass

                        # print('values', values)
                    field.set_values(*values)
                    # where set_values(self, starting_point, starting_fixed_edit, starting_related_ddi, offset, sign1='', amount1=0, unit1='', sign2='', amount2=0, unit2=''):
                    #  i.e. the attributes that may be missing are set as optional arguments

            else:
                field.hide()

            # --- debug ---
            if field == self.ddi_response_source_step:
                debug_message += 'ui object value: ' + self.ddi_response_source_step.text(
                ) + '; visibility: ' + str(
                    self.ddi_response_source_step.combo_box.isVisible()) + '\n'
                self.debug__(debug_message)

    def show_step_details(self):
        selected_step_name = self.step_tree.selectedItems()[0].text(0)
        self.selected_step = self.uj.find_step_by_name(selected_step_name)

        # self.ddi_name.set_text(selected_ddi_name)
        # self.ddi_description.set_text(self.selected_ddi.description)
        # self.ddi_type.set_text(DDI_TYPES[self.selected_ddi.type])
        # self.ddi_sharing.set_text(self.selected_ddi.scope)
        # self.ddi_refresh.set_text(self.selected_ddi.lifecycle)

        self.step_name.set_text(selected_step_name)
        self.step_pre_time.set_text(self.selected_step.sleeptime)
        self.step_description.set_text(self.selected_step.description)
        # print(self.selected_step.first_cycle_only, self.selected_step.last_cycle_only, self.selected_step.count_as_transaction, self.selected_step.processresponse, self.selected_step.execute_separately)
        self.step_checkboxes.set_values(
            self.selected_step.first_cycle_only,
            self.selected_step.last_cycle_only,
            self.selected_step.count_as_transaction,
            self.selected_step.processresponse,
            self.selected_step.execute_separately)
        # ['Run First Cycle Only', 'Run Last Cycle Only', 'Count as Transaction', 'Process Response', 'Execute Separately'])
        self.request_type.set_text(self.selected_step.type)
        selected_step_content_type = ''
        for header in self.selected_step.headers:
            if header['name'] == 'Content-Type':
                selected_step_content_type = header['value']
        self.content_type.set_text(selected_step_content_type)
        self.step_url.set_text(self.selected_step.request)
        if self.selected_step.get_itmes == []:
            self.step_url_params_table.hide()
        else:
            self.step_url_params_table.show()
            get_params = []
            for item in self.selected_step.get_itmes:
                get_params.append([item['name'], item['value']])
            self.step_url_params_table.set_text(get_params)

        if self.selected_step.post_items == []:
            self.step_post_params_table.hide()
            self.step_post_block.hide()
        else:
            if self.selected_step.post_items[0]['name'] == '':
                self.step_post_params_table.hide()
                self.step_post_block.show()
                self.step_post_block.appendPlainText(
                    self.selected_step.post_items[0]['value'])
            else:
                self.step_post_block.hide()
                self.step_post_params_table.show()
                self.step_post_params_table.set_text(
                    [[z['name'], z['value']]
                     for z in self.selected_step.post_items])

        selected_step_validation_type = ''
        for item in self.selected_step.items:
            if item['name'] == 'ValidationType':
                selected_step_validation_type = item['value']

        self.step_validation_type.set_text(selected_step_validation_type)
        self.step_validation_text.set_text(self.selected_step.success)

        selected_step_max_response = ''
        for item in self.selected_step.items:
            if item['name'] == 'MaxResponseTime':
                selected_step_max_response = item['value']
        if self.selected_step.is_lead():
            self.max_response.show()
            self.max_response.set_text(selected_step_max_response)
        else:
            self.max_response.hide()

        selected_step_dynamic_content = []
        for item in self.selected_step.items:
            if item['name'] == 'contentTypes':
                selected_step_dynamic_content = [
                    z in item['value']
                    for z in ['images', 'javaScript', 'css']
                ]
        if self.selected_step.is_lead():
            self.step_dynamic_content.show()
            self.step_dynamic_content.set_values(
                *selected_step_dynamic_content)
        else:
            self.step_dynamic_content.hide()

        self.step_flow_target_plus.hide()
        self.step_flow_sleep.hide()
        self.step_flow_ddl_ddi.hide()
        self.step_flow_varloop_start.hide()
        self.step_flow_varloop_end.hide()
        self.step_flow_fixloop.hide()
        self.step_flow_conditional_true.hide()
        self.step_flow_conditional_false.hide()
        self.step_flow_response_table.hide()
        self.step_flow_percentage_table.hide()
        self.step_flow_conditional_table.hide()

        # need to update the step list every time because new steps might have been added after the initial import
        steps = self.uj.list_step_name_id_pairs()
        steps.update({
            'NEXT_STEP': 'Next Step',
            'END_CYCLE': 'End Cycle',
            'END_USER': '******'
        })
        self.step_flow_target_plus.reset_items(
            steps)  # reset val even if hidden

        if not hasattr(self.selected_step, 'flow_type'):
            self.step_flow_control_type.set_text('GoTo')
            self.step_flow_target_plus.show()
            self.step_flow_target_plus.set_text('NEXT_STEP')
            self.step_flow_sleep.show()
            self.step_flow_sleep.set_text('0.0')
        else:  # ==================================== ALL NON 'GOTO' FLOW CONTROL ====================================
            self.step_flow_control_type.set_text(self.selected_step.flow_type)

            selected_step_flow_target = ''
            selected_step_flow_sleep = ''
            selected_step_flow_ddi = ''
            selected_step_flow_miniter = ''
            selected_step_flow_maxiter = ''
            selected_step_flow_iter = ''
            selected_step_flow_conditional_true_target = ''
            selected_step_flow_conditional_false_target = ''
            for item in self.selected_step.flow_items:
                if item['name'] == 'DESTINATIONSTEP':
                    selected_step_flow_target = item['value']
                if item['name'] == 'SLEEPTIME':
                    selected_step_flow_sleep = str(float(item['value']) / 1000)
                if item['name'] == 'DDITEM':
                    selected_step_flow_ddi = item['value']
                if item['name'] == 'MINITERCOUNT':
                    selected_step_flow_miniter = item['value']
                if item['name'] == 'MAXITERCOUNT':
                    selected_step_flow_maxiter = item['value']
                if item['name'] == 'ITERCOUNT':
                    selected_step_flow_iter = item['value']
                if item['name'] == 'TRUECONDITIONSTEP':
                    selected_step_flow_conditional_true_target = item['value']
                if item['name'] == 'FALSECONDITIONSTEP':
                    selected_step_flow_conditional_false_target = item['value']

            if self.selected_step.flow_type not in [
                    'RESPONSE', 'CONDTITIONAL', 'PERCENTAGE'
            ]:
                self.step_flow_target_plus.show()
                self.step_flow_target_plus.set_text(selected_step_flow_target)

            if self.selected_step.flow_type not in ['RESPONSE', 'PERCENTAGE']:
                self.step_flow_sleep.show()
                self.step_flow_sleep.set_text(selected_step_flow_sleep)

            if self.selected_step.flow_type == 'DDLOOP':
                applicable_ddis_by_refresh = self.uj.find_ddis_by_attribute(
                    'lifecycle', 'T'
                )  # {'C': 'Cycle', 'R': 'Run', 'T': 'Time', 'U': 'User',}
                applicable_ddis_by_selection_randomunique = self.uj.find_ddis_by_attribute(
                    'selection_type', 'RANDONCE')
                applicable_ddis_by_selection_sequnique = self.uj.find_ddis_by_attribute(
                    'selection_type', 'SEQUONCE')
                applicable_ddis_by_selection = applicable_ddis_by_selection_randomunique + applicable_ddis_by_selection_sequnique
                applicable_ddis = {
                    z.name: z.name
                    for z in applicable_ddis_by_refresh
                    if z in applicable_ddis_by_selection
                }
                if len(applicable_ddis):
                    self.step_flow_ddl_ddi.reset_items(applicable_ddis)
                    self.step_flow_ddl_ddi.set_text(selected_step_flow_ddi)
                self.step_flow_ddl_ddi.show()

            if self.selected_step.flow_type == 'VARIABLELOOP':
                self.step_flow_varloop_start.show()
                self.step_flow_varloop_end.show()
                self.step_flow_varloop_start.set_text(
                    selected_step_flow_miniter)
                self.step_flow_varloop_end.set_text(selected_step_flow_maxiter)

            if self.selected_step.flow_type == 'FIXEDLOOP':
                self.step_flow_fixloop.show()
                self.step_flow_fixloop.set_text(selected_step_flow_iter)

            if self.selected_step.flow_type == 'RESPONSE':
                orders = list(
                    set([z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_response_table.show()
                    table = []
                    for order in orders:
                        destination = ''
                        match = ''
                        source = ''
                        sleep = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'DESTINATIONSTEP' and item[
                                    'order'] == order:
                                try:
                                    destination = self.uj.find_step_by_id(
                                        int(item['value'])).name
                                except ValueError:
                                    if item['value'] == 'NEXT_STEP':
                                        destination = 'Next Step'
                                    elif item['value'] == 'END_CYCLE':
                                        destination = 'End Cycle'
                                    else:
                                        destination = 'End User'
                            if item['name'] == 'MATCHCRITERIA' and item[
                                    'order'] == order:
                                match = item['value']
                            if item['name'] == 'RESPONSESTEP' and item[
                                    'order'] == order:
                                source = self.uj.find_step_by_id(
                                    int(item['value'])).name
                            if item['name'] == 'SLEEPTIME' and item[
                                    'order'] == order:
                                sleep = str(float(item['value']) / 1000)

                        table.append([{
                            source: self.uj.list_step_name_id_pairs()
                        }, match, {
                            destination: steps
                        }, sleep])

                    self.step_flow_response_table.set_text(table)

            if self.selected_step.flow_type == 'PERCENTAGE':
                orders = list(
                    set([z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_percentage_table.show()
                    table = [['100', {'Next Step': ['Next Step']}, '0.0']]
                    extra_percentage = 0
                    for order in orders:
                        destination = ''
                        percentage = ''
                        sleep = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'DESTINATIONSTEP' and item[
                                    'order'] == order:
                                destination = item['value']
                            if item['name'] == 'PERCENTAGE' and item[
                                    'order'] == order:
                                percentage = item['value']
                                extra_percentage += int(percentage)
                            if item['name'] == 'SLEEPTIME' and item[
                                    'order'] == order:
                                sleep = str(float(item['value']) / 1000)

                        table.append([percentage, {destination: steps}, sleep])

                    table[0][0] = str(100 - extra_percentage)
                    self.step_flow_percentage_table.set_text(table)

            if self.selected_step.flow_type == 'CONDTITIONAL':

                self.step_flow_conditional_true.show()
                self.step_flow_conditional_true.reset_items(steps)
                self.step_flow_conditional_true.set_text(
                    selected_step_flow_conditional_true_target)
                self.step_flow_conditional_false.show()
                self.step_flow_conditional_false.reset_items(steps)
                self.step_flow_conditional_false.set_text(
                    selected_step_flow_conditional_false_target)

                orders = list(
                    set([z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_conditional_table.show()
                    table = []
                    for order in orders:
                        condition = ''
                        phrase1 = ''
                        operator = ''
                        phrase2 = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'CONDITION' and item[
                                    'order'] == order:
                                conditions = {
                                    'less than': '<',
                                    'less than or equals': '<=',
                                    'equals': '=',
                                    'not equals': '!=',
                                    'greater than or equals': '>=',
                                    'greater than': '>',
                                    'contains': 'in',
                                    'does not contain': 'not in',
                                }
                                condition = conditions[item['value']]
                            if item['name'] == 'FIRSTPHRASE' and item[
                                    'order'] == order:
                                phrase1 = item['value']
                            if item['name'] == 'OPERATOR' and item[
                                    'order'] == order:
                                operator = item['value']
                            if item['name'] == 'SECONDPHRASE' and item[
                                    'order'] == order:
                                phrase2 = item['value']

                        table.append([
                            phrase1, {
                                condition: [
                                    '<', '<=', '=', '=>', '>', '!=', 'in',
                                    'not in'
                                ]
                            }, phrase2, {
                                operator: ['AND', 'OR']
                            }
                        ])

                    self.step_flow_conditional_table.set_text(table)
Beispiel #6
0
class DataTree(QTabWidget):
    """ Class Definition for the Data Tree. """
    def __init__(self, viewer, parent=None):
        """ Initialize the Datatree """
        super(DataTree, self).__init__(parent)
        self.old_trace = []
        self.similar_items = []
        self.checkableItems = []
        self.changing_item = None
        self.viewer = viewer
        self.keys = viewer.keys
        self.noPrintTypes = viewer.noPrintTypes

        # Add the Tree Widgets
        self.Tree = QTreeWidget(self)
        self.Tree.setSizePolicy(QSP(QSP.Fixed, QSP.Expanding))
        self.Tree.headerItem().setText(0, "")
        self.Tree.headerItem().setText(1, "")
        self.Tree.setTreePosition(1)
        header = self.Tree.header()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setStretchLastSection(False)
        header.setVisible(False)
        self.Tree.setColumnWidth(0, 10)
        self.Tree.setColumnHidden(0, True)
        self.Tree.currentItemChanged.connect(self.viewer._change_tree)
        self.addTab(self.Tree, "Files")
        self.Tree.contextMenuEvent = self.viewer._dropdown
        self.Tree.resizeColumnToContents(1)

        # Add an alternative Tree Widget
        self.secTree = QTreeWidget(parent)
        self.secTree.setSizePolicy(QSP(QSP.Fixed, QSP.Expanding))
        self.secTree.headerItem().setText(0, "")
        self.secTree.headerItem().setText(1, "")
        self.secTree.setTreePosition(1)
        header = self.secTree.header()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setStretchLastSection(False)
        header.setVisible(False)
        self.secTree.setColumnWidth(0, 10)
        self.secTree.setColumnHidden(0, True)
        self.secTree.currentItemChanged.connect(self.viewer._change_tree)
        self.secTree.resizeColumnToContents(1)
        self.addTab(self.secTree, "Data")

        # Connect Signal at the end to avoid errors
        self.root = self.currentWidget().invisibleRootItem()
        self.currentChanged.connect(self._update_treetab)
        self.setAcceptDrops(True)

    def clear_tree(self):
        """ Clear the Tree. """
        self.checkableItems = []
        self.update_tree()

    def current_item(self):
        """ Return the currently selected Item. """
        return self.currentWidget().currentItem()

    def is_files_tree(self):
        """ Return True if the currently open Tree is the "Files"-Tree. """
        return self.currentWidget() == self.Tree

    def _finish_renaming(self):
        """ Finish the renaming of a data-key. """
        if not self.old_trace:
            return
        new_trace = self.viewer._get_obj_trace(self.changing_item)
        if new_trace == self.old_trace:
            return
        self.Tree.itemChanged.disconnect(self._finish_renaming)
        # Check if the name exists in siblings
        itemIndex = self.Tree.indexFromItem(self.changing_item, 1)
        siblingTxt = []
        if self.changing_item.parent():
            for n in range(self.changing_item.parent().childCount()):
                if itemIndex.sibling(n, 1) != itemIndex:
                    siblingTxt.append(itemIndex.sibling(n, 1).data(0))
        else:
            for n in range(self.Tree.topLevelItemCount()):
                if self.Tree.topLevelItem(n) != self.changing_item:
                    siblingTxt.append(itemIndex.sibling(n, 1).data(0))
        if new_trace[-1] in siblingTxt:
            self.changing_item.setData(0, 1, self.old_trace[-1])
            self.old_trace = []
            return
        # Replace the key
        self.viewer.set_data(new_trace, self.viewer.pop(self.old_trace))
        # If element is top-level-item
        if not self.changing_item.parent() and self.old_trace[0] in self.keys:
            self.keys[self.keys.index(self.old_trace[0])] = new_trace[0]
        self.old_trace = []
        # Make Item non-editable
        self.changing_item.setFlags(Qt.ItemFlag(61))

    def remove_from_checkables(self, item_list):
        """ Remove items from the checkableItems list. As it causes errors. """
        for item in item_list:
            if item in self.checkableItems:
                self.checkableItems.remove(item)
            if item.childCount() > 0:
                self.remove_from_checkables(item.takeChildren())

    def rename_key(self):
        """ Start the renaming of a data-key. """
        self.changing_item = self.Tree.currentItem()
        self.old_trace = self.viewer._get_obj_trace(self.changing_item)
        # Make Item editable
        self.changing_item.setFlags(Qt.ItemFlag(63))
        self.Tree.editItem(self.changing_item, 1)
        self.Tree.itemChanged.connect(self._finish_renaming)

    def _update_subtree(self, item, data):
        """ Add a new subtree to the current QTreeWidgetItem. """
        for n, k in enumerate(realsorted(data.keys(), key=_lowercase)):
            item.addChild(QTreeWidgetItem([None, k]))
            child = item.child(n)
            if isinstance(data[k], dict):
                self._update_subtree(child, data[k])
            elif not isinstance(data[k], self.noPrintTypes):
                child.setCheckState(0, Qt.Unchecked)
                self.checkableItems.append(child)

    def _update_subtree_sec(self, item, data):
        """ Add a new subtree to the current QTreeWidgetItem. """
        if not isinstance(data, dict):
            for s in self.similar_items:
                sitem = QTreeWidgetItem([None, s])
                sitem.setToolTip(1, s)
                item.addChild(sitem)
            if not isinstance(data, self.noPrintTypes):
                for c in range(item.childCount()):
                    item.child(c).setCheckState(0, Qt.Unchecked)
                    self.checkableItems.append(item.child(c))
        else:
            for n, k in enumerate(realsorted(data.keys(), key=_lowercase)):
                item.addChild(QTreeWidgetItem([None, k]))
                child = item.child(n)
                if isinstance(data[k], dict):
                    self._update_subtree(child, data[k])
                else:
                    for s in self.similar_items:
                        sitem = QTreeWidgetItem([None, s])
                        sitem.setToolTip(0, s)
                        child.addChild(sitem)
                    if not isinstance(data[k], self.noPrintTypes):
                        for c in range(child.childCount()):
                            child.child(c).setCheckState(0, Qt.Unchecked)
                            self.checkableItems.append(child.child(c))

    def update_tree(self):
        """ Add new data to TreeWidget. """
        itemList = []
        self.checkableItems = []
        for i in self.keys:
            item = QTreeWidgetItem([None, i])
            item.setToolTip(0, i)
            self._update_subtree(item, self.viewer._data[i])
            itemList.append(item)
        self.Tree.clear()
        self.Tree.addTopLevelItems(itemList)
        if self.currentWidget() == self.secTree:
            self._update_treetab(1)

    def _update_treetab(self, index):
        """ Update the currently selected treetab, on switching. """
        if self.viewer.diffBtn.isVisible():
            self.viewer._start_diff()
        if index == 1:
            self._update_tree_sec()
        else:
            self.update_tree()

    def _update_tree_sec(self):
        """ Generate the data tree. """
        self.checkableItems = []
        # get TopLevelItem of the current item as a reference
        ref = self.Tree.currentItem()
        self.secTree.clear()
        if ref is None:
            ref = self.Tree.topLevelItem(0)
            if ref is None:
                return
        while ref.parent() is not None:
            ref = ref.parent()
        flipped_var = self.viewer._data[ref.text(1)].keys()
        # Find all with a similar structure
        self.similar_items = []
        for i in range(self.Tree.topLevelItemCount()):
            top_level_key = self.Tree.topLevelItem(i).text(1)
            if self.viewer._data[top_level_key].keys() == flipped_var:
                self.similar_items.append(top_level_key)
        # Build the tree
        itemList = []
        for k in flipped_var:
            item = QTreeWidgetItem([None, k])
            self._update_subtree_sec(item, self.viewer._data[ref.text(1)][k])
            itemList.append(item)
        for k in self.keys:
            if k[:4] == "Diff":
                item = QTreeWidgetItem([None, k])
                self._update_subtree(item, self.viewer._data[k])
                itemList.append(item)
        self.secTree.addTopLevelItems(itemList)

    def dragEnterEvent(self, ev):
        """ Catch dragEnterEvents for file dropdown. """
        if ev.mimeData().hasUrls():
            ev.acceptProposedAction()

    def dropEvent(self, ev):
        """ Catch dropEvent to load the dropped file. """
        fnames = []
        for url in ev.mimeData().urls():
            fnames.append(url.toLocalFile())
        self.viewer.load_files(fnames)
class DictParameterWidget(GenericParameterWidget):
    """Widget class for DictParameter."""
    def __init__(self, parameter, parent=None):
        """Constructor

        .. versionadded:: 3.1

        :param parameter: A DictParameter object.
        :type parameter: DictParameter

        """
        # pylint: disable=E1002
        super().__init__(parameter, parent)
        # pylint: enable=E1002

        self.input = QTreeWidget()

        # generate tree model
        widget_items = self.generate_tree_model(self._parameter.value)
        self.input.addTopLevelItems(widget_items)
        # set header
        self.input.headerItem().setText(0, 'Keys')
        self.input.headerItem().setText(1, 'Values')

        self.inner_input_layout.addWidget(self.input)

        # override self._input_layout arrangement to make the label at the top
        # reset the layout
        self.input_layout.setParent(None)
        self.help_layout.setParent(None)

        self.label.setParent(None)
        self.inner_input_layout.setParent(None)

        self.input_layout = QVBoxLayout()
        self.input_layout.setSpacing(0)

        # put element into layout
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

    def generate_tree_model(self, data_dict):
        """Generate a tree model for specified dictionary

        :param data_dict: A dictionary
        :type data_dict: dict
        :return: list of QTreeWidgetItem
        :rtype list:
        """
        widget_items = []
        font = QFont()
        font.setBold(True)
        for key in data_dict.keys():
            entry = data_dict[key]
            key_item = QTreeWidgetItem()
            key_item.setText(0, str(key))
            key_item.setFont(0, font)
            if isinstance(entry, dict):
                items = self.generate_tree_model(entry)
                key_item.addChildren(items)
            else:
                # value_item = QTreeWidgetItem()
                key_item.setText(1, str(entry))
                key_item.setFlags(key_item.flags() | Qt.ItemIsEditable)
                # key_item.addChild(key_item)
            widget_items.append(key_item)

        return widget_items

    def extract_dict(self, widget_items):
        """Extract dictionary key and values from QTreeWidgetItems

        :param widget_items: List of QTreeWidgetItems
        :type widget_items: list
        :return: hierarchical dictionary extracted from widget_items
        :rtype dict:
        """
        data_dict = {}
        element_type = self._parameter.element_type
        if element_type == object:
            def object_cast(obj):
                return obj
            element_type = object_cast
        for key_item in widget_items:
            key = str(key_item.text(0))
            value = None
            if key_item.childCount() == 0:
                # value_item = key_item.child(0)
                value = element_type(key_item.text(1))
            elif key_item.childCount() > 1:
                value_items = [key_item.child(i)
                               for i in range(key_item.childCount())]
                value = self.extract_dict(value_items)
            data_dict[key] = value
        return data_dict

    def get_parameter(self):
        """Obtain the parameter object from the current widget state.

        :returns: A DictParameter from the current state of widget

        """
        root_widget_item = self.input.invisibleRootItem()
        widget_items = [root_widget_item.child(i)
                        for i in range(root_widget_item.childCount())]
        data_dict = self.extract_dict(widget_items)
        self._parameter.value = data_dict
        return self._parameter
Beispiel #8
0
class MultimediaManagementTab(QWidget):
    def __init__(self, obj_mng: ObjectManager, tpl_mng: TemplateManager,
                 sgn_mng: SignageManager, mtm_mng: MultimediaManager,
                 chn_mng: ChannelManager):
        super().__init__()

        self._obj_mng = obj_mng
        self._tpl_mng = tpl_mng
        self._sgn_mng = sgn_mng
        self._mtm_mng = mtm_mng
        self._chn_mng = chn_mng

        self._res = ResourceManager()
        self._multimedia_list = QTreeWidget()

        def multimedia_change_handler(change_type: utils.ChangeType,
                                      mtm_text: str = ''):
            get_selected = self._multimedia_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.DELETE:
                    parent = item.parent()
                    parent.removeChild(parent.child(parent.childCount() - 1))
                    parent.removeChild(item)
                    parent.addChild(QTreeWidgetItem(['+']))

        self._media_widget = MultimediaWidget(self._mtm_mng,
                                              multimedia_change_handler)
        self.init_ui()

    def multimedia_to_tree_item(self) -> [QTreeWidgetItem]:
        video_label_item = QTreeWidgetItem(['Video'])
        for video_id in self._mtm_mng.videos.keys():
            video_item = QTreeWidgetItem([video_id])
            video_label_item.addChild(video_item)
        video_label_item.addChild(QTreeWidgetItem(['+']))

        image_label_item = QTreeWidgetItem(['Image'])
        for image_id in self._mtm_mng.images.keys():
            image_item = QTreeWidgetItem([image_id])
            image_label_item.addChild(image_item)
        image_label_item.addChild(QTreeWidgetItem(['+']))

        return [video_label_item, image_label_item]

    def init_ui(self):
        # Left side of screen
        self._multimedia_list = QTreeWidget()
        self._multimedia_list.setHeaderLabel(self._res['multimediaListLabel'])
        self._multimedia_list.addTopLevelItems(self.multimedia_to_tree_item())
        self._multimedia_list.expandAll()
        self._multimedia_list.itemSelectionChanged.connect(
            self.list_item_clicked)

        # Gather altogether
        hbox_outmost = QHBoxLayout()
        hbox_outmost.addWidget(self._multimedia_list, 1)
        hbox_outmost.addWidget(self._media_widget, 4)
        self.setLayout(hbox_outmost)

    def list_item_clicked(self):
        get_selected = self.sender().selectedItems()
        if get_selected:
            item = get_selected[0]
            item_text = item.text(0)
            if item.parent() is None:
                # It is at topmost level
                # Selected one is Video/Image
                self._media_widget.clear_data_on_ui()
            else:
                if item_text == '+':
                    item.setSelected(False)
                    if item.parent().text(0) == "Image":
                        title = "Select Image"
                        extensions = "JPG Files (*.jpg);;JPEG Files (*.jpeg);;PNG Files (*.png)"
                    else:
                        title = "Select Video"
                        extensions = "MP4 Files (*.mp4);;OGG Files (*.ogg)"
                    options = QFileDialog.Options()
                    options |= QFileDialog.DontUseNativeDialog
                    file_name, _ = QFileDialog.getOpenFileName(self,
                                                               title,
                                                               "",
                                                               extensions,
                                                               options=options)
                    if file_name:
                        if item.parent().text(0) == "Image":
                            self._mtm_mng.add_image(Path(file_name))
                        else:
                            self._mtm_mng.add_video(Path(file_name))
                        file_id = file_name.split('/')[-1]
                        item.setText(0, file_id)

                        item.parent().addChild(QTreeWidgetItem(['+']))
                else:
                    # Selected one is multimedia
                    if item.parent().text(0) == 'Image':
                        # Selected one is image
                        mtm = self._mtm_mng.get_image(item_text)
                    else:
                        # Selected one is video
                        mtm = self._mtm_mng.get_video(item_text)
                    self._media_widget.load_data_on_ui(mtm)
Beispiel #9
0
class Window(QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        grid = QGridLayout()
        grid.addWidget(self.create_top_group(), 0, 0)
        grid.addLayout(self.create_mid_group(), 1, 0)
        grid.addWidget(self.create_bottom_group(), 2, 0)
        self.setLayout(grid)

        self.setWindowTitle("UJ Editor")
        self.resize(1280, 640)
        # self.create_actions()

    # def __wiggets_to_layout(self, layout, *widgets):
    #     for widget in widgets:
    #         layout.addWidget(widget)

    def __mix_to_layout(self, layout, *mix):
        for item in mix:
            if isinstance(item, QWidget):
                layout.addWidget(item)
            if isinstance(item, QLayout):
                layout.addLayout(item)

    def create_top_group(self):
        group_box = QGroupBox()
        self.import_button = QPushButton('&Import UJ')
        self.import_button.clicked.connect(self.import_uj)
        export_button = QPushButton('&Export UJ')
        export_button.clicked.connect(self.export_uj)
        self.uj_name = LabelLineEdit('UJ Name')
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0,0,0,0)
        self.__mix_to_layout(hbox, self.import_button, export_button, self.uj_name.layout)
        group_box.setLayout(hbox)
        group_box.setMaximumHeight(60)
        return group_box

    def create_mid_group(self):
        self.ddi_tree = QTreeWidget()
        self.ddi_tree.itemSelectionChanged.connect(self.show_ddi_details)

        ddi_details = QGroupBox()
        ddi_details_layout = QGridLayout()
        ddi_details_layout.setContentsMargins(0,0,0,0)
        ddi_details_layout.addWidget(self.create_common_ddi_details(), 0, 0, 1, 1)
        ddi_details_layout.addWidget(self.create_specific_ddi_details(), 1, 0, 3, 1)
        ddi_details.setLayout(ddi_details_layout)

        self.step_tree = QTreeWidget()
        self.step_tree.itemSelectionChanged.connect(self.show_step_details)
        step_details = QGroupBox()
        step_details_layout = QGridLayout()
        step_details_layout.setContentsMargins(0,0,0,0)
        step_details_layout.addWidget(self.create_common_step_details(), 0, 0, 1, 1)
        # step_details_layout.addWidget(self.create_specific_step_details(), 1, 0, 3, 1)
        step_details.setLayout(step_details_layout)

        splitter = QSplitter(self)
        splitter.addWidget(self.ddi_tree)
        splitter.addWidget(ddi_details)
        splitter.addWidget(self.step_tree)
        splitter.addWidget(step_details)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0,0,0,0)
        # self.__mix_to_layout(hbox, self.ddi_tree, ddi_details, self.step_tree, step_details)
        hbox.addWidget(splitter)
        # group_box.setLayout(hbox)
        return hbox

    def create_bottom_group(self):
        group_box = QGroupBox()
        group_box.setMaximumHeight(200)
        self.debug_edit = QPlainTextEdit()
        self.debug_edit.setMaximumHeight(160)
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0,0,0,0)
        # hbox.setSizeConstraint(200)
        hbox.addWidget(self.debug_edit)
        group_box.setLayout(hbox)
        return group_box

    def create_common_ddi_details(self):
        group_box = QGroupBox()
        vbox = QVBoxLayout()
        self.ddi_description = LabelLineEdit('Description')
        type_name_layout = QHBoxLayout()
        type_name_layout.setContentsMargins(0,0,0,0)
        self.ddi_name = LabelLineEdit('DDI Name')
        self.ddi_type = LabelComboBox('Type', DDI_TYPES)
        self.__mix_to_layout(type_name_layout, self.ddi_type.layout, self.ddi_name.layout)
        self.ddi_sharing = LabelButtonGroup('State Sharing', {'SCRIPT  ': '&Single User', 'THREAD  ': '&All Run Users'})
        self.ddi_refresh = LabelButtonGroup('Refresh Condition', {'C': 'Every Cycle', 'R': 'Once per Run', 'T': 'Every Time', 'U': 'Once per User'})
        vbox.setContentsMargins(0,0,0,0)
        self.__mix_to_layout(vbox, type_name_layout, self.ddi_description.layout, self.ddi_sharing.layout, self.ddi_refresh.layout)
        group_box.setLayout(vbox)
        return group_box


    # ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
    def create_specific_ddi_details(self):
        group_box = QGroupBox()
        self.ddi_specific_layout = QVBoxLayout()
        self.ddi_specific_layout.setContentsMargins(0,0,0,0)
        # self.value_layout = self.create_ddi_value()
        self.ddi_value_widget = LabelLineEdit('Value')
        self.ddi_selector_widget = LabelComboBox('Selector', SELECTOR_TYPES)
        # self.ddi_specific_layout.addWidget(self.ddi_value_widget)
        self.ddi_specific_layout.addLayout(self.ddi_value_widget.layout)
        self.ddi_specific_layout.addLayout(self.ddi_selector_widget.layout)

        self.ddi_date = DateFieldsGroup()
        self.ddi_specific_layout.addLayout(self.ddi_date.layout)

        self.ddi_delimiter_character_widget = LabelLineEdit('Delimiter:')
        self.ddi_delimiter_character_widget.line_edit.setMaxLength(1)
        self.ddi_delimited_filename_widget = LabelLineEdit('File Name:')
        self.ddi_delimited_file_picker_button = QPushButton('&Load Data File')
        self.ddi_delimited_file_picker_button.clicked.connect(self.load_data_file)
        delimited_layout = QHBoxLayout()
        delimited_layout.setContentsMargins(0,0,0,0)
        delimited_layout.addLayout(self.ddi_delimiter_character_widget.layout, stretch = 0)
        delimited_layout.addLayout(self.ddi_delimited_filename_widget.layout, stretch = 2)
        delimited_layout.addWidget(self.ddi_delimited_file_picker_button, stretch = 1)
        self.ddi_specific_layout.addLayout(delimited_layout)

        self.ddi_column_index_widget = LabelLineEdit('Column Index:')
        self.ddi_column_index_widget.line_edit.setInputMask('999')
        self.ddi_specific_layout.addLayout(self.ddi_column_index_widget.layout)

        self.ddi_list_table = MyTableWidget()
        self.ddi_specific_layout.addLayout(self.ddi_list_table.layout)

        self.ddi_related_ddi = LabelComboBox('Related to:')
        self.ddi_specific_layout.addLayout(self.ddi_related_ddi.layout)

        self.ddi_response_source_step = LabelComboBox('Source Step:')
        self.ddi_specific_layout.addLayout(self.ddi_response_source_step.layout)

        self.ddi_siphon_table = RowControlTableWidget([('type', list(SIPHON_TYPES.values())), ('start', ''), ('end', ''), ('index', '')])
        self.ddi_specific_layout.addLayout(self.ddi_siphon_table.layout)

        self.ddi_auto_correlate_type = LabelComboBox('Field Type:', { z:z for z in ['Repeated Fields', 'Known Fields'] })
        self.ddi_specific_layout.addLayout(self.ddi_auto_correlate_type.layout)
        self.ddi_auto_correlate_name = LabelComboBox('Field Name:')
        self.ddi_specific_layout.addLayout(self.ddi_auto_correlate_name.layout)
        self.ddi_auto_correlate_appears_in = LabelCheckboxesGroup('Appears In:', ['URL', 'Post', 'Headers'])
        self.ddi_specific_layout.addLayout(self.ddi_auto_correlate_appears_in.layout)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0,0,0,0)
        self.ddi_auto_increment_starting_value = LabelLineEdit('Starting Value:')
        hbox.addLayout(self.ddi_auto_increment_starting_value.layout)
        self.ddi_auto_increment_increment = LabelLineEdit('Increment:')
        hbox.addLayout(self.ddi_auto_increment_increment.layout)
        self.ddi_auto_increment_prefix = LabelLineEdit('Prefix:')
        hbox.addLayout(self.ddi_auto_increment_prefix.layout)
        self.ddi_auto_increment_suffix = LabelLineEdit('Suffix:')
        hbox.addLayout(self.ddi_auto_increment_suffix.layout)
        self.ddi_auto_increment_min_lenght = LabelLineEdit('Minimum Length:')
        hbox.addLayout(self.ddi_auto_increment_min_lenght.layout)
        self.ddi_specific_layout.addLayout(hbox)

        group_box.setLayout(self.ddi_specific_layout)
        return group_box

    # ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()

    def create_common_step_details(self):
        group_box = QGroupBox()
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0,0,0,0)
        vbox.setAlignment(Qt.AlignTop)
        name_sleep_layout = QHBoxLayout()
        name_sleep_layout.setContentsMargins(0,0,0,0)
        self.step_name = LabelLineEdit('Name')
        name_sleep_layout.addLayout(self.step_name.layout)
        self.step_pre_time = LabelLineEdit('Sleep Time (sec)')
        name_sleep_layout.addLayout(self.step_pre_time.layout)
        vbox.addLayout(name_sleep_layout)
        self.step_description = LabelLineEdit('Description')
        vbox.addLayout(self.step_description.layout)
        self.step_checkboxes = LabelCheckboxesGroup('', ['Run First Cycle Only', 'Run Last Cycle Only', 'Count as Transaction', 'Process Response', 'Execute Separately'])
        vbox.addLayout(self.step_checkboxes.layout)
        request_type_content_type_layout = QHBoxLayout()
        self.request_type = LabelComboBox('Type', { z:z for z in STEP_REQUEST_TYPES })
        request_type_content_type_layout.addLayout(self.request_type.layout)
        self.content_type = LabelComboBox('Content-Type', { z:z for z in STEP_CONTENT_TYPES })
        request_type_content_type_layout.addLayout(self.content_type.layout)
        # self.content_type.combo_box.currentIndexChanged[str].connect(self.update_content_subtypes)
        # # what's the point of having type and subtypes? the one sent with headers is the subtype - it should be the only selection
        # self.content_subtype = LabelComboBox('', { z:z for z in STEP_CONTENT_TYPES['form'] })
        # request_type_content_type_layout.addLayout(self.content_subtype.layout)
        vbox.addLayout(request_type_content_type_layout)
        self.step_url = LabelLineEdit('URL')
        vbox.addLayout(self.step_url.layout)
        self.step_url_params_table = RowControlTableWidget([('Parameter', ''), ('Value', '')])
        vbox.addLayout(self.step_url_params_table.layout)
        self.step_post_params_table = RowControlTableWidget([('Parameter', ''), ('Value', '')])
        vbox.addLayout(self.step_post_params_table.layout)
        self.step_post_block = QPlainTextEdit()
        vbox.addWidget(self.step_post_block)
        success_validation_layout = QHBoxLayout()
        success_validation_layout.setContentsMargins(0,0,0,0)
        self.step_validation_type = LabelComboBox('Success Validation', {'PLAIN': 'Plain Text','REGEX': 'Regular Expression'})
        success_validation_layout.addLayout(self.step_validation_type.layout)
        self.step_validation_text = LabelLineEdit('')
        success_validation_layout.addLayout(self.step_validation_text.layout)
        self.max_response = LabelLineEdit('Max acceptable response time')
        success_validation_layout.addLayout(self.max_response.layout)
        vbox.addLayout(success_validation_layout)
        self.step_dynamic_content = LabelCheckboxesGroup('Select content types to be handled automatically', ['images', 'css', 'javaScript'])
        vbox.addLayout(self.step_dynamic_content.layout)

        flow_control_line1_layout = QHBoxLayout()
        self.step_flow_control_type = LabelComboBox('Flow Control', {'': 'GoTo', 'RESPONSE': 'Response Based', 'CONDTITIONAL': 'Conditional', 'PERCENTAGE': 'Percentage Based',
                                                                     'DDLOOP': 'Dynamic Data Loop', 'VARIABLELOOP': 'Variable Loop', 'FIXEDLOOP': 'Fixed Loop'})
        flow_control_line1_layout.addLayout(self.step_flow_control_type.layout)
        self.step_flow_target_plus = LabelComboBox('Step', {'NEXT_STEP': 'Next Step', 'END_CYCLE': 'End Cycle', 'END_USER': '******'})
        flow_control_line1_layout.addLayout(self.step_flow_target_plus.layout)
        self.step_flow_sleep = LabelLineEdit('Sleep Time (sec)')
        flow_control_line1_layout.addLayout(self.step_flow_sleep.layout)
        vbox.addLayout(flow_control_line1_layout)

        flow_control_line2_layout = QHBoxLayout()
        self.step_flow_ddl_ddi = LabelComboBox('Dynamic Data Item', {})
        flow_control_line2_layout.addLayout(self.step_flow_ddl_ddi.layout)
        self.step_flow_varloop_start = LabelLineEdit('Minimum Iterations')
        flow_control_line2_layout.addLayout(self.step_flow_varloop_start.layout)
        self.step_flow_varloop_end = LabelLineEdit('Maximum Iterations')
        flow_control_line2_layout.addLayout(self.step_flow_varloop_end.layout)
        self.step_flow_fixloop = LabelLineEdit('Iterations')
        flow_control_line2_layout.addLayout(self.step_flow_fixloop.layout)

        self.step_flow_conditional_true = LabelComboBox('If true, go to', {'NEXT_STEP': 'Next Step', 'END_CYCLE': 'End Cycle', 'END_USER': '******'})
        flow_control_line2_layout.addLayout(self.step_flow_conditional_true.layout)
        self.step_flow_conditional_false = LabelComboBox('otherwise, go to', {'NEXT_STEP': 'Next Step', 'END_CYCLE': 'End Cycle', 'END_USER': '******'})
        flow_control_line2_layout.addLayout(self.step_flow_conditional_false.layout)
        vbox.addLayout(flow_control_line2_layout)

        self.step_flow_response_table = RowControlTableWidget([('Response Step', []), ('Match Criteria', ''), ('Step', []), ('Sleep Time', '0.0')])
        vbox.addLayout(self.step_flow_response_table.layout)
        self.step_flow_percentage_table = RowControlTableWidget([('Percentage', ''), ('Step', []), ('Sleep Time', '0.0')])
        self.step_flow_percentage_table.set_text([['100', {'Next Step': ['Next Step', 'End Cycle', 'End User']}, '0.0']])
        vbox.addLayout(self.step_flow_percentage_table.layout)
        self.step_flow_conditional_table = RowControlTableWidget([('Phrase 1', ''), ('Conditional',  ['<', '<=', '=', '=>', '>', '!=', 'in', 'not in']), ('Phrase 2', ''), ('Operator', ['AND', 'OR'])])
        vbox.addLayout(self.step_flow_conditional_table.layout)


        group_box.setLayout(vbox)
        return group_box


    def create_specific_step_details(self):
        pass




# ------------------------------------------------------ end of creations ----------------------------------------------------------

    # def create_actions(self):
    #     self.import_act = QAction("&Import...", self, shortcut="Ctrl+I", triggered=self.import_uj)
    #     self.export_act = QAction("&Export...", self, shortcut="Ctrl+E", triggered=self.export_uj)

    # def update_content_subtypes(self, item):
    #     # item is supposed to be the text value of the combobox selection
    #     print('reached function update_content_subtypes with item=', item)



    def debug__(self, message):
        self.debug_edit.appendPlainText(message)

    def load_data_file(self):
        filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
        self.ddi_delimited_filename_widget.set_text(filename[0])
        self.selected_ddi.file_name = filename

    def import_uj(self, filename=[]):
        if not filename:
            filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
        self.uj = UserJourney('')
        self.uj.import_uj(filename[0])
        self.uj_name.set_text(self.uj.name)

        ddi_nodes = []
        for ddi in self.uj.dditems:
            new_ddi_node = QTreeWidgetItem()
            new_ddi_node.setText(0, ddi.name)
            ddi_nodes.append(new_ddi_node)

        self.ddi_tree.addTopLevelItems(ddi_nodes)

        date_type_ddis = self.uj.find_ddis_by_attribute('type', 'DATE    ')
        if date_type_ddis:
            self.ddi_date.related_ddi_box.reset_items({z.name:z.name for z in date_type_ddis})

        relatable_type_ddis = []
        for type_ in ['FLATFILE', 'LIST    ', 'RESPONSE']:
            relatable_type_ddis.extend(self.uj.find_ddis_by_attribute('type', type_))
        if relatable_type_ddis:
            self.ddi_related_ddi.reset_items({z.name:z.name for z in relatable_type_ddis})

        sourceable_step_names = []
        sourceable_steps = self.uj.find_steps_by_attribute('name_user_defined', True)
        if sourceable_steps:
            self.ddi_response_source_step.reset_items({ str(z.id):z.name for z in sourceable_steps })
            sourceable_step_names = [ z.name for z in sourceable_steps ]

        self.correlated_names = { z.field_name:z.field_name for z in self.uj.find_ddis_by_attribute('type', 'AUTOCORR') } # if z.field_type == 'Repeated Fields' }
        self.ddi_auto_correlate_name.reset_items(self.correlated_names)

        groupnodes = []
        for stepgroup in self.uj.stepgroups:
            new_group_node = QTreeWidgetItem()
            new_group_node.setText(0, stepgroup.name)
            stepnodes = []
            for step in stepgroup.steps:
                new_step_node = QTreeWidgetItem(new_group_node)
                new_step_node.setText(0, step.name)
                stepnodes.append(new_step_node)

            groupnodes.append(new_group_node)

        self.step_tree.addTopLevelItems(groupnodes)

        self.step_flow_response_table.reset_row_template([('Response Step', sourceable_step_names), ('Match Criteria', ''), ('Step', ['Next Step'] + sourceable_step_names + ['End Cycle', 'End User']), ('Sleep Time', '0.0')])
        self.step_flow_percentage_table.reset_row_template([('Percentage', ''), ('Step', ['Next Step'] + sourceable_step_names + ['End Cycle', 'End User']), ('Sleep Time', '0.0')])
        # self.step_flow_percentage_table.set_text([['100', {'Next Step': ['Next Step', 'End Cycle', 'End User']}, '0.0']])

    def export_uj(self):
        pass
        # convert SIPHON_TYPES from full text to code before passing them to self.uj...

    def show_ddi_details(self):
        selected_ddi_name = self.ddi_tree.selectedItems()[0].text(0)
        self.selected_ddi = self.uj.find_ddi_by_name(selected_ddi_name)

        # Common
        self.ddi_name.set_text(selected_ddi_name)
        self.ddi_description.set_text(self.selected_ddi.description)
        self.ddi_type.set_text(DDI_TYPES[self.selected_ddi.type])
        self.ddi_sharing.set_text(self.selected_ddi.scope)
        self.ddi_refresh.set_text(self.selected_ddi.lifecycle)

        # Specific
        ddi_specific_fields = [
            self.ddi_value_widget,
            self.ddi_selector_widget,
            self.ddi_delimiter_character_widget,
            self.ddi_delimited_filename_widget,
            self.ddi_delimited_file_picker_button,
            self.ddi_column_index_widget,
            self.ddi_date,
            self.ddi_list_table,
            self.ddi_related_ddi,
            self.ddi_response_source_step,
            self.ddi_siphon_table,
            self.ddi_auto_correlate_type,
            self.ddi_auto_correlate_name,
            self.ddi_auto_correlate_appears_in,
            self.ddi_auto_increment_starting_value,
            self.ddi_auto_increment_increment,
            self.ddi_auto_increment_prefix,
            self.ddi_auto_increment_suffix,
            self.ddi_auto_increment_min_lenght,
        ]

        ddi_type_mappings = {
            ConstantDDI: {self.ddi_value_widget: 'value'},
            DateDDI: {
                self.ddi_date: [
                                'starting_point',
                                'starting_value',
                                'starting_value',
                                'offset_type',
                                'format',
                                'first_offset_sign',
                                'first_offset_value',
                                'first_offset_unit',
                                'second_offset_sign',
                                'second_offset_value',
                                'second_offset_unit',
                                ]
            },
            DelimitedFileDDI: {
                self.ddi_delimiter_character_widget: 'delimiter',
                self.ddi_delimited_filename_widget: 'file_name',
                self.ddi_delimited_file_picker_button: '',
                self.ddi_column_index_widget: 'column',
                self.ddi_selector_widget: 'selection_type',
            },
            ListDDI: {self.ddi_selector_widget: 'selection_type', self.ddi_column_index_widget: 'column', self.ddi_list_table: ['table']},
            VariableDDI: {self.ddi_value_widget: 'value'},
            RelatedDDI: {self.ddi_column_index_widget: 'column', self.ddi_related_ddi: 'associated'},
            ResponseDDI: {self.ddi_selector_widget: 'selection_type', self.ddi_column_index_widget: 'column', self.ddi_response_source_step: 'source_step_id', self.ddi_siphon_table: 'dict_siphons'},
            AutoCorrelatedDDI: {self.ddi_auto_correlate_type: 'field_type', self.ddi_auto_correlate_name: 'field_name', self.ddi_auto_correlate_appears_in: ['find_in_url', 'find_in_post', 'find_in_headers']},
            AutoIncrementDDI: {self.ddi_auto_increment_starting_value: 'starting_value', self.ddi_auto_increment_increment: 'increment', self.ddi_auto_increment_prefix: 'prefix',
                               self.ddi_auto_increment_suffix: 'suffix', self.ddi_auto_increment_min_lenght: 'minimum_length'},
        }

        object_attribute_pairs = ddi_type_mappings[type(self.selected_ddi)]
        # print('obj', object_attribute_pairs )
        # print('selected type', type(self.selected_ddi) ,'selected item expected attributes', object_attribute_pairs.values() )
        for field in ddi_specific_fields:
            debug_message = ''
            # print('field', field, 'values', object_attribute_pairs.values())
            if field in object_attribute_pairs.keys():
                field.show()
                target_attribute_name = object_attribute_pairs[field]
                # print('target attribute', target_attribute_name)
                if isinstance(target_attribute_name, str):
                    if target_attribute_name != '':
                        # print('ttt', type(getattr(self.selected_ddi, object_attribute_pairs[field])))
                        value = getattr(self.selected_ddi, object_attribute_pairs[field])
                        if callable(value):
                            value = value()
                        else:
                            value = str(value)
                        field.set_text(value)
                        # print('target attribute value', value)

                        # --- debug ---
                        if field == self.ddi_value_widget:
                            debug_message += 'field: '+str(field)+'; uj object value: '+ str(value) + '\n'
                else:
                    # currently this section covers for Date group & table widget
                    values =[]
                    for attribute in target_attribute_name:
                        try:
                            # values.append(str(getattr(self.selected_ddi, attribute)))
                            values.append(getattr(self.selected_ddi, attribute))
                        except AttributeError:
                            pass

                        # print('values', values)
                    field.set_values(*values)
                    # where set_values(self, starting_point, starting_fixed_edit, starting_related_ddi, offset, sign1='', amount1=0, unit1='', sign2='', amount2=0, unit2=''):
                    #  i.e. the attributes that may be missing are set as optional arguments


            else:
                field.hide()

            # --- debug ---
            if field == self.ddi_response_source_step:
                debug_message += 'ui object value: ' + self.ddi_response_source_step.text() + '; visibility: ' + str(self.ddi_response_source_step.combo_box.isVisible()) + '\n'
                self.debug__(debug_message)



    def show_step_details(self):
        selected_step_name = self.step_tree.selectedItems()[0].text(0)
        self.selected_step = self.uj.find_step_by_name(selected_step_name)

        # self.ddi_name.set_text(selected_ddi_name)
        # self.ddi_description.set_text(self.selected_ddi.description)
        # self.ddi_type.set_text(DDI_TYPES[self.selected_ddi.type])
        # self.ddi_sharing.set_text(self.selected_ddi.scope)
        # self.ddi_refresh.set_text(self.selected_ddi.lifecycle)

        self.step_name.set_text(selected_step_name)
        self.step_pre_time.set_text(self.selected_step.sleeptime)
        self.step_description.set_text(self.selected_step.description)
        # print(self.selected_step.first_cycle_only, self.selected_step.last_cycle_only, self.selected_step.count_as_transaction, self.selected_step.processresponse, self.selected_step.execute_separately)
        self.step_checkboxes.set_values(self.selected_step.first_cycle_only, self.selected_step.last_cycle_only, self.selected_step.count_as_transaction,
                                         self.selected_step.processresponse, self.selected_step.execute_separately)
        # ['Run First Cycle Only', 'Run Last Cycle Only', 'Count as Transaction', 'Process Response', 'Execute Separately'])
        self.request_type.set_text(self.selected_step.type)
        selected_step_content_type = ''
        for header in self.selected_step.headers:
            if header['name'] == 'Content-Type':
                selected_step_content_type = header['value']
        self.content_type.set_text(selected_step_content_type)
        self.step_url.set_text(self.selected_step.request)
        if self.selected_step.get_itmes == []:
            self.step_url_params_table.hide()
        else:
            self.step_url_params_table.show()
            get_params = []
            for item in self.selected_step.get_itmes:
                get_params.append([item['name'], item['value']])
            self.step_url_params_table.set_text(get_params)

        if self.selected_step.post_items == []:
            self.step_post_params_table.hide()
            self.step_post_block.hide()
        else:
            if self.selected_step.post_items[0]['name'] == '':
                self.step_post_params_table.hide()
                self.step_post_block.show()
                self.step_post_block.appendPlainText(self.selected_step.post_items[0]['value'])
            else:
                self.step_post_block.hide()
                self.step_post_params_table.show()
                self.step_post_params_table.set_text([ [z['name'], z['value']] for z in self.selected_step.post_items])

        selected_step_validation_type = ''
        for item in self.selected_step.items:
            if item['name'] == 'ValidationType':
                selected_step_validation_type = item['value']

        self.step_validation_type.set_text(selected_step_validation_type)
        self.step_validation_text.set_text(self.selected_step.success)

        selected_step_max_response = ''
        for item in self.selected_step.items:
            if item['name'] == 'MaxResponseTime':
                selected_step_max_response = item['value']
        if self.selected_step.is_lead():
            self.max_response.show()
            self.max_response.set_text(selected_step_max_response)
        else:
            self.max_response.hide()

        selected_step_dynamic_content = []
        for item in self.selected_step.items:
            if item['name'] == 'contentTypes':
                selected_step_dynamic_content = [ z in item['value'] for z in ['images', 'javaScript', 'css'] ]
        if self.selected_step.is_lead():
            self.step_dynamic_content.show()
            self.step_dynamic_content.set_values(*selected_step_dynamic_content)
        else:
            self.step_dynamic_content.hide()

        self.step_flow_target_plus.hide()
        self.step_flow_sleep.hide()
        self.step_flow_ddl_ddi.hide()
        self.step_flow_varloop_start.hide()
        self.step_flow_varloop_end.hide()
        self.step_flow_fixloop.hide()
        self.step_flow_conditional_true.hide()
        self.step_flow_conditional_false.hide()
        self.step_flow_response_table.hide()
        self.step_flow_percentage_table.hide()
        self.step_flow_conditional_table.hide()

        # need to update the step list every time because new steps might have been added after the initial import
        steps = self.uj.list_step_name_id_pairs()
        steps.update({'NEXT_STEP': 'Next Step', 'END_CYCLE': 'End Cycle', 'END_USER': '******'})
        self.step_flow_target_plus.reset_items(steps) # reset val even if hidden

        if not hasattr(self.selected_step, 'flow_type'):
            self.step_flow_control_type.set_text('GoTo')
            self.step_flow_target_plus.show()
            self.step_flow_target_plus.set_text('NEXT_STEP')
            self.step_flow_sleep.show()
            self.step_flow_sleep.set_text('0.0')
        else:                              # ==================================== ALL NON 'GOTO' FLOW CONTROL ====================================
            self.step_flow_control_type.set_text(self.selected_step.flow_type)

            selected_step_flow_target = ''
            selected_step_flow_sleep = ''
            selected_step_flow_ddi = ''
            selected_step_flow_miniter = ''
            selected_step_flow_maxiter = ''
            selected_step_flow_iter = ''
            selected_step_flow_conditional_true_target = ''
            selected_step_flow_conditional_false_target = ''
            for item in self.selected_step.flow_items:
                if item['name'] == 'DESTINATIONSTEP':
                    selected_step_flow_target = item['value']
                if item['name'] == 'SLEEPTIME':
                    selected_step_flow_sleep = str(float(item['value'])/1000)
                if item['name'] == 'DDITEM':
                    selected_step_flow_ddi = item['value']
                if item['name'] == 'MINITERCOUNT':
                    selected_step_flow_miniter = item['value']
                if item['name'] == 'MAXITERCOUNT':
                    selected_step_flow_maxiter = item['value']
                if item['name'] == 'ITERCOUNT':
                    selected_step_flow_iter = item['value']
                if item['name'] == 'TRUECONDITIONSTEP':
                    selected_step_flow_conditional_true_target = item['value']
                if item['name'] == 'FALSECONDITIONSTEP':
                    selected_step_flow_conditional_false_target = item['value']

            if self.selected_step.flow_type not in ['RESPONSE', 'CONDTITIONAL', 'PERCENTAGE']:
                self.step_flow_target_plus.show()
                self.step_flow_target_plus.set_text(selected_step_flow_target)

            if self.selected_step.flow_type not in ['RESPONSE', 'PERCENTAGE']:
                self.step_flow_sleep.show()
                self.step_flow_sleep.set_text(selected_step_flow_sleep)

            if self.selected_step.flow_type == 'DDLOOP':
                applicable_ddis_by_refresh = self.uj.find_ddis_by_attribute('lifecycle', 'T') # {'C': 'Cycle', 'R': 'Run', 'T': 'Time', 'U': 'User',}
                applicable_ddis_by_selection_randomunique = self.uj.find_ddis_by_attribute('selection_type', 'RANDONCE')
                applicable_ddis_by_selection_sequnique = self.uj.find_ddis_by_attribute('selection_type', 'SEQUONCE')
                applicable_ddis_by_selection = applicable_ddis_by_selection_randomunique + applicable_ddis_by_selection_sequnique
                applicable_ddis = { z.name:z.name for z in applicable_ddis_by_refresh if z in applicable_ddis_by_selection }
                if len(applicable_ddis):
                    self.step_flow_ddl_ddi.reset_items(applicable_ddis)
                    self.step_flow_ddl_ddi.set_text(selected_step_flow_ddi)
                self.step_flow_ddl_ddi.show()

            if self.selected_step.flow_type == 'VARIABLELOOP':
                self.step_flow_varloop_start.show()
                self.step_flow_varloop_end.show()
                self.step_flow_varloop_start.set_text(selected_step_flow_miniter)
                self.step_flow_varloop_end.set_text(selected_step_flow_maxiter)

            if self.selected_step.flow_type == 'FIXEDLOOP':
                self.step_flow_fixloop.show()
                self.step_flow_fixloop.set_text(selected_step_flow_iter)

            if self.selected_step.flow_type == 'RESPONSE':
                orders = list(set([ z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_response_table.show()
                    table = []
                    for order in orders:
                        destination = ''
                        match = ''
                        source = ''
                        sleep = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'DESTINATIONSTEP' and item['order'] == order:
                                try:
                                    destination = self.uj.find_step_by_id(int(item['value'])).name
                                except ValueError:
                                    if item['value'] == 'NEXT_STEP':
                                        destination = 'Next Step'
                                    elif item['value'] == 'END_CYCLE':
                                        destination = 'End Cycle'
                                    else:
                                        destination = 'End User'
                            if item['name'] == 'MATCHCRITERIA' and item['order'] == order:
                                match = item['value']
                            if item['name'] == 'RESPONSESTEP' and item['order'] == order:
                                source = self.uj.find_step_by_id(int(item['value'])).name
                            if item['name'] == 'SLEEPTIME' and item['order'] == order:
                                sleep = str(float(item['value'])/1000)

                        table.append([{source: self.uj.list_step_name_id_pairs()}, match, {destination: steps}, sleep])

                    self.step_flow_response_table.set_text(table)

            if self.selected_step.flow_type == 'PERCENTAGE':
                orders = list(set([ z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_percentage_table.show()
                    table = [['100', {'Next Step': ['Next Step']}, '0.0']]
                    extra_percentage = 0
                    for order in orders:
                        destination = ''
                        percentage = ''
                        sleep = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'DESTINATIONSTEP' and item['order'] == order:
                                destination = item['value']
                            if item['name'] == 'PERCENTAGE' and item['order'] == order:
                                percentage = item['value']
                                extra_percentage += int(percentage)
                            if item['name'] == 'SLEEPTIME' and item['order'] == order:
                                sleep = str(float(item['value'])/1000)

                        table.append([percentage, {destination: steps}, sleep])

                    table[0][0] = str(100 - extra_percentage)
                    self.step_flow_percentage_table.set_text(table)

            if self.selected_step.flow_type == 'CONDTITIONAL':

                self.step_flow_conditional_true.show()
                self.step_flow_conditional_true.reset_items(steps)
                self.step_flow_conditional_true.set_text(selected_step_flow_conditional_true_target)
                self.step_flow_conditional_false.show()
                self.step_flow_conditional_false.reset_items(steps)
                self.step_flow_conditional_false.set_text(selected_step_flow_conditional_false_target)

                orders = list(set([ z['order'] for z in self.selected_step.flow_items]))
                orders.sort()
                if len(orders):
                    self.step_flow_conditional_table.show()
                    table = []
                    for order in orders:
                        condition = ''
                        phrase1 = ''
                        operator = ''
                        phrase2 = ''
                        for item in self.selected_step.flow_items:
                            if item['name'] == 'CONDITION' and item['order'] == order:
                                conditions = {'less than': '<', 'less than or equals': '<=', 'equals': '=', 'not equals': '!=', 'greater than or equals': '>=', 'greater than': '>', 'contains': 'in', 'does not contain': 'not in',}
                                condition = conditions[item['value']]
                            if item['name'] == 'FIRSTPHRASE' and item['order'] == order:
                                phrase1 = item['value']
                            if item['name'] == 'OPERATOR' and item['order'] == order:
                                operator = item['value']
                            if item['name'] == 'SECONDPHRASE' and item['order'] == order:
                                phrase2 = item['value']

                        table.append([phrase1, {condition: ['<', '<=', '=', '=>', '>', '!=', 'in', 'not in']}, phrase2, {operator: ['AND', 'OR']}])

                    self.step_flow_conditional_table.set_text(table)
Beispiel #10
0
class GrandAvgWidget(QWidget):
    def __init__(self, mw):
        super().__init__()
        self.mw = mw
        self.gad = {
            'GroupA': ['a', 'b', 'c'],
            'GroupB': {
                'a': ['a', 'b', 'c'],
                'b': ['a', 'b', 'c'],
                'c': ['a', 'b', 'c']
            },
            'GroupC': {
                'd': {
                    'a': ['a', 'b', 'c'],
                    'b': ['a', 'b', 'c'],
                    'c': ['a', 'b', 'c']
                },
                'e': ['a', 'b', 'c']
            }
        }
        self.layout = QVBoxLayout()
        self.treew = QTreeWidget()
        self.treew.setColumnCount(1)
        self.treew.setHeaderLabel('Subject:')

        self.update_treew()

        self.layout.addWidget(self.treew)
        self.setLayout(self.layout)

    # Just supports level 3 nested dictionaries, kind of bulky needs improvement (probably with model/view-architecture)
    def update_treew(self):
        top_items = []
        for key1 in self.gad:
            top_item = QTreeWidgetItem()
            top_item.setText(0, key1)
            if type(self.gad[key1]) is dict:
                for key2 in self.gad[key1]:
                    sub_item = QTreeWidgetItem(top_item)
                    sub_item.setText(0, key2)
                    if type(self.gad[key1][key2]) is dict:
                        for key3 in self.gad[key1][key2]:
                            subsub_item = QTreeWidgetItem(sub_item)
                            subsub_item.setText(0, key3)
                            if type(self.gad[key1][key2][key3]) is list:
                                for listitem in self.gad[key1][key2][key3]:
                                    subsubsub_item = QTreeWidgetItem(
                                        subsub_item)
                                    subsubsub_item.setText(0, listitem)
                            else:
                                subsubsub_item = QTreeWidgetItem(subsub_item)
                                subsubsub_item.setText(
                                    0, self.gad[key1][key2][key3])
                    elif type(self.gad[key1][key2]) is list:
                        for listitem in self.gad[key1][key2]:
                            subsub_item = QTreeWidgetItem(sub_item)
                            subsub_item.setText(0, listitem)
                    else:
                        subsub_item = QTreeWidgetItem(sub_item)
                        subsub_item.setText(0, self.gad[key1][key2])
            elif type(self.gad[key1]) is list:
                for listitem in self.gad[key1]:
                    sub_item = QTreeWidgetItem(top_item)
                    sub_item.setText(0, listitem)
            else:
                sub_item = QTreeWidgetItem(top_item)
                sub_item.setText(0, self.gad[key1])
            top_items.append(top_item)

        self.treew.addTopLevelItems(top_items)

    # List-items and dict in same list not allowed
    def get_treew(self):
        new_dict = {}
        for idx in range(self.treew.topLevelItemCount()):
            top_item = self.treew.topLevelItem(idx)
            ti_text = top_item.text(0)
            new_dict.update({ti_text: None})
            for child_idx1 in range(top_item.childCount()):
                child_item1 = top_item.child(child_idx1)
                ci1_text = child_item1.text(0)
                if child_item1.childCount() == 0:
                    if type(new_dict[ti_text]) is list:
                        new_dict[ti_text].append(ci1_text)
                    else:
                        new_dict.update({ti_text: [ci1_text]})
                else:
                    if type(new_dict[ti_text]) is dict:
                        new_dict[ti_text].update({ci1_text: None})
                    else:
                        new_dict[ti_text] = {ci1_text: None}
                    for child_idx2 in range(child_item1.childCount()):
                        child_item2 = child_item1.child(child_idx2)
                        ci2_text = child_item2.text(0)
                        if child_item2.childCount() == 0:
                            if type(new_dict[ti_text][ci1_text]) is list:
                                new_dict[ti_text][ci1_text].append(ci2_text)
                            else:
                                new_dict[ti_text].update(
                                    {ci1_text: [ci2_text]})
                        else:
                            if type(new_dict[ti_text][ci1_text]) is dict:
                                new_dict[ti_text][ci1_text].update(
                                    {ci2_text: None})
                            else:
                                new_dict[ti_text][ci1_text] = {ci2_text: None}
                            for child_idx3 in range(child_item2.childCount()):
                                child_item3 = child_item2.child(child_idx3)
                                ci3_text = child_item3.text(0)
                                if type(new_dict[ti_text][ci1_text]
                                        [ci2_text]) is list:
                                    new_dict[ti_text][ci1_text][
                                        ci2_text].append(ci3_text)
                                else:
                                    new_dict[ti_text][ci1_text].update(
                                        {ci2_text: [ci3_text]})

    def add_group(self):
        group_name = QInputDialog.getText(self,
                                          'Enter Group',
                                          'Enter the name for a group:',
                                          text='group-name')
        if group_name:
            self.treew.addTopLevelItem(0, group_name)
            self.mw.pr.grand_avg_dict.update({group_name: None})

    def add_sub_group(self):
        pass
Beispiel #11
0
class SignageManagementTab(QWidget):
    def __init__(self, obj_mng: ObjectManager, tpl_mng: TemplateManager,
                 sgn_mng: SignageManager, mtm_mng: MultimediaManager,
                 chn_mng: ChannelManager):
        super().__init__()

        self._obj_mng = obj_mng
        self._tpl_mng = tpl_mng
        self._sgn_mng = sgn_mng
        self._mtm_mng = mtm_mng
        self._chn_mng = chn_mng

        self._res = ResourceManager()
        # Left part of screen
        self._signage_list = QTreeWidget()
        self._btn_up = QPushButton(self._res['upButtonText'])
        self._btn_down = QPushButton(self._res['downButtonText'])
        # Right part of screen
        self._stacked_widget = QStackedWidget()
        self._widget_idx = dict()

        self.init_ui()

    def signage_to_tree_item(self) -> [QTreeWidgetItem]:
        signage_items = []
        # For all signage
        for signage_id in self._sgn_mng.signages.keys():
            signage = self._sgn_mng.get_signage(signage_id)
            signage_text = utils.gen_ui_text(signage.title, signage.id)
            signage_item = QTreeWidgetItem([signage_text])

            # Add frame
            frame_tpl = signage.frame.template
            frame_text = utils.gen_ui_text(frame_tpl.definition.name,
                                           frame_tpl.id)
            frame_item = QTreeWidgetItem(["F:" + frame_text])
            signage_item.addChild(frame_item)

            # Add scene
            idx = 1
            for scene in signage.scenes:
                scene_tpl = scene.template
                scene_text = utils.gen_ui_text(scene_tpl.definition.name,
                                               scene_tpl.id)
                scene_item = QTreeWidgetItem([str(idx) + ":" + scene_text])
                signage_item.addChild(scene_item)
                idx += 1

            # Add scene addition button
            scene_addition_item = QTreeWidgetItem(["+"])
            signage_item.addChild(scene_addition_item)
            signage_items.append(signage_item)

        # Add signage addition button
        signage_addition_item = QTreeWidgetItem(["+"])
        signage_items.append(signage_addition_item)
        return signage_items

    def init_ui(self) -> None:
        # Left side of screen
        self._signage_list.setHeaderLabel(self._res['signageListLabel'])
        self._signage_list.addTopLevelItems(self.signage_to_tree_item())
        self._signage_list.expandAll()
        self._signage_list.itemSelectionChanged.connect(
            self.update_ui_component)

        # Buttons
        self._btn_up.clicked.connect(self.move_button_clicked)
        self._btn_down.clicked.connect(self.move_button_clicked)
        # Disable at first
        self._btn_up.setEnabled(False)
        self._btn_down.setEnabled(False)

        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(self._btn_up)
        hbox_buttons.addWidget(self._btn_down)

        vbox_left = QVBoxLayout()
        vbox_left.addWidget(self._signage_list)
        vbox_left.addLayout(hbox_buttons)

        # Right side of screen
        def signage_change_handler(change_type: utils.ChangeType,
                                   sgn_text: str = '') -> None:
            # Get selected signage item
            get_selected = self._signage_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.SAVE:
                    # Update QTreeWidgetItem
                    item.setText(0, sgn_text)
                elif change_type == utils.ChangeType.DELETE:
                    self._signage_list.removeItemWidget(item)

        signage_widget = SignageWidget(self._sgn_mng, signage_change_handler)
        self._widget_idx['signage'] = self._stacked_widget.addWidget(
            signage_widget)

        def frame_change_handler(change_type: utils.ChangeType,
                                 frame_text: str = '') -> None:
            # Get selected signage item
            get_selected = self._signage_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.SAVE:
                    # Update QTreeWidgetItem
                    item.setText(0, "F:" + frame_text)

        frame_widget = FrameWidget(self._tpl_mng, self._obj_mng, self._mtm_mng,
                                   frame_change_handler)
        self._widget_idx['frame'] = self._stacked_widget.addWidget(
            frame_widget)

        def scene_change_handler(change_type: utils.ChangeType,
                                 scene_text: str = '') -> None:
            # Get selected scene item
            get_selected = self._signage_list.selectedItems()
            if get_selected:
                item = get_selected[0]
                if change_type == utils.ChangeType.SAVE:
                    # Update QTreeWidgetItem
                    idx = item.text(0).split(':')[0]
                    item.setText(0, idx + ':' + scene_text)
                elif change_type == utils.ChangeType.DELETE:
                    scene_idx = int(scene_text)
                    parent = item.parent()
                    parent.removeChild(parent.child(parent.childCount() - 1))
                    for i in range(parent.childCount()):
                        if i > scene_idx + 1:
                            it = parent.child(i)
                            it_text = ':'.join(it.text(0).split(':')[1:])
                            it.setText(0, str(i - 1) + ':' + it_text)
                    parent.removeChild(item)
                    parent.addChild(QTreeWidgetItem(['+']))

        scene_widget = SceneWidget(self._tpl_mng, self._obj_mng, self._mtm_mng,
                                   scene_change_handler)
        self._widget_idx['scene'] = self._stacked_widget.addWidget(
            scene_widget)

        # Gather altogether
        hbox_outmost = QHBoxLayout()
        hbox_outmost.addLayout(vbox_left, 1)
        hbox_outmost.addWidget(self._stacked_widget, 5)

        self.setLayout(hbox_outmost)

    # Move given item up or down
    def _move_scene_item(self, selected_item: QTreeWidgetItem,
                         direction: Direction) -> None:
        # UI Modification
        parent = selected_item.parent()  # Signage of selected scene
        sel_idx = int(selected_item.text(0).split(':')[0])

        offset = 0  # Default value
        if direction == Direction.DOWN:
            offset = +1
        elif direction == Direction.UP:
            offset = -1
        else:
            Exception("Direction not provided")  # Should NEVER be reached
        target_item = parent.child(sel_idx + offset)
        target_text = ':'.join(target_item.text(0).split(':')[1:])
        sel_text = ':'.join(selected_item.text(0).split(':')[1:])

        parent.child(sel_idx).setText(0,
                                      str(sel_idx + offset) + ':' + sel_text)
        parent.removeChild(parent.child(sel_idx + offset))
        moved_item = QTreeWidgetItem([str(sel_idx) + ':' + target_text])
        parent.insertChild(sel_idx, moved_item)

        # Data Modification
        sgn_id = utils.ui_text_to_id(parent.text(0))
        signage = self._sgn_mng.get_signage(sgn_id)
        signage.rearrange_scene(sel_idx - 1, sel_idx - 1 +
                                offset)  # Index starts from 0 here

        # Update UI status
        self.update_ui_component()

    def move_button_clicked(self) -> None:
        button_text = self.sender().text()
        get_selected = self._signage_list.selectedItems()
        if get_selected:
            item = get_selected[0]
            if button_text == self._res['upButtonText']:
                # Up button clicked. Guaranteed it can be moved up
                self._move_scene_item(item, Direction.UP)
            elif button_text == self._res['downButtonText']:
                # Down button clicked. Guaranteed it can be moved down
                self._move_scene_item(item, Direction.DOWN)

    def update_ui_component(self) -> None:
        get_selected = self._signage_list.selectedItems()
        if get_selected:
            item = get_selected[0]
            item_text = item.text(0)
            if item.parent() is None:
                # Selected one is at topmost level
                # Signage cannot move up or down, so disable UP/DOWN button
                self._btn_up.setEnabled(False)
                self._btn_up.setEnabled(False)
                if item_text == "+":
                    item.setSelected(False)
                    text, ok = QInputDialog.getText(
                        self, self._res['idDialogTitle'],
                        self._res['idDialogDescription'])
                    if ok:
                        try:
                            utils.validate_id(text)
                        except AttributeError:
                            QMessageBox.warning(
                                self, self._res['idInvalidTitle'],
                                self._res['idInvalidDescription'],
                                QMessageBox.Ok, QMessageBox.Ok)
                            return  # Invalid ID. Do not create signage
                        initial_scene = self._create_scene()
                        initial_frame = self._create_frame()
                        new_signage = Signage(text,
                                              frame=initial_frame,
                                              scenes=[initial_scene])
                        self._sgn_mng.add_signage(
                            new_signage)  # Add new signage

                        # Add to UI
                        signage_text = utils.gen_ui_text(
                            new_signage.title, new_signage.id)
                        item.setText(0, signage_text)

                        frame_tpl = initial_frame.template
                        frame_text = utils.gen_ui_text(
                            frame_tpl.definition.name, frame_tpl.id)
                        frame_item = QTreeWidgetItem(["F:" + frame_text])
                        item.addChild(frame_item)

                        scene_tpl = initial_scene.template
                        scene_text = utils.gen_ui_text(
                            scene_tpl.definition.name, scene_tpl.id)
                        scene_item = QTreeWidgetItem(["1:" + scene_text])
                        item.addChild(scene_item)

                        item.addChild(QTreeWidgetItem(['+']))
                        item.setExpanded(True)
                        # Add + button again
                        self._signage_list.addTopLevelItem(
                            QTreeWidgetItem(['+']))
                else:
                    # Selected one is signage
                    sgn_id = utils.ui_text_to_id(item_text)
                    signage = self._sgn_mng.get_signage(sgn_id)
                    idx = self._widget_idx['signage']
                    self._stacked_widget.widget(idx).load_data_on_ui(signage)
                    self._stacked_widget.setCurrentIndex(idx)
            else:
                sgn_id = utils.ui_text_to_id(item.parent().text(0))
                signage = self._sgn_mng.get_signage(sgn_id)
                if item_text.startswith("F:"):
                    # Selected one is frame
                    # Frame cannot move up or down, so disable UP/DOWN button
                    self._btn_up.setEnabled(False)
                    self._btn_down.setEnabled(False)

                    idx = self._widget_idx['frame']
                    frame = signage.frame
                    self._stacked_widget.widget(idx).load_data_on_ui(frame)
                    self._stacked_widget.setCurrentIndex(idx)
                elif item_text == '+':
                    # Add scene to signage
                    new_scene = self._create_scene()
                    signage.add_scene(new_scene)

                    # Add scene to list on UI
                    # Make current item's text as added scene
                    parent = item.parent()
                    num_child = parent.childCount()
                    scene_tpl = new_scene.template
                    scene_text = utils.gen_ui_text(scene_tpl.definition.name,
                                                   scene_tpl.id)
                    item.setText(0, str(num_child - 1) + ":" + scene_text)
                    # Add + button at last
                    parent.addChild(QTreeWidgetItem(['+']))

                    self.update_ui_component()  # Update UI status
                else:
                    # Selected one is scene
                    scene_idx = int(item_text.split(':')[0])
                    # First, scene can be moved
                    self._btn_up.setEnabled(True)
                    self._btn_down.setEnabled(True)
                    if scene_idx == 1:
                        # Scene at top. Cannot move up
                        self._btn_up.setEnabled(False)
                    if scene_idx == len(signage.scenes):
                        # Scene at bottom. Cannot move down
                        self._btn_down.setEnabled(False)
                    idx = self._widget_idx['scene']
                    self._stacked_widget.widget(idx).load_data_on_ui(
                        signage, scene_idx - 1)
                    self._stacked_widget.setCurrentIndex(idx)

    def _create_scene(self) -> Scene:
        scene_tpls = list(self._tpl_mng.scene_templates.keys())
        initial_tpl_id = random.choice(scene_tpls)
        # Default template
        initial_tpl = self._tpl_mng.get_scene_template(initial_tpl_id)

        # Default values
        obj_value = ObjectValue(None, initial_tpl.definition, self._obj_mng,
                                self._mtm_mng)

        return Scene(initial_tpl, obj_value)

    def _create_frame(self) -> Frame:
        frame_tpls = list(self._tpl_mng.frame_templates.keys())
        initial_tpl_id = random.choice(frame_tpls)
        # Default template
        initial_tpl = self._tpl_mng.get_frame_template(initial_tpl_id)

        # Default values
        obj_value = ObjectValue(None, initial_tpl.definition, self._obj_mng,
                                self._mtm_mng)

        return Frame(initial_tpl, obj_value)