Exemplo n.º 1
0
    def __init__(self, view_type=None, parent=None):
        super(MItemViewSet, self).__init__(parent)
        self.main_lay = QtWidgets.QVBoxLayout()
        self.main_lay.setSpacing(5)
        self.main_lay.setContentsMargins(0, 0, 0, 0)

        self.sort_filter_model = MSortFilterModel()
        self.source_model = MTableModel()
        self.sort_filter_model.setSourceModel(self.source_model)
        view_class = view_type or MItemViewSet.TableViewType
        self.item_view = view_class()
        self.item_view.doubleClicked.connect(self.sig_double_clicked)
        self.item_view.pressed.connect(self.slot_left_clicked)
        self.item_view.setModel(self.sort_filter_model)

        self._search_line_edit = MLineEdit().search().small()
        self._search_attr_button = (
            MToolButton().icon_only().svg("down_fill.svg").small())
        self._search_line_edit.set_prefix_widget(self._search_attr_button)
        self._search_line_edit.textChanged.connect(
            self.sort_filter_model.set_search_pattern)
        self._search_line_edit.setVisible(False)
        self._search_lay = QtWidgets.QHBoxLayout()
        self._search_lay.setContentsMargins(0, 0, 0, 0)
        self._search_lay.addStretch()
        self._search_lay.addWidget(self._search_line_edit)

        self.main_lay.addLayout(self._search_lay)
        self.main_lay.addWidget(self.item_view)
        self.setLayout(self.main_lay)
    def __init__(self, parent=None):
        super(SocketTool, self).__init__(parent=parent)
        ui_file = os.path.join(__file__, "..", "skeletal_socket_tool.ui")
        load_ui(ui_file, self)

        self.model = MTableModel()
        header_list = [u"骨骼", u"插槽"]
        self.header_list = [
            {
                "label": data,
                "key": data,
                "editable": True,
                "selectable": True,
                "width": 400,
            }
            for data in header_list
        ]
        self.model.set_header_list(self.header_list)

        self.model_sort = MSortFilterModel()
        self.model_sort.setSourceModel(self.model)

        self.Table_View.setShowGrid(True)
        self.Table_View.setModel(self.model_sort)
        header = self.Table_View.horizontalHeader()
        header.setStretchLastSection(True)

        self.popMenu = QtWidgets.QMenu(self)
        action = QtWidgets.QAction(u"删除选择", self)
        action.triggered.connect(self.remove_line)
        self.popMenu.addAction(action)
        action = QtWidgets.QAction(u"添加一行", self)
        action.triggered.connect(self.add_line)
        self.popMenu.addAction(action)

        self.Table_View.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.Table_View.customContextMenuRequested.connect(self.on_context_menu)

        self.Close_Action.triggered.connect(self.close)
        self.Export_CSV_Action.triggered.connect(self.export_csv)
        self.Import_CSV_Action.triggered.connect(self.import_csv)
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(
                "http://wiki.l0v0.com/unreal/PyToolkit/#/anim/2_skeletal_socket_tool"
            )
        )
        self.Socket_BTN.clicked.connect(self.add_socket)
        self.Clear_BTN.clicked.connect(self.clear_socket)

        # NOTE 读取 csv
        self.settings = QtCore.QSettings(
            "%s.ini" % self.__class__.__name__, QtCore.QSettings.IniFormat
        )
        csv_file = self.settings.value("csv_file")
        # csv_file = os.path.join(__file__,"..","test.csv")
        if csv_file and os.path.exists(csv_file):
            self.handle_csv(csv_file)
Exemplo n.º 3
0
    def _init_ui(self):
        table_small = MListView(size=dayu_theme.small)
        table_default = MListView()
        table_large = MListView(size=dayu_theme.large)

        model_1 = MTableModel()
        model_1.set_header_list(mock.header_list)
        model_sort = MSortFilterModel()
        model_sort.setSourceModel(model_1)
        table_small.setModel(model_sort)
        table_default.setModel(model_sort)
        table_large.setModel(model_sort)
        model_sort.set_header_list(mock.header_list)
        table_small.set_header_list(mock.header_list)
        table_default.set_header_list(mock.header_list)
        table_large.set_header_list(mock.header_list)
        model_1.set_data_list(mock.data_list)

        line_edit = MLineEdit().search().small()
        line_edit.textChanged.connect(model_sort.set_search_pattern)

        main_lay = QVBoxLayout()
        main_lay.addWidget(line_edit)
        main_lay.addWidget(MDivider('Small Size'))
        main_lay.addWidget(table_small)
        main_lay.addWidget(MDivider('Default Size'))
        main_lay.addWidget(table_default)
        main_lay.addWidget(MDivider('Large Size'))
        main_lay.addWidget(table_large)
        main_lay.addStretch()
        self.setLayout(main_lay)
Exemplo n.º 4
0
    def _init_ui(self):
        model_1 = MTableModel()
        model_1.set_header_list(mock.header_list)
        model_sort = MSortFilterModel()
        model_sort.setSourceModel(model_1)

        tree_view = MTreeView()
        tree_view.setModel(model_sort)

        model_sort.set_header_list(mock.header_list)
        tree_view.set_header_list(mock.header_list)
        model_1.set_data_list(mock.tree_data_list)

        line_edit = MLineEdit().search().small()
        line_edit.textChanged.connect(model_sort.set_search_pattern)

        expand_all_button = MPushButton('Expand All').small()
        expand_all_button.clicked.connect(tree_view.expandAll)
        collapse_all_button = MPushButton('Collapse All').small()
        collapse_all_button.clicked.connect(tree_view.collapseAll)
        button_lay = QHBoxLayout()
        button_lay.addWidget(expand_all_button)
        button_lay.addWidget(collapse_all_button)
        button_lay.addWidget(line_edit)
        button_lay.addStretch()

        main_lay = QVBoxLayout()
        main_lay.addLayout(button_lay)
        main_lay.addWidget(tree_view)
        main_lay.addStretch()
        self.setLayout(main_lay)
