class DialSetupTab(QtWidgets.QDialog, Ui_DialogSetupProject): header1, header2 = range(2) def __init__(self): super(DialSetupTab, self).__init__() self.setupUi(self) self.settings = Setup() self.data = TableProgram() # All Tab setup, options are split inside many function # Tab Project setup --------------------------------------------------- # Defined data needed ----------------------------------------------- self.list_levels = QtGui.QStandardItemModel() # Write all Slot and Connect ---------------------------------------- self.field_setup() self.tab_project_setup() # Tab Network Setup --------------------------------------------------- # self.tab_network() # Tab Source Control Setup -------------------------------------------- self.tab_source_control() # Setups Buttons box_btn = QtWidgets.QDialogButtonBox btn = self.buttonBox.button btn(box_btn.RestoreDefaults).clicked.connect(self.btn_restore) btn(box_btn.Save).clicked.connect(self.save_tab) btn(box_btn.Open).clicked.connect(load_generic) btn(box_btn.Close).clicked.connect(self.close) # TODO Disable the Network tab settings self.tabWidget.setTabEnabled(2, False) # Ui Functions ------------------------------------------------------------ # Tab Project setup ----------------------------------------------------- def tab_project_setup(self, index=None, value=str): """ Generate the Tab Setup, include the Paths field and the Tree Levels with all editable data. It's only a function to add the slot and signal inside the Ui. :param index: None by default, this value give a Int to choice the field used, Unreal Editor (1) or the Project field (2). :param value: String data, it a simple information to send it a field :return: """ if index: if index == 1: self.ue4_path_text.setText(value) elif index == 2: self.project_file_text.setText(value) elif index == 3: value = self.sub_folder_text.text() self.sub_folder_text.setText(value) elif self.settings.last_job_run(): db = self.data.select_paths() self.ue4_path_text.setText(db[0][1]) self.project_file_text.setText(db[0][2]) self.sub_folder_text.setText(db[0][3]) level_path = join(dirname(self.project_file_text.text()), 'Content', self.sub_folder_text.text()) root_model = self.model_base(self) self.ProjectTreeLevels.reset() self.ProjectTreeLevels.setModel(root_model) if self.project_file_text.text(): data_tree = self.levels_list(level_path) self.model_populate(data_tree, root_model.invisibleRootItem()) self.ProjectTreeLevels.expandAll() self.ProjectTreeLevels.setColumnWidth(0, 250) self.ProjectTreeLevels.setSortingEnabled(True) self.ProjectTreeLevels.sortByColumn(0, Qt.AscendingOrder) root_model.itemChanged.connect(self.save_level) return self def field_setup(self): """Generate the fields and signal about all paths""" self.ue4_path_edit.clicked.connect(lambda: self.select_file(1)) self.project_file_edit.clicked.connect(lambda: self.select_file(2)) self.sub_folder_edit.clicked.connect( lambda: self.tab_project_setup(index=3)) def save_field(self): self.data.write_data_path(self.ue4_path_text, self.project_file_text, self.sub_folder_text) def levels_list(self, path): """ Generate a list with all levels inside a path give than argument :param path: specify a path to scan the folder, it's a simple string :return: a dict with all levels and folder """ folders = {} levels = [] if isdir(path): obj_path = PureWindowsPath(path) tree = listdir(str(obj_path)) tree.sort(key=lambda s: s.find('.umap')) for item in tree: abs_path = obj_path.joinpath(item) if isdir(abs_path): key = obj_path.stem sub_levels = self.levels_list(abs_path) if len(sub_levels) and type(sub_levels) == dict: levels.append(sub_levels) folders[key] = levels else: if '.umap' in item: regex = r"^.*Content" relative_path = re.sub(regex, "", str(obj_path)) levels.append(join(relative_path, item)) key = basename(dirname(abs_path)) folders[key] = levels else: folders = {'No Data': 'Error Path'} return folders def save_level(self, index_item): """ Function to save or remove the levels from the Data Base :param index_item: A string with the level name to save it. :return: """ state = index_item.checkState() row = index_item.row() parent = index_item.parent() path_level = parent.child(row, 1) if state == Qt.Checked: info = [index_item.text(), path_level.text(), 1] else: info = [index_item.text(), path_level.text(), 0] self.data.write_data_levels(state=state, data=info) def model_populate(self, children, parent): """ Function to work with the Model. You need to give 2 parameter, a dict with your Data you want show, and a parent to define the index. It's a recursive function, if your Data has a Dict inside a Dict, the function generate the Tree with all sub-node. :param children: It's only a Dict, included your Data :param parent: Define your first level, work with the Invisible Root :return: nothing returns. """ for key, values in sorted(children.items()): item_object = QStandardItem(key) folder_icon = QtGui.QIcon() folder_icon.addPixmap( QtGui.QPixmap("Resources/Icons/file-submodule.png")) item_object.setIcon(folder_icon) parent.appendRow(item_object) if type(values) == list: for value in values: if type(value) == str: level_name = basename(value) check = False if self.settings.last_job_run(): if len(self.data.select_levels(name=level_name)): check = 2 item_name = QStandardItem(level_name) item_name.setCheckable(True) item_name.setCheckState(check) item_path = QStandardItem(value) item_object.appendRow([item_name, item_path]) elif type(value) == dict: self.model_populate(value, item_object) def model_base(self, parent): """ Function to work with the Model. This function generate the Tree View base, with all header generate. :param parent: QTreeView :return: give a Model """ model = QStandardItemModel(0, 2, parent) model.setHeaderData(self.header1, Qt.Horizontal, 'Names') model.setHeaderData(self.header2, Qt.Horizontal, 'Paths') return model def select_file(self, index): """Function to choose a path about the Editor and Project file""" select = file_open(self, index) self.tab_project_setup(index, select[0]) # Ui Functions ------------------------------------------------------------ # Tab Source Control ---------------------------------------------------- def tab_source_control(self): """ The Ui about the Source Control panel, all option and slot connect. :return: """ soft_work = self.softwares_comboBox soft_work.currentIndexChanged.connect(self.sc_software) if self.settings.last_job_run(): data_sc = self.data.select_scv() index_cb = self.softwares_comboBox.findText(data_sc[0]) self.softwares_comboBox.setCurrentIndex(index_cb) self.user_text.setText(data_sc[1]) self.password_text.setText(data_sc[2]) def sc_software(self): """ Event on the Source control tab, activate or disable all fields about the option on this tab. Hide the password field. :return: """ self.password_text.setEchoMode(QLineEdit.Password) type_items = QLineEdit, QLabel, QPushButton parent_setup = self.sc_groupBox_setup.findChildren(type_items) state = False if self.softwares_comboBox.currentText() == 'Disabled': state = True for item in parent_setup: item.setDisabled(state) def sc_save(self): sc_data = [ self.softwares_comboBox.currentText(), self.user_text.text(), self.password_text.text() ] self.data.write_scv(sc_data) # Buttons Box Function ---------------------------------------------------- @staticmethod def btn_restore(): """ Function to restore the view. :return: """ return print('Restore View') def save_tab(self): """ Simple function to save a new project or update the Data Base :return: """ if not self.settings.last_job_run(): file_save_project(self) else: # TODO Look to save the path field with the function save_field() # self.save_field() self.sc_save() self.data.close()
class MainWindows(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): """ Main Window, principal view, this windows can show all level, access on many option -path setup, network, log... """ super(MainWindows, self).__init__(parent) self.setupUi(self) # Setup settings base # Generate all variable needed with this window self.settings = Setup() self.data = TableProgram() self.job = self.settings.last_job_run() if self.job: self.scv_data = self.data.select_scv() self.checkBoxLevels = {} self.menu_setup() self.levels_tools() self.levels_generate() self.bottom_tools() # Ui Function ------------------------------------------------------------- # File Menu setup ------------------------------------------------------- def menu_setup(self): """ Function to setup all connection an signal inside the File Menu bar. :return: """ # File Menu self.actionNew_Setup.triggered.connect(self.dial_setup_project) open_project = self.actionLoad_Lastproject open_project.triggered.connect( lambda: load_generic(self, 'Open Project', '*.db')) # Setup Menu self.actionProject.triggered.connect(self.dial_setup_project) self.actionNetworks.triggered.connect( lambda: self.dial_setup_project(2)) self.actionCSV.triggered.connect(lambda: self.dial_setup_project(1)) # Log Menu self.actionShow_log_folder.triggered.connect(self.dialogue_log) self.actionClean_Log.triggered.connect(self.dialogue_log) # Help Menu self.actionAbout.triggered.connect(self.dialogue_help) self.actionShortcut.triggered.connect(lambda: self.dialogue_help(1)) # Ui Function ------------------------------------------------------------- # Toolbars and function about the levels -------------------------------- def levels_tools(self): """ Function to setup the tools about the levels, add all signal used. :return: """ self.pushLevelsSelect.clicked.connect(lambda: self.select_level(2)) self.pushLevelsDeselect.clicked.connect(self.select_level) self.toolLevelsEdit.clicked.connect(self.dial_setup_project) def levels_generate(self): """ Function to draw all levels setup with this project. :return: """ group_parent = self.allLevelsWidget vertical_parent = self.allLevelsCheck vertical_parent.setAlignment(Qt.AlignLeft) # Generate all Checkbox Levels. if self.job: print('Make all levels Label') levels = self.data.select_levels() project_path = self.data.select_paths() project_path = join(dirname(project_path[0][2]) + '/Content/') project_path = PureWindowsPath(project_path) sc_software = self.scv_data[0] for level in levels: print('Loop about a level :', level) # Define horizontal layout h_layout = QtWidgets.QHBoxLayout() h_layout.setObjectName('h_layout') h_layout.setAlignment(Qt.AlignLeft) # Define all variable used state = True nbr = levels.index(level) level_name = level[1] msg_label = level_name level_path = str(project_path) + level[2] icon = QPixmap("Resources/Icons/s-empty.png") print('Level Name :', level_name) # Test with the Source Control -work only with Perforce # TODO Add a progress bar, check levels on sc can be long # TODO Setup another Source Control solution -git, subversion if sc_software != str('Disabled'): sc = perforce.connect() for file in listdir(dirname(level_path)): # TODO add an operator to sync the files ? # perforce.sync(file, sc) item = join(dirname(level_path), file) item_norm = PureWindowsPath(item) print('Path SC Norm > ', item_norm) revision = perforce.Revision(connection=sc, data=str(item_norm)) if len(revision.openedBy): print('Level', file, 'opening by someone.') state = False msg_label = 'Level checkout' break if not revision.isSynced: state = False path = "Resources/Icons/cloud-download.png" icon = QPixmap(path) msg_label = 'Not Sync, update it.' print('Level', file, 'not sync.') break # Generate the Ui with all parameter self.checkBoxLevels[nbr] = QtWidgets.QCheckBox(level_name) self.checkBoxLevels[nbr].setObjectName(level_name) self.checkBoxLevels[nbr].setEnabled(state) self.checkBoxLevels[nbr].setToolTip(msg_label) h_layout.addWidget(self.checkBoxLevels[nbr], alignment=Qt.AlignLeft) label_work = QtWidgets.QLabel(group_parent) label_work.setPixmap(icon) label_work.setToolTip(msg_label) h_layout.addWidget(label_work, alignment=Qt.AlignLeft) h_layout.addWidget(self.checkBoxLevels[nbr], alignment=Qt.AlignLeft) vertical_parent.addLayout(h_layout) # self.allLevelsCheck.contentsMargins() if 'False' not in self.scv_data[0]: self.checkBoxSubmit.setEnabled(True) # Ui Function ------------------------------------------------------------- # Bottom Toolbars, option to launch the rendering and the log ----------- def bottom_tools(self): """ Function to add signal on the bottom toolbars. :return: """ self.pushToolsBuils.clicked.connect(self.view_rendering) self.pushToolsBuils.setToolTip(self.pushToolsBuils.statusTip()) # Window Call ------------------------------------------------------------- # Tab Setup dialogue --------------------------------------------------- def dial_setup_project(self, index): """ Function to show the dialogue 'Setup Tab', when it's close the function 'levels_generate' is rebuilt. :param index: A simple index to select the Tab opened. :return: """ ui_setup_tab = DialSetupTab() ui_setup_tab.tabWidget.setCurrentIndex(index) ui_setup_tab.show() rsp = ui_setup_tab.exec_() if rsp == QtWidgets.QDialog.Rejected: layout_levels = self.allLevelsCheck for item in reversed(range(layout_levels.count())): delete_item = layout_levels.itemAt(item).widget() layout_levels.removeWidget(delete_item) delete_item.setParent(None) self.levels_generate() # Old, refactoring function ----------------------------------------------- def dialogue_log(self): """ A simple function to show the Windows Log with all option :return: """ dialog_log = DialLogTools(self) dialog_log.show() def dialogue_help(self, index): """ Function to show the dialogue 'Help'. :param index: A simple index to select the Tab opened. :return: """ dialog_help = DialViewAbout(self) dialog_help.show() dialog_help.tabWidget.setCurrentIndex(index) def select_level(self, state=0): """ Event to select or deselect all levels, :param state: return the state checkbox, 0 to off, 1 to be semi-push and 2 to be check :return: """ for key, value in self.checkBoxLevels.items(): btn = self.checkBoxLevels[key] if QtWidgets.QAbstractButton.isEnabled(btn): btn.setCheckState(state) def view_rendering(self): """ Event to launch the rendering windows and all build -check the swarm, the source control used... and more. :return: """ lvl_rendering = [] submit_state = False for key, value in self.checkBoxLevels.items(): btn = self.checkBoxLevels[key] name = btn.text() if QAbstractButton.isChecked(btn): lvl_rendering.append(name) if len(lvl_rendering) == 0: message = 'No level selected !' popup_msg(self, 'information', 'Error', message) else: message = 'Launch the rendering ?' reply = popup_msg(self, 'question', 'Rendering', message) lvl_rendering.sort() if reply == QMessageBox.Yes: machines = self.checkBoxMachines swarm_setup(QAbstractButton.isChecked(machines)) if QAbstractButton.isChecked(self.checkBoxSubmit): submit_state = True dial_rendering = DialRendering(self, lvl_list=lvl_rendering, csv=self.scv_data, submit=submit_state) dial_rendering.show() rsp = dial_rendering.exec_() if rsp == QtWidgets.QDialog.Accepted: print('Rendering Validate') swarm_setup(False) message = 'Level Build' else: message = 'Rendering abort.' self.statusbar.showMessage(message)