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