Exemplo n.º 5
0
    def _init_ui(self):
        model_1 = MTableModel()
        model_1.set_header_list(mock.header_list)
        model_sort = MSortFilterModel()
        model_sort.setSourceModel(model_1)

        table_small = MTableView(size=dayu_theme.small, show_row_count=True)
        table_grid = MTableView(size=dayu_theme.small, show_row_count=True)
        table_grid.setShowGrid(True)
        table_default = MTableView(size=dayu_theme.medium, show_row_count=True)
        thread = MFetchDataThread(self)

        self.loading_wrapper = MLoadingWrapper(widget=table_default,
                                               loading=False)
        thread.started.connect(
            functools.partial(self.loading_wrapper.set_dayu_loading, True))
        thread.finished.connect(
            functools.partial(self.loading_wrapper.set_dayu_loading, False))
        thread.finished.connect(
            functools.partial(table_default.setModel, model_sort))
        button = MPushButton(text="Get Data: 4s")
        button.clicked.connect(thread.start)
        switch_lay = QtWidgets.QHBoxLayout()
        switch_lay.addWidget(button)
        switch_lay.addStretch()
        table_large = MTableView(size=dayu_theme.large, show_row_count=False)

        table_small.setModel(model_sort)
        table_grid.setModel(model_sort)
        table_large.setModel(model_sort)
        model_sort.set_header_list(mock.header_list)
        table_small.set_header_list(mock.header_list)
        table_grid.set_header_list(mock.header_list)
        table_default.set_header_list(mock.header_list)
        table_large.set_header_list(mock.header_list)
        model_1.set_data_list(mock.data_list)

        line_edit = MLineEdit().search().small()
        line_edit.textChanged.connect(model_sort.set_search_pattern)

        main_lay = QtWidgets.QVBoxLayout()
        main_lay.addWidget(line_edit)
        main_lay.addWidget(MDivider("Small Size"))
        main_lay.addWidget(table_small)
        main_lay.addWidget(MDivider("Default Size"))
        main_lay.addLayout(switch_lay)
        main_lay.addWidget(self.loading_wrapper)
        main_lay.addWidget(MDivider("Large Size (Hide Row Count)"))
        main_lay.addWidget(table_large)
        main_lay.addWidget(MDivider("With Grid"))
        main_lay.addWidget(table_grid)
        main_lay.addStretch()
        main_lay.addWidget(
            MAlert('Simply use "MItemViewSet" or "MItemViewFullSet"'))
        self.setLayout(main_lay)
    def _init_ui(self):
        model_1 = MTableModel()
        model_1.set_header_list(header_list)
        model_sort = MSortFilterModel()
        model_sort.setSourceModel(model_1)

        table_grid = MTableView(size=dayu_theme.small, show_row_count=True)
        table_grid.setShowGrid(True)
        table_grid.setModel(model_sort)
        model_sort.set_header_list(header_list)
        table_grid.set_header_list(header_list)
        button_delegate = MPushButtonDelegate(parent=self)
        table_grid.setItemDelegateForColumn(4, button_delegate)
        button_delegate.sig_clicked.connect(self.slot_cell_clicked)
        model_1.set_data_list([
            {
                "name": "John Brown",
                "sex": "Male",
                "sex_list": ["Male", "Female"],
                "age": 18,
                "score": 89,
                "city": "New York",
                "city_list": ["New York", "Ottawa", "London", "Sydney"],
                "date": "2016-10-03",
            },
            {
                "name": "Jim Green",
                "sex": "Male",
                "sex_list": ["Male", "Female"],
                "age": 24,
                "score": 55,
                "city": "London",
                "city_list": ["New York", "Ottawa", "London", "Sydney"],
                "date": "2016-10-01",
            },
        ])

        main_lay = QtWidgets.QVBoxLayout()
        main_lay.addWidget(table_grid)
        self.setLayout(main_lay)
class SocketTool(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(SocketTool, self).__init__(parent=parent)
        ui_file = os.path.join(__file__, "..", "skeletal_socket_tool.ui")
        load_ui(ui_file, self)

        self.model = MTableModel()
        header_list = [u"骨骼", u"插槽"]
        self.header_list = [
            {
                "label": data,
                "key": data,
                "editable": True,
                "selectable": True,
                "width": 400,
            }
            for data in header_list
        ]
        self.model.set_header_list(self.header_list)

        self.model_sort = MSortFilterModel()
        self.model_sort.setSourceModel(self.model)

        self.Table_View.setShowGrid(True)
        self.Table_View.setModel(self.model_sort)
        header = self.Table_View.horizontalHeader()
        header.setStretchLastSection(True)

        self.popMenu = QtWidgets.QMenu(self)
        action = QtWidgets.QAction(u"删除选择", self)
        action.triggered.connect(self.remove_line)
        self.popMenu.addAction(action)
        action = QtWidgets.QAction(u"添加一行", self)
        action.triggered.connect(self.add_line)
        self.popMenu.addAction(action)

        self.Table_View.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.Table_View.customContextMenuRequested.connect(self.on_context_menu)

        self.Close_Action.triggered.connect(self.close)
        self.Export_CSV_Action.triggered.connect(self.export_csv)
        self.Import_CSV_Action.triggered.connect(self.import_csv)
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(
                "http://wiki.l0v0.com/unreal/PyToolkit/#/anim/2_skeletal_socket_tool"
            )
        )
        self.Socket_BTN.clicked.connect(self.add_socket)
        self.Clear_BTN.clicked.connect(self.clear_socket)

        # NOTE 读取 csv
        self.settings = QtCore.QSettings(
            "%s.ini" % self.__class__.__name__, QtCore.QSettings.IniFormat
        )
        csv_file = self.settings.value("csv_file")
        # csv_file = os.path.join(__file__,"..","test.csv")
        if csv_file and os.path.exists(csv_file):
            self.handle_csv(csv_file)

    def get_data_list(self):
        row_count = self.model.rowCount()
        column_count = self.model.columnCount()
        return [
            {
                self.header_list[j]["key"]: self.model.itemData(self.model.index(i, j))[
                    0
                ]
                for j in range(column_count)
            }
            for i in range(row_count)
        ]

    def remove_line(self):

        data_list = self.get_data_list()
        indexes = self.Table_View.selectionModel().selectedRows()
        for index in sorted(indexes, reverse=True):
            data_list.pop(index.row())
        self.model.set_data_list(data_list)

    def add_line(self):
        data_list = self.get_data_list()
        column_count = self.model.columnCount()
        data_list.append({self.header_list[j]["key"]: "" for j in range(column_count)})
        self.model.set_data_list(data_list)

    def on_context_menu(self, point):
        x = point.x()
        y = point.y()
        self.popMenu.exec_(self.Table_View.mapToGlobal(QtCore.QPoint(x, y + 35)))

    def import_csv(self, path=None):
        if not path:
            path, _ = QFileDialog.getOpenFileName(
                self, caption=u"获取设置", filter=u"csv (*.csv)"
            )
        # NOTE 如果文件不存在则返回空
        if not path or not os.path.exists(path):
            return

        self.handle_csv(path)
        self.settings.setValue("csv_file", path)

    def export_csv(self, path=None):
        if not path:
            path, _ = QFileDialog.getSaveFileName(
                self, caption=u"输出设置", filter=u"csv (*.csv)"
            )
            if not path:
                return

        csv = ""
        row_count = self.model.rowCount()
        column_count = self.model.columnCount()
        for i in range(row_count):
            col_list = [
                self.model.itemData(self.model.index(i, j))[0]
                for j in range(column_count)
            ]
            csv += ",".join(col_list) + "\n"

        with open(path, "w", encoding="gbk") as f:
            f.write(csv)

        # NOTE 打开输出的路径
        subprocess.Popen(
            ["start", "", os.path.dirname(path)], creationflags=0x08000000, shell=True
        )

    def handle_csv(self, csv_path):
        data_list = []
        with open(csv_path, "r") as f:
            reader = csv.reader(f)
            for i, row in enumerate(reader):
                data_list.append(
                    {
                        self.header_list[j]["key"]: r.decode("gbk")
                        for j, r in enumerate(row)
                    }
                )
        self.model.set_data_list(data_list)
        self.Table_View.resizeColumnToContents(1)

    def get_selectal_mesh(self):
        skel_mesh_list = [
            a
            for a in unreal.EditorUtilityLibrary.get_selected_assets()
            if isinstance(a, unreal.SkeletalMesh)
        ]
        if not skel_mesh_list:
            msg = u"请选择一个 Skeletal Mesh 物体"
            alert(msg)
            return []
        return skel_mesh_list

    def clear_socket(self):
        for skel_mesh in self.get_selectal_mesh():
            skeleton = skel_mesh.get_editor_property("skeleton")
            # NOTE 删除所有的 socket
            socket_list = [
                skel_mesh.get_socket_by_index(i) for i in range(skel_mesh.num_sockets())
            ]
            py_lib.delete_skeletal_mesh_socket(skeleton, socket_list)

    def add_socket(self):

        # NOTE 读取 Table View
        row_count = self.model.rowCount()
        itemData = self.model.itemData
        index = self.model.index
        data_dict = {
            itemData(index(i, 0))[0]: itemData(index(i, 1))[0] for i in range(row_count)
        }

        if not data_dict:
            msg = u"当前配置数据为空请导入一个 CSV"
            alert(msg)
            return

        fail_list = {}
        add_socket = py_lib.add_skeletal_mesh_socket
        bone_num = py_lib.get_skeleton_bone_num
        bone_name = py_lib.get_skeleton_bone_name
        for skel_mesh in self.get_selectal_mesh():

            skeleton = skel_mesh.get_editor_property("skeleton")
            bone_list = [bone_name(skeleton, i) for i in range(bone_num(skeleton))]

            for bone, socket in data_dict.items():
                # NOTE 检查骨骼名称是否存在
                if not socket or skel_mesh.find_socket(socket):
                    continue
                elif bone not in bone_list:
                    skel_name = skel_mesh.get_name()
                    if skel_name not in fail_list:
                        fail_list[skel_name] = []
                    fail_list[skel_name].append(bone)
                    continue

                _socket = add_socket(skeleton, bone).socket_name
                if _socket != socket:
                    skel_mesh.rename_socket(_socket, socket)

        if fail_list:
            msg = u"以下骨骼名称没有在选中的角色找到↓↓↓\n\n"
            for skel_name, bone_list in fail_list.items():
                msg += u"%s:\n%s" % (skel_name, "\n".join(bone_list))

            alert(msg)
Exemplo n.º 8
0
    def __init__(self, parent=None):
        super(RenamerWinBase, self).__init__(parent)
        DIR, file_name = os.path.split(__file__)
        file_name = os.path.splitext(file_name)[0]
        load_ui(os.path.join(DIR, "%s.ui" % file_name), self)

        # NOTE 获取 convention 数据
        self.conventions = get_convention()

        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        # NOTE 配置 MComboBox
        self.Search_LE.setPlaceholderText(u"输入关键字")
        self.Replace_LE.setPlaceholderText(u"输入替换文字")
        self.Export_Setting_Action.triggered.connect(self.export_setting)
        self.Import_Setting_Action.triggered.connect(self.import_setting)
        self.Help_Action.triggered.connect(lambda: webbrowser.open_new_tab(
            'http://wiki.l0v0.com/unreal/PyToolkit/#/msic/2_renamer'))
        self.Convention_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(
                'https://github.com/Allar/ue4-style-guide'))

        # NOTE 隐藏左侧配置项
        self.Splitter.splitterMoved.connect(lambda: self.settings.setValue(
            "splitter_size", self.Splitter.sizes()))
        splitter_size = self.settings.value("splitter_size")
        self.Splitter.setSizes(
            [int(i) for i in splitter_size] if splitter_size else [0, 1])

        # NOTE 配置 Header
        self.model = MTableModel()
        self.header_list = [{
            'label':
            data,
            'key':
            data,
            'bg_color':
            lambda x, y: y.get('bg_color', QtGui.QColor('transparent')),
            'tooltip':
            lambda x, y: y.get('asset').get_path_name(),
            'edit':
            lambda x, y: x or y.get('asset').get_name(),
            'display':
            lambda x, y: x or y.get('asset').get_name(),
            'editable':
            i == 1,
            'draggable':
            True,
            'width':
            100,
        } for i, data in enumerate([u"原名称", u"新名称", u"文件类型"])]
        self.model.set_header_list(self.header_list)
        self.model_sort = MSortFilterModel()
        self.model_sort.setSourceModel(self.model)

        self.Table_View.setModel(self.model_sort)
        self.Table_View.setShowGrid(True)
        header = self.Table_View.horizontalHeader()
        header.setStretchLastSection(True)

        self.setAcceptDrops(True)
        self.load_settings()
Exemplo n.º 9
0
class RenamerWinBase(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(RenamerWinBase, self).__init__(parent)
        DIR, file_name = os.path.split(__file__)
        file_name = os.path.splitext(file_name)[0]
        load_ui(os.path.join(DIR, "%s.ui" % file_name), self)

        # NOTE 获取 convention 数据
        self.conventions = get_convention()

        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        # NOTE 配置 MComboBox
        self.Search_LE.setPlaceholderText(u"输入关键字")
        self.Replace_LE.setPlaceholderText(u"输入替换文字")
        self.Export_Setting_Action.triggered.connect(self.export_setting)
        self.Import_Setting_Action.triggered.connect(self.import_setting)
        self.Help_Action.triggered.connect(lambda: webbrowser.open_new_tab(
            'http://wiki.l0v0.com/unreal/PyToolkit/#/msic/2_renamer'))
        self.Convention_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(
                'https://github.com/Allar/ue4-style-guide'))

        # NOTE 隐藏左侧配置项
        self.Splitter.splitterMoved.connect(lambda: self.settings.setValue(
            "splitter_size", self.Splitter.sizes()))
        splitter_size = self.settings.value("splitter_size")
        self.Splitter.setSizes(
            [int(i) for i in splitter_size] if splitter_size else [0, 1])

        # NOTE 配置 Header
        self.model = MTableModel()
        self.header_list = [{
            'label':
            data,
            'key':
            data,
            'bg_color':
            lambda x, y: y.get('bg_color', QtGui.QColor('transparent')),
            'tooltip':
            lambda x, y: y.get('asset').get_path_name(),
            'edit':
            lambda x, y: x or y.get('asset').get_name(),
            'display':
            lambda x, y: x or y.get('asset').get_name(),
            'editable':
            i == 1,
            'draggable':
            True,
            'width':
            100,
        } for i, data in enumerate([u"原名称", u"新名称", u"文件类型"])]
        self.model.set_header_list(self.header_list)
        self.model_sort = MSortFilterModel()
        self.model_sort.setSourceModel(self.model)

        self.Table_View.setModel(self.model_sort)
        self.Table_View.setShowGrid(True)
        header = self.Table_View.horizontalHeader()
        header.setStretchLastSection(True)

        self.setAcceptDrops(True)
        self.load_settings()

    def export_setting(self):
        path, _ = QFileDialog.getSaveFileName(self,
                                              caption=u"输出设置",
                                              filter=u"ini (*.ini)")
        if not path:
            return
        copyfile(self.settings.fileName(), path)
        toast(u"导出成功", "info")

    def import_setting(self):
        path, _ = QFileDialog.getOpenFileName(self,
                                              caption=u"获取设置",
                                              filter=u"ini (*.ini)")
        # NOTE 如果文件不存在则返回空
        if not path or not os.path.exists(path):
            return

        self.settings = QtCore.QSettings(path, QtCore.QSettings.IniFormat)
        self.settings.sync()
        self.load_settings()
        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        self.save_settings()
        toast(u"加载成功", "info")

    @staticmethod
    def check_loop(loop_list):
        for w in loop_list:
            name = w.objectName()
            if not hasattr(w, "objectName") or not name:
                continue
            elif isinstance(w,
                            QtWidgets.QLineEdit) and not name.endswith("_LE"):
                continue
            elif isinstance(w,
                            QtWidgets.QSpinBox) and not name.endswith("_SP"):
                continue
            elif isinstance(w,
                            QtWidgets.QCheckBox) and not name.endswith("_CB"):
                continue
            elif isinstance(
                    w, QtWidgets.QRadioButton) and not name.endswith("_RB"):
                continue
            elif isinstance(
                    w, QtWidgets.QComboBox) and not name.endswith("_Combo"):
                continue
            yield w

    def save_settings(self):
        CB_list = self.findChildren(QtWidgets.QCheckBox)
        RB_list = self.findChildren(QtWidgets.QRadioButton)
        LE_list = self.findChildren(QtWidgets.QLineEdit)
        SP_list = self.findChildren(QtWidgets.QSpinBox)
        Combo_list = self.findChildren(QtWidgets.QComboBox)

        for B in self.check_loop(CB_list + RB_list):
            self.settings.setValue(B.objectName(), B.isChecked())

        for LE in self.check_loop(LE_list):
            self.settings.setValue(LE.objectName(), LE.text())

        for SP in self.check_loop(SP_list):
            self.settings.setValue(SP.objectName(), SP.value())

        for Combo in self.check_loop(Combo_list):
            index = Combo.currentIndex()
            self.settings.setValue(Combo.objectName(), index)

        # NOTE 获取 Table 记录
        data_list = self.model.get_data_list()
        asset_data = [data.get("asset").get_path_name() for data in data_list]
        self.settings.setValue("table_asset", asset_data)

    def load_settings(self):
        CB_list = self.findChildren(QtWidgets.QCheckBox)
        RB_list = self.findChildren(QtWidgets.QRadioButton)
        LE_list = self.findChildren(QtWidgets.QLineEdit)
        SP_list = self.findChildren(QtWidgets.QSpinBox)
        Combo_list = self.findChildren(QtWidgets.QComboBox)
        widget_dict = {}
        for B in self.check_loop(CB_list + RB_list):
            val = self.settings.value(B.objectName())
            if val is not None:
                val = True if str(val).lower() == "true" else False
                widget_dict[B.setChecked] = val

        for LE in self.check_loop(LE_list):
            val = self.settings.value(LE.objectName())
            if val is not None:
                widget_dict[LE.setText] = val

        for SP in self.check_loop(SP_list):
            val = self.settings.value(SP.objectName())
            if val is not None:
                widget_dict[SP.setValue] = int(val)

        for Combo in self.check_loop(Combo_list):
            val = self.settings.value(Combo.objectName())
            if val is not None:
                widget_dict[Combo.setCurrentIndex] = int(val)

        # NOTE 添加 data_list
        asset_data = self.settings.value("table_asset")
        # NOTE 批量设置属性值
        for setter, val in widget_dict.items():
            setter(val)
        if not asset_data:
            return

        actor_list = []
        asset_list = []
        for path in asset_data:
            asset = unreal.load_object(None, path)
            if isinstance(asset, unreal.Actor):
                actor_list.append(asset)
            elif asset_lib.does_asset_exist(path):
                asset_list.append(asset)

        data_list = self.model.get_data_list()

        data_list.extend([{
            'bg_color': QtGui.QColor("transparent"),
            'asset': asset,
            u"原名称": asset.get_name(),
            u"新名称": "",
            u"文件类型": type(asset).__name__,
        } for asset in asset_list])

        data_list.extend([{
            'bg_color': QtGui.QColor("transparent"),
            'asset': actor,
            u"原名称": actor.get_actor_label(),
            u"新名称": "",
            u"文件类型": type(actor).__name__,
        } for actor in actor_list])

        self.model.set_data_list(data_list)
        self.update_table()

    def dragEnterEvent(self, event):
        event.accept() if event.mimeData().hasUrls() else event.ignore()

    def dropEvent(self, event):
        # Note 获取拖拽文件的地址
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            print(event.mimeData().urls())
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def show(self):
        super(RenamerWinBase, self).show()

        # NOTE 配置 QGroupBox 的样式
        self.setStyleSheet(self.styleSheet() + """
        QGroupBox{
            border: 0.5px solid black;
            padding-top:10px;
        }
        """)

    def update_table(self):
        raise NotImplementedError
Exemplo n.º 10
0
class MItemViewSet(QtWidgets.QWidget):
    sig_double_clicked = QtCore.Signal(QtCore.QModelIndex)
    sig_left_clicked = QtCore.Signal(QtCore.QModelIndex)
    TableViewType = MTableView
    BigViewType = MBigView
    TreeViewType = MTreeView
    ListViewType = MListView

    def __init__(self, view_type=None, parent=None):
        super(MItemViewSet, self).__init__(parent)
        self.main_lay = QtWidgets.QVBoxLayout()
        self.main_lay.setSpacing(5)
        self.main_lay.setContentsMargins(0, 0, 0, 0)

        self.sort_filter_model = MSortFilterModel()
        self.source_model = MTableModel()
        self.sort_filter_model.setSourceModel(self.source_model)
        view_class = view_type or MItemViewSet.TableViewType
        self.item_view = view_class()
        self.item_view.doubleClicked.connect(self.sig_double_clicked)
        self.item_view.pressed.connect(self.slot_left_clicked)
        self.item_view.setModel(self.sort_filter_model)

        self._search_line_edit = MLineEdit().search().small()
        self._search_attr_button = (
            MToolButton().icon_only().svg("down_fill.svg").small())
        self._search_line_edit.set_prefix_widget(self._search_attr_button)
        self._search_line_edit.textChanged.connect(
            self.sort_filter_model.set_search_pattern)
        self._search_line_edit.setVisible(False)
        self._search_lay = QtWidgets.QHBoxLayout()
        self._search_lay.setContentsMargins(0, 0, 0, 0)
        self._search_lay.addStretch()
        self._search_lay.addWidget(self._search_line_edit)

        self.main_lay.addLayout(self._search_lay)
        self.main_lay.addWidget(self.item_view)
        self.setLayout(self.main_lay)

    @QtCore.Slot(QtCore.QModelIndex)
    def slot_left_clicked(self, start_index):
        button = QtWidgets.QApplication.mouseButtons()
        if button == QtCore.Qt.LeftButton:
            real_index = self.sort_filter_model.mapToSource(start_index)
            self.sig_left_clicked.emit(real_index)

    def set_header_list(self, header_list):
        self.source_model.set_header_list(header_list)
        self.sort_filter_model.set_header_list(header_list)
        self.sort_filter_model.setSourceModel(self.source_model)
        self.item_view.set_header_list(header_list)

    @QtCore.Slot()
    def setup_data(self, data_list):
        self.source_model.clear()
        if data_list:
            self.source_model.set_data_list(data_list)
            self.item_view.set_header_list(self.source_model.header_list)

    def get_data(self):
        return self.source_model.get_data_list()

    def searchable(self):
        """Enable search line edit visible."""
        self._search_line_edit.setVisible(True)
        return self

    def insert_widget(self, widget):
        """Use can insert extra widget into search layout."""
        self._search_lay.insertWidget(0, widget)
    def __init__(self, parent=None):
        super(PropertyTransferTool, self).__init__(parent)
        DIR, file_name = os.path.split(__file__)
        file_name = os.path.splitext(file_name)[0]
        load_ui(os.path.join(DIR, "%s.ui" % file_name), self)

        self.source = None

        help_link = "http://redarttoolkit.pages.oa.com/docs/posts/85c3f876.html"
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(help_link))

        # NOTE 设置按钮图标
        QtCore.QTimer.singleShot(0, self.update_icon)

        menu_callback = lambda: self.Asset_Menu.popup(QtGui.QCursor.pos())
        self.Asset_List.customContextMenuRequested.connect(menu_callback)
        menu_callback = lambda: self.Property_Menu.popup(QtGui.QCursor.pos())
        self.Property_Tree.customContextMenuRequested.connect(menu_callback)

        self.Asset_Label.setVisible(lambda: self.state.lable_visible)
        self.Asset_Label.setText(lambda: self.state.lable_text)

        self.Src_BTN.clicked.connect(self.get_source)
        self.Dst_BTN.clicked.connect(self.get_destination)
        self.Transfer_BTN.clicked.connect(self.transfer_property)

        # NOTE 配置 splitter
        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        self.Splitter.splitterMoved.connect(lambda: self.settings.setValue(
            "splitter_size", self.Splitter.sizes()))
        splitter_size = self.settings.value("splitter_size")
        size = [int(i) for i in splitter_size] if splitter_size else [700, 200]
        self.Splitter.setSizes(size)

        # NOTE 配置搜索栏
        self.Dst_Filter_LE.search()
        self.Dst_Filter_LE.setPlaceholderText("")
        self.Prop_Filter_LE.search()
        self.Prop_Filter_LE.setPlaceholderText("")

        # NOTE 配置 Property_Tree
        self.property_model = MTableModel()
        columns = {u"property": "属性名", u"value": "数值"}
        self.property_header_list = [{
            "label":
            label,
            "key":
            key,
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "checkable":
            i == 0,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        } for i, (key, label) in enumerate(columns.items())]
        self.property_model.set_header_list(self.property_header_list)
        self.property_model_sort = MSortFilterModel()
        self.property_model_sort.set_header_list(self.property_header_list)
        self.property_model_sort.setSourceModel(self.property_model)
        self.Prop_Filter_LE.textChanged.connect(
            self.property_model_sort.set_search_pattern)

        self.Property_Tree.setModel(self.property_model_sort)
        header = self.Property_Tree.header()
        header.setStretchLastSection(True)

        # NOTE 配置 Asset_List
        self.asset_model = MTableModel()
        self.asset_header_list = [{
            "label":
            "destination",
            "key":
            "destination",
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "tooltip":
            lambda x, y: y.get("asset").get_path_name(),
            "checkable":
            True,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        }]
        self.asset_model.set_header_list(self.asset_header_list)
        self.asset_model_sort = MSortFilterModel()
        self.asset_model_sort.search_reg.setPatternSyntax(
            QtCore.QRegExp.RegExp)
        self.asset_model_sort.set_header_list(self.asset_header_list)
        self.asset_model_sort.setSourceModel(self.asset_model)
        self.Dst_Filter_LE.textChanged.connect(
            self.asset_model_sort.set_search_pattern)

        self.Asset_List.setModel(self.asset_model_sort)
        self.Asset_List.selectionModel().selectionChanged.connect(
            self.asset_selection_change)
        self.property_model.dataChanged.connect(self.asset_selection_change)
class PropertyTransferTool(QtWidgets.QWidget, MaterialTransfer, AssetList):

    state = PropertyTransferBinder()

    def update_icon(self):
        data = {
            "Del": {
                "icon": QtWidgets.QStyle.SP_BrowserStop,
                "callback": self.remove_assets,
                "tooltip": u"删除文件",
            },
            "Locate": {
                "icon": QtWidgets.QStyle.SP_FileDialogContentsView,
                "callback": self.locate_asset_location,
                "tooltip": u"定位文件",
            },
            "Drive": {
                "icon": QtWidgets.QStyle.SP_DriveHDIcon,
                "callback": self.locate_file_location,
                "tooltip": u"打开系统目录路径",
            },
            "Expand": {
                "icon": QtWidgets.QStyle.SP_ToolBarVerticalExtensionButton,
                "callback": self.Property_Tree.expandAll,
                "tooltip": u"扩展全部",
            },
            "Collapse": {
                "icon": QtWidgets.QStyle.SP_ToolBarHorizontalExtensionButton,
                "callback": self.Property_Tree.collapseAll,
                "tooltip": u"收缩全部",
            },
            "Source_Locate": {
                "icon":
                QtWidgets.QStyle.SP_FileDialogContentsView,
                "callback":
                lambda: asset_lib.sync_browser_to_objects(
                    [self.source.get_path_name()]),
                "tooltip":
                u"定位复制源",
            },
        }

        widget_dict = {
            "BTN": "clicked",
            "Action": "triggered",
        }

        style = QtWidgets.QApplication.style()
        icon = style.standardIcon(QtWidgets.QStyle.SP_BrowserReload)
        self.setWindowIcon(icon)
        for typ, info in data.items():
            icon = style.standardIcon(info.get("icon"))
            tooltip = info.get("tooltip", "")
            tooltip = '<span style="font-weight:600;">%s</span>' % tooltip
            callback = info.get("callback", lambda *args: None)

            for widget, signal in widget_dict.items():
                widget = "%s_%s" % (typ, widget)
                if hasattr(self, widget):
                    widget = getattr(self, widget)
                    widget.setIcon(icon)
                    getattr(widget, signal).connect(callback)
                    widget.setToolTip(tooltip)
                    widget.setEnabled(lambda: self.state.source_eanble)
                    widget.setEnabled(self.state.source_eanble)
                    # print(widget.isEnabled())

        # QtCore.QTimer.singleShot(0, lambda: self.state.source_eanble >> Set(False))

    def __init__(self, parent=None):
        super(PropertyTransferTool, self).__init__(parent)
        DIR, file_name = os.path.split(__file__)
        file_name = os.path.splitext(file_name)[0]
        load_ui(os.path.join(DIR, "%s.ui" % file_name), self)

        self.source = None

        help_link = "http://redarttoolkit.pages.oa.com/docs/posts/85c3f876.html"
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(help_link))

        # NOTE 设置按钮图标
        QtCore.QTimer.singleShot(0, self.update_icon)

        menu_callback = lambda: self.Asset_Menu.popup(QtGui.QCursor.pos())
        self.Asset_List.customContextMenuRequested.connect(menu_callback)
        menu_callback = lambda: self.Property_Menu.popup(QtGui.QCursor.pos())
        self.Property_Tree.customContextMenuRequested.connect(menu_callback)

        self.Asset_Label.setVisible(lambda: self.state.lable_visible)
        self.Asset_Label.setText(lambda: self.state.lable_text)

        self.Src_BTN.clicked.connect(self.get_source)
        self.Dst_BTN.clicked.connect(self.get_destination)
        self.Transfer_BTN.clicked.connect(self.transfer_property)

        # NOTE 配置 splitter
        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        self.Splitter.splitterMoved.connect(lambda: self.settings.setValue(
            "splitter_size", self.Splitter.sizes()))
        splitter_size = self.settings.value("splitter_size")
        size = [int(i) for i in splitter_size] if splitter_size else [700, 200]
        self.Splitter.setSizes(size)

        # NOTE 配置搜索栏
        self.Dst_Filter_LE.search()
        self.Dst_Filter_LE.setPlaceholderText("")
        self.Prop_Filter_LE.search()
        self.Prop_Filter_LE.setPlaceholderText("")

        # NOTE 配置 Property_Tree
        self.property_model = MTableModel()
        columns = {u"property": "属性名", u"value": "数值"}
        self.property_header_list = [{
            "label":
            label,
            "key":
            key,
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "checkable":
            i == 0,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        } for i, (key, label) in enumerate(columns.items())]
        self.property_model.set_header_list(self.property_header_list)
        self.property_model_sort = MSortFilterModel()
        self.property_model_sort.set_header_list(self.property_header_list)
        self.property_model_sort.setSourceModel(self.property_model)
        self.Prop_Filter_LE.textChanged.connect(
            self.property_model_sort.set_search_pattern)

        self.Property_Tree.setModel(self.property_model_sort)
        header = self.Property_Tree.header()
        header.setStretchLastSection(True)

        # NOTE 配置 Asset_List
        self.asset_model = MTableModel()
        self.asset_header_list = [{
            "label":
            "destination",
            "key":
            "destination",
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "tooltip":
            lambda x, y: y.get("asset").get_path_name(),
            "checkable":
            True,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        }]
        self.asset_model.set_header_list(self.asset_header_list)
        self.asset_model_sort = MSortFilterModel()
        self.asset_model_sort.search_reg.setPatternSyntax(
            QtCore.QRegExp.RegExp)
        self.asset_model_sort.set_header_list(self.asset_header_list)
        self.asset_model_sort.setSourceModel(self.asset_model)
        self.Dst_Filter_LE.textChanged.connect(
            self.asset_model_sort.set_search_pattern)

        self.Asset_List.setModel(self.asset_model_sort)
        self.Asset_List.selectionModel().selectionChanged.connect(
            self.asset_selection_change)
        self.property_model.dataChanged.connect(self.asset_selection_change)

    def get_source(self):
        assets = util_lib.get_selected_assets()
        if len(assets) < 1:
            toast(u"请选择一个资产")
            return

        asset = assets[0]
        self.set_property_tree(asset)
        self.asset_model.set_data_list([])
        self.property_model_sort.sort(0, QtCore.Qt.AscendingOrder)

    def get_destination(self):
        if not self.source:
            toast(u"请拾取复制源")
            return

        data_list = self.asset_model.get_data_list()
        tooltip_list = [
            data.get("asset").get_path_name() for data in data_list
        ]

        assets = [
            asset for asset in util_lib.get_selected_assets()
            if isinstance(asset, type(self.source))
            and asset.get_path_name() not in tooltip_list
        ]

        if not assets:
            toast(u"请选择匹配的资产")
            return

        data_list.extend([
            {
                "bg_color": QtGui.QColor("transparent"),
                "asset": asset,
                "destination": asset.get_name(),
                # NOTE 默认勾选
                "destination_checked": QtCore.Qt.Checked,
            } for asset in assets if not asset is self.source
        ])

        self.asset_model.set_data_list(data_list)

    def asset_selection_change(self, *args):
        model = self.Asset_List.selectionModel()
        indexes = model.selectedRows()
        if not indexes:
            return
        index = indexes[0]
        data_list = self.asset_model.get_data_list()
        data = data_list[index.row()]
        asset = data.get("asset")
        if isinstance(asset, unreal.MaterialInterface):
            self.update_material_property(asset)

    def set_property_tree(self, asset):
        if isinstance(asset, unreal.MaterialInterface):
            data = self.get_material_property(asset)
        elif isinstance(asset, unreal.Blueprint):
            pass
        else:
            toast(u"不支持资产")
            return

        data_list = [{
            "property":
            group,
            "value":
            "",
            "children": [{
                "bg_color": QtGui.QColor("transparent"),
                "property": prop,
                "property_checked": QtCore.Qt.Unchecked,
                "value": value,
            } for prop, value in props.items()],
        } for group, props in data.items()]
        self.property_model.set_data_list(data_list)

        self.source = asset
        self.state.source_eanble = True
        self.state.lable_visible = True
        self.state.lable_text = asset.get_name()

    def transfer_property(self):

        for i, asset in enumerate(self.asset_model.get_data_list()):
            if not asset.get("destination_checked"):
                continue

            # NOTE 选择 Asset_List 的资产 更新颜色
            index = self.asset_model.index(i, 0)
            model = self.Asset_List.selectionModel()
            model.setCurrentIndex(index, model.SelectCurrent)

            property_list = [
                prop.get("property")
                for grp in self.property_model.get_data_list()
                for prop in grp.get("children")
                # NOTE 过滤没有勾选的资源
                if prop.get("bg_color") == QtGui.QColor("green")
                and prop.get("property_checked")
            ]

            # NOTE 传递属性
            asset = asset.get("asset")
            if isinstance(asset, type(self.source)):
                self.transfer_material_property(asset, property_list)
            else:
                raise RuntimeError(u"%s 和复制源类型不匹配" % asset.get_name())

        toast("传递完成", 'success')
Exemplo n.º 13
0
    def __init__(self, table_view=True, big_view=False, parent=None):
        super(MItemViewFullSet, self).__init__(parent)
        self.sort_filter_model = MSortFilterModel()
        self.source_model = MTableModel()
        self.sort_filter_model.setSourceModel(self.source_model)

        self.stack_widget = QtWidgets.QStackedWidget()

        self.view_button_grp = MToolButtonGroup(exclusive=True)
        data_group = []
        if table_view:
            self.table_view = MTableView(show_row_count=True)
            self.table_view.doubleClicked.connect(self.sig_double_clicked)
            self.table_view.pressed.connect(self.slot_left_clicked)
            self.table_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.table_view)
            data_group.append({
                "svg": "table_view.svg",
                "checkable": True,
                "tooltip": "Table View"
            })
        if big_view:
            self.big_view = MBigView()
            self.big_view.doubleClicked.connect(self.sig_double_clicked)
            self.big_view.pressed.connect(self.slot_left_clicked)
            self.big_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.big_view)
            data_group.append({
                "svg": "big_view.svg",
                "checkable": True,
                "tooltip": "Big View"
            })

        # 设置多个view 共享 MItemSelectionModel
        leader_view = self.stack_widget.widget(0)
        self.selection_model = leader_view.selectionModel()
        for index in range(self.stack_widget.count()):
            if index == 0:
                continue
            other_view = self.stack_widget.widget(index)
            other_view.setSelectionModel(self.selection_model)

        self.selection_model.currentChanged.connect(self.sig_current_changed)
        self.selection_model.currentRowChanged.connect(
            self.sig_current_row_changed)
        self.selection_model.currentColumnChanged.connect(
            self.sig_current_column_changed)
        self.selection_model.selectionChanged.connect(
            self.sig_selection_changed)

        self.tool_bar = QtWidgets.QWidget()
        self.top_lay = QtWidgets.QHBoxLayout()
        self.top_lay.setContentsMargins(0, 0, 0, 0)
        if data_group and len(data_group) > 1:
            self.view_button_grp.sig_checked_changed.connect(
                self.stack_widget.setCurrentIndex)
            self.view_button_grp.set_button_list(data_group)
            self.view_button_grp.set_dayu_checked(0)
            self.top_lay.addWidget(self.view_button_grp)
        self.search_line_edit = MLineEdit().search().small()
        self.search_attr_button = MToolButton().icon_only().svg(
            "down_fill.svg").small()
        self.search_line_edit.set_prefix_widget(self.search_attr_button)
        self.search_line_edit.textChanged.connect(
            self.sort_filter_model.set_search_pattern)
        self.search_line_edit.setVisible(False)

        self.top_lay.addStretch()
        self.top_lay.addWidget(self.search_line_edit)
        self.tool_bar.setLayout(self.top_lay)

        self.page_set = MPage()
        self.main_lay = QtWidgets.QVBoxLayout()
        self.main_lay.setSpacing(5)
        self.main_lay.setContentsMargins(0, 0, 0, 0)
        self.main_lay.addWidget(self.tool_bar)
        self.main_lay.addWidget(self.stack_widget)
        self.main_lay.addWidget(self.page_set)
        self.setLayout(self.main_lay)
Exemplo n.º 14
0
class MItemViewFullSet(QtWidgets.QWidget):
    sig_double_clicked = QtCore.Signal(QtCore.QModelIndex)
    sig_left_clicked = QtCore.Signal(QtCore.QModelIndex)
    sig_current_changed = QtCore.Signal(QtCore.QModelIndex, QtCore.QModelIndex)
    sig_current_row_changed = QtCore.Signal(QtCore.QModelIndex,
                                            QtCore.QModelIndex)
    sig_current_column_changed = QtCore.Signal(QtCore.QModelIndex,
                                               QtCore.QModelIndex)
    sig_selection_changed = QtCore.Signal(QtCore.QItemSelection,
                                          QtCore.QItemSelection)
    sig_context_menu = QtCore.Signal(object)

    def __init__(self, table_view=True, big_view=False, parent=None):
        super(MItemViewFullSet, self).__init__(parent)
        self.sort_filter_model = MSortFilterModel()
        self.source_model = MTableModel()
        self.sort_filter_model.setSourceModel(self.source_model)

        self.stack_widget = QtWidgets.QStackedWidget()

        self.view_button_grp = MToolButtonGroup(exclusive=True)
        data_group = []
        if table_view:
            self.table_view = MTableView(show_row_count=True)
            self.table_view.doubleClicked.connect(self.sig_double_clicked)
            self.table_view.pressed.connect(self.slot_left_clicked)
            self.table_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.table_view)
            data_group.append({
                "svg": "table_view.svg",
                "checkable": True,
                "tooltip": "Table View"
            })
        if big_view:
            self.big_view = MBigView()
            self.big_view.doubleClicked.connect(self.sig_double_clicked)
            self.big_view.pressed.connect(self.slot_left_clicked)
            self.big_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.big_view)
            data_group.append({
                "svg": "big_view.svg",
                "checkable": True,
                "tooltip": "Big View"
            })

        # 设置多个view 共享 MItemSelectionModel
        leader_view = self.stack_widget.widget(0)
        self.selection_model = leader_view.selectionModel()
        for index in range(self.stack_widget.count()):
            if index == 0:
                continue
            other_view = self.stack_widget.widget(index)
            other_view.setSelectionModel(self.selection_model)

        self.selection_model.currentChanged.connect(self.sig_current_changed)
        self.selection_model.currentRowChanged.connect(
            self.sig_current_row_changed)
        self.selection_model.currentColumnChanged.connect(
            self.sig_current_column_changed)
        self.selection_model.selectionChanged.connect(
            self.sig_selection_changed)

        self.tool_bar = QtWidgets.QWidget()
        self.top_lay = QtWidgets.QHBoxLayout()
        self.top_lay.setContentsMargins(0, 0, 0, 0)
        if data_group and len(data_group) > 1:
            self.view_button_grp.sig_checked_changed.connect(
                self.stack_widget.setCurrentIndex)
            self.view_button_grp.set_button_list(data_group)
            self.view_button_grp.set_dayu_checked(0)
            self.top_lay.addWidget(self.view_button_grp)
        self.search_line_edit = MLineEdit().search().small()
        self.search_attr_button = MToolButton().icon_only().svg(
            "down_fill.svg").small()
        self.search_line_edit.set_prefix_widget(self.search_attr_button)
        self.search_line_edit.textChanged.connect(
            self.sort_filter_model.set_search_pattern)
        self.search_line_edit.setVisible(False)

        self.top_lay.addStretch()
        self.top_lay.addWidget(self.search_line_edit)
        self.tool_bar.setLayout(self.top_lay)

        self.page_set = MPage()
        self.main_lay = QtWidgets.QVBoxLayout()
        self.main_lay.setSpacing(5)
        self.main_lay.setContentsMargins(0, 0, 0, 0)
        self.main_lay.addWidget(self.tool_bar)
        self.main_lay.addWidget(self.stack_widget)
        self.main_lay.addWidget(self.page_set)
        self.setLayout(self.main_lay)

    def enable_context_menu(self):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.enable_context_menu(True)
            view.sig_context_menu.connect(self.sig_context_menu)

    def set_no_data_text(self, text):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.set_no_data_text(text)

    def set_selection_mode(self, mode):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.setSelectionMode(mode)

    def tool_bar_visible(self, flag):
        self.tool_bar.setVisible(flag)

    @QtCore.Slot(QtCore.QModelIndex)
    def slot_left_clicked(self, start_index):
        button = QtWidgets.QApplication.mouseButtons()
        if button == QtCore.Qt.LeftButton:
            real_index = self.sort_filter_model.mapToSource(start_index)
            self.sig_left_clicked.emit(real_index)

    def set_header_list(self, header_list):
        self.source_model.set_header_list(header_list)
        self.sort_filter_model.set_header_list(header_list)
        self.sort_filter_model.setSourceModel(self.source_model)
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.set_header_list(header_list)

    def tool_bar_append_widget(self, widget):
        self.top_lay.addWidget(widget)

    def tool_bar_insert_widget(self, widget):
        self.top_lay.insertWidget(0, widget)

    @QtCore.Slot()
    def setup_data(self, data_list):
        self.source_model.clear()
        if data_list:
            self.source_model.set_data_list(data_list)
        self.set_record_count(len(data_list))
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.set_header_list(self.source_model.header_list)

    @QtCore.Slot(int)
    def set_record_count(self, total):
        self.page_set.set_total(total)

    def get_data(self):
        return self.source_model.get_data_list()

    def searchable(self):
        """Enable search line edit visible."""
        self.search_line_edit.setVisible(True)
        return self