def createWidget(self): # Add vertical layout to dock contents verticalLayout = QtWidgets.QVBoxLayout(self) verticalLayout.setContentsMargins(10, 10, 10, 10) verticalLayout.setAlignment(Qt.AlignTop) # Add vertical layout to main widget (self) self.setLayout(verticalLayout) # Add the next and previous buttons: horizontalGroupBox = QGroupBox() horizontalGroupBox.setContentsMargins(0, 0, 0, 0) horizontalLayout = QHBoxLayout() horizontalGroupBox.setLayout(horizontalLayout) horizontalGroupBox.setFlat(True) verticalLayout.addWidget(horizontalGroupBox) next_button = QPushButton("Next step") prev_button = QPushButton("Previous step") prev_button.setEnabled(False) prev_button.clicked.connect(lambda: self.updateStep('prev')) next_button.clicked.connect(lambda: self.updateStep('next')) horizontalLayout.addWidget(prev_button) horizontalLayout.addWidget(next_button) self.next_button = next_button self.prev_button = prev_button self.current_step = -1 self.uiElements = { 'verticalLayout': verticalLayout, 'buttonGroupBox': horizontalGroupBox, } self.widgets = {}
def create_group_box(parent, text): font = QFont() font.setBold(True) group_box = QGroupBox(text, parent) group_box.setFont(font) group_box.setAlignment(Qt.AlignHCenter) group_box.setFlat(False) return group_box
def __init__(self, all_course_info, choose_info, parent=None): super(OptionalCourse, self).__init__(parent) self.course_info = [] # Create optional course GroupBox optional_course = QGroupBox("选修") optional_course.setFlat(False) # Optional course checkbox self.checkBox = [] for i in all_course_info: if i[0] == '-': checkbox = QCheckBox(i[1:]) checkbox.stateChanged.connect(self.checkbox_func) self.checkBox.append(checkbox) # Add label and combobox to GridLayout i = 0 j = 0 self.optional_layout = QGridLayout() for obj in range(len(self.checkBox)): self.optional_layout.addWidget(self.checkBox[obj], j, i) if i == 4: j = j + 1 i = 0 else: i = i + 2 optional_course.setLayout(self.optional_layout) button = QPushButton("确定", self) button.clicked.connect(self.return_info) mainLayout = QVBoxLayout() mainLayout.addWidget(optional_course) mainLayout.addWidget(button) for i in choose_info: for j in self.checkBox: if i == j.text(): j.setChecked(True) self.setLayout(mainLayout) self.setWindowTitle("选修课")
def __init__(self, candidate_course, all_candidate_course, candidate_course_semester, pre2post, parent=None): super(ChangeCourse, self).__init__(parent) self.course_info = dict() self.pre2post = pre2post self.candidate_course_semester = candidate_course_semester # Create required course GroupBox required_course = QGroupBox("必修") required_course.setFlat(False) # Course Label self.checkBox = [] for i in candidate_course: if i[0] != '-': self.course_info[i] = '' label = QLabel(i) self.checkBox.append(label) # Course combobox self.cb = [] for i in range(0, len(candidate_course)): if candidate_course[i][0] != '-': cb = QComboBox(self) for k in range( candidate_course_semester[all_candidate_course[i]], 8): self.course_info[ all_candidate_course[i]] = candidate_course_semester[ all_candidate_course[i]] cb.addItem(str(k)) cb.currentIndexChanged.connect(self.get_info) self.cb.append(cb) # Add label and combobox to GridLayout i = 0 j = 0 self.required_layout = QGridLayout() for obj in range(len(self.checkBox)): self.required_layout.addWidget(self.checkBox[obj], j, i) self.required_layout.addWidget(self.cb[obj], j, i + 1) if i == 4: j = j + 1 i = 0 else: i = i + 2 required_course.setLayout(self.required_layout) # Create optional course GroupBox optional_course = QGroupBox("选修") optional_course.setFlat(False) # Optional course checkbox self.checkBox_optional = [] for i in candidate_course: if i[0] == '-': checkbox = QLabel(i[1:]) # checkbox.stateChanged.connect(self.checkbox_func) self.checkBox_optional.append(checkbox) # Optional course combobox self.cb = [] for i in range(0, len(self.checkBox_optional)): cb = QComboBox(self) # cb.addItem('') for k in range( candidate_course_semester[ self.checkBox_optional[i].text()], 8): self.course_info[self.checkBox_optional[i].text( )] = candidate_course_semester[ self.checkBox_optional[i].text()] cb.addItem(str(k)) cb.currentIndexChanged.connect(self.get_info) self.cb.append(cb) # Add label and combobox to GridLayout i = 0 j = 0 self.optional_layout = QGridLayout() for obj in range(len(self.checkBox_optional)): self.optional_layout.addWidget(self.checkBox_optional[obj], j, i) self.optional_layout.addWidget(self.cb[obj], j, i + 1) if i == 4: j = j + 1 i = 0 else: i = i + 2 optional_course.setLayout(self.optional_layout) button = QPushButton("确定", self) button.clicked.connect(self.return_info) mainLayout = QVBoxLayout() mainLayout.addWidget(required_course) mainLayout.addWidget(optional_course) mainLayout.addWidget(button) self.setLayout(mainLayout) self.setWindowTitle("高级排课选项")
class MyDockWidget(cutter.CutterDockWidget): def __init__(self, parent, action): super(MyDockWidget, self).__init__(parent, action) self.setObjectName("Capa explorer") self.setWindowTitle("Capa explorer") self._config = CutterBindings.Configuration.instance() self.model_data = CapaExplorerDataModel() self.range_model_proxy = CapaExplorerRangeProxyModel() self.range_model_proxy.setSourceModel(self.model_data) self.search_model_proxy = CapaExplorerSearchProxyModel() self.search_model_proxy.setSourceModel(self.range_model_proxy) self.create_view_tabs() self.create_tree_tab_ui() self.create_view_attack() self.connect_signals() self.setWidget(self.tabs) self.show() def create_view_tabs(self): # Create tabs container self.tabs = QTabWidget() # Create the tabs self.tab_attack = QWidget(self.tabs) self.tab_tree_w_model = QWidget(self.tabs) self.tabs.addTab(self.tab_tree_w_model, "Tree View") self.tabs.addTab(self.tab_attack, "MITRE") # Add load button self.btn_load_capa_results = QPushButton() self.btn_load_capa_results.setText("Load capa JSON") self.btn_load_capa_results.setStyleSheet( "margin-bottom: 2px;margin-right:2px") self.btn_load_capa_results.clicked.connect(self.load_file) self.tabs.setCornerWidget(self.btn_load_capa_results) def create_tree_tab_ui(self): self.capa_tree_view_layout = QVBoxLayout() self.capa_tree_view_layout.setAlignment(Qt.AlignTop) self.chk_fcn_scope = QCheckBox("Limit to Current function") #TODO: reset state on load file self.chk_fcn_scope.setChecked(False) self.chk_fcn_scope.stateChanged.connect( self.slot_checkbox_limit_by_changed) self.input_search = QLineEdit() self.input_search.setStyleSheet("margin:0px; padding:0px;") self.input_search.setPlaceholderText("search...") self.input_search.textChanged.connect( self.slot_limit_results_to_search) self.filter_controls_container = QGroupBox() self.filter_controls_container.setObjectName("scope") self.filter_controls_container.setFlat(True) self.filter_controls_container.setStyleSheet( "#scope{border:0px; padding:0px; margin:0px;subcontrol-origin: padding; subcontrol-position: left top;}" ) self.filter_controls_layout = QHBoxLayout( self.filter_controls_container) self.filter_controls_layout.setContentsMargins(0, 0, 0, 0) self.filter_controls_layout.addWidget(self.input_search) self.filter_controls_layout.addWidget(self.chk_fcn_scope) self.view_tree = CapaExplorerQtreeView(self.search_model_proxy) self.view_tree.setModel(self.search_model_proxy) # Make it look a little nicer when no results are loaded self.view_tree.header().setStretchLastSection(True) self.capa_tree_view_layout.addWidget(self.filter_controls_container) self.capa_tree_view_layout.addWidget(self.view_tree) self.tab_tree_w_model.setLayout(self.capa_tree_view_layout) def create_view_attack(self): table_headers = [ "ATT&CK Tactic", "ATT&CK Technique ", ] table = QTableWidget() table.setColumnCount(len(table_headers)) table.verticalHeader().setVisible(False) table.setSortingEnabled(False) table.setEditTriggers(QAbstractItemView.NoEditTriggers) table.setFocusPolicy(Qt.NoFocus) table.setSelectionMode(QAbstractItemView.NoSelection) table.setHorizontalHeaderLabels(table_headers) table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) table.horizontalHeader().setStretchLastSection(True) table.setShowGrid(False) table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) #table.setStyleSheet("QTableWidget::item { padding: 25px; }") attack_view_layout = QVBoxLayout() attack_view_layout.setAlignment(Qt.AlignTop) self.attack_table = table attack_view_layout.addWidget(self.attack_table) self.tab_attack.setLayout(attack_view_layout) return table def connect_signals(self): QObject.connect(cutter.core(), SIGNAL("functionRenamed(RVA, QString)"), self.model_data.refresh_function_names) QObject.connect(cutter.core(), SIGNAL("functionsChanged()"), self.model_data.refresh_function_names) QObject.connect(cutter.core(), SIGNAL("seekChanged(RVA)"), self.signal_shim_slot_checkbox_limit_by_changed) def render_new_table_header_item(self, text): """create new table header item with our style @param text: header text to display """ item = QTableWidgetItem(text) item.setForeground(self._config.getColor("graph.true")) font = QFont() font.setBold(True) item.setFont(font) return item def fill_attack_table(self, rules): tactics = collections.defaultdict(set) for key, rule in rules.items(): if not rule["meta"].get("att&ck"): continue for attack in rule["meta"]["att&ck"]: tactic, _, rest = attack.partition("::") if "::" in rest: technique, _, rest = rest.partition("::") subtechnique, _, id = rest.rpartition(" ") tactics[tactic].add((technique, subtechnique, id)) else: technique, _, id = rest.rpartition(" ") tactics[tactic].add((technique, id)) column_one = [] column_two = [] for (tactic, techniques) in sorted(tactics.items()): column_one.append(tactic.upper()) # add extra space when more than one technique column_one.extend(["" for i in range(len(techniques) - 1)]) for spec in sorted(techniques): if len(spec) == 2: technique, id = spec column_two.append("%s %s" % (technique, id)) elif len(spec) == 3: technique, subtechnique, id = spec column_two.append("%s::%s %s" % (technique, subtechnique, id)) else: raise RuntimeError("unexpected ATT&CK spec format") self.attack_table.setRowCount(max(len(column_one), len(column_two))) for (row, value) in enumerate(column_one): self.attack_table.setItem(row, 0, self.render_new_table_header_item(value)) for (row, value) in enumerate(column_two): self.attack_table.setItem(row, 1, QTableWidgetItem(value)) def slot_limit_results_to_search(self, text): """limit tree view results to search matches reset view after filter to maintain level 1 expansion """ self.search_model_proxy.set_query(text) self.view_tree.reset_ui(should_sort=False) def signal_shim_slot_checkbox_limit_by_changed(self): if self.chk_fcn_scope.isChecked(): self.slot_checkbox_limit_by_changed(Qt.Checked) def slot_checkbox_limit_by_changed(self, state): """slot activated if checkbox clicked if checked, configure function filter if screen location is located in function, otherwise clear filter @param state: checked state """ invoke_reset = True if state == Qt.Checked: minbound, maxbound = util.get_function_boundries_at_current_location( ) if self.range_model_proxy.min_ea == minbound and self.range_model_proxy.max_ea == maxbound: # Seek only changed within current function, avoid resetting tree invoke_reset = False self.limit_results_to_function((minbound, maxbound)) else: self.range_model_proxy.reset_address_range_filter() if invoke_reset: self.view_tree.reset_ui() def limit_results_to_function(self, f): """add filter to limit results to current function adds new address range filter to include function bounds, allowing basic blocks matched within a function to be included in the results @param f: (tuple (maxbound, minbound)) """ if f: self.range_model_proxy.add_address_range_filter(f[0], f[1]) else: # if function not exists don't display any results (assume address never -1) self.range_model_proxy.add_address_range_filter(-1, -1) def log(self, msg): """log to cutter console @param msg: message to log """ cutter.message(f"[CAPAExplorer]: {msg}") def load_file(self): # Disable load button during loading self.btn_load_capa_results.setEnabled(False) filename = QFileDialog.getOpenFileName() path = filename[0] if len(path): try: data = util.load_capa_json(path) self.fill_attack_table(data['rules']) self.model_data.clear() self.model_data.render_capa_doc(data) # Restore ability to scroll on last column self.view_tree.header().setStretchLastSection(False) self.view_tree.slot_resize_columns_to_content() except Exception as e: util.log('Could not load json file.') else: util.log('No file selected.') self.btn_load_capa_results.setEnabled(True)
class Preferences(QDialog): def __init__(self, prefs, icon=None): super().__init__() self.prefs = prefs self.hotkey_format_regex = r'^\<ctrl\>\+\<alt\>(\+\<shift\>)?\+[a-zA-Z0-9]$' if icon: self.setWindowIcon(icon) # checkbox: Include Minimized include_minimized = QCheckBox() include_minimized.setFixedWidth(35) include_minimized.setChecked(self.prefs["include_minimized"]["value"]) include_minimized.stateChanged.connect( lambda: self.include_minimized(include_minimized.isChecked())) include_minimized_descr = QLabel( self.prefs["include_minimized"]["description"]) include_minimized_descr.setWordWrap(True) include_minimized_layout = QHBoxLayout() include_minimized_layout.addWidget(include_minimized) include_minimized_layout.addWidget(include_minimized_descr) include_minimized_groupbox = QGroupBox("Include Minimized") include_minimized_groupbox.setLayout(include_minimized_layout) # checkbox: snap to grid snap_to_grid = QCheckBox() snap_to_grid.setFixedWidth(35) snap_to_grid.setChecked(self.prefs["snap_to_grid"]["value"]) snap_to_grid.stateChanged.connect( lambda: self.snap_to_grid(snap_to_grid.isChecked())) snap_to_grid_descr = QLabel(self.prefs["snap_to_grid"]["description"]) snap_to_grid_descr.setWordWrap(True) snap_to_grid_layout = QHBoxLayout() snap_to_grid_layout.addWidget(snap_to_grid) snap_to_grid_layout.addWidget(snap_to_grid_descr) snap_to_grid_groupbox = QGroupBox("Snap To Grid") snap_to_grid_groupbox.setLayout(snap_to_grid_layout) # checkbox: fit into screen fit_into_screen = QCheckBox() fit_into_screen.setFixedWidth(35) fit_into_screen.setChecked(self.prefs["fit_into_screen"]["value"]) fit_into_screen.stateChanged.connect( lambda: self.fit_into_screen(fit_into_screen.isChecked())) fit_into_screen_descr = QLabel( self.prefs["fit_into_screen"]["description"]) fit_into_screen_descr.setWordWrap(True) fit_into_screen_layout = QHBoxLayout() fit_into_screen_layout.addWidget(fit_into_screen) fit_into_screen_layout.addWidget(fit_into_screen_descr) fit_into_screen_groupbox = QGroupBox("Fit Into Screen") fit_into_screen_groupbox.setLayout(fit_into_screen_layout) # doublespinBox: match cutoff match_cutoff = QDoubleSpinBox() match_cutoff.setFixedWidth(35) match_cutoff.setValue(self.prefs["match_cutoff"]["value"]) match_cutoff.setRange(0.1, 1.0) match_cutoff.setSingleStep(0.1) match_cutoff.setDecimals(1) match_cutoff.valueChanged.connect( lambda: self.match_cutoff(match_cutoff.value())) match_cutoff_descr = QLabel(self.prefs["match_cutoff"]["description"]) match_cutoff_descr.setWordWrap(True) match_cutoff_layout = QHBoxLayout() match_cutoff_layout.addWidget(match_cutoff) match_cutoff_layout.addWidget(match_cutoff_descr) match_cutoff_groupbox = QGroupBox("Match Cutoff") match_cutoff_groupbox.setLayout(match_cutoff_layout) # checkbox: enable hotkeys enable_hotkeys = QCheckBox() enable_hotkeys.setFixedWidth(35) enable_hotkeys.setChecked(self.prefs["enable_hotkeys"]["value"]) enable_hotkeys.stateChanged.connect( lambda: self.enable_hotkeys(enable_hotkeys.isChecked())) enable_hotkeys_descr = QLabel( self.prefs["enable_hotkeys"]["description"]) enable_hotkeys_descr.setWordWrap(True) enable_hotkeys_layout = QHBoxLayout() enable_hotkeys_layout.addWidget(enable_hotkeys) enable_hotkeys_layout.addWidget(enable_hotkeys_descr) # lineedit: hotkeys shortcuts hotkey_freeze_new_name = QLabel("Freeze New:") hotkey_freeze_new_name.setFixedWidth(75) self.hotkey_freeze_new_status = QLabel() self.hotkey_freeze_new = QLineEdit( self.prefs["hotkeys"]["value"]["freeze_new"]) self.hotkey_freeze_new.setFixedWidth(140) self.hotkey_freeze_new.editingFinished.connect( lambda: self.hotkey_freeze_new_update(self.hotkey_freeze_new.text( ))) self.hotkey_freeze_new.cursorPositionChanged.connect( self.hotkey_freeze_new_status.clear) hotkey_freeze_new_layout = QHBoxLayout() hotkey_freeze_new_layout.addWidget(hotkey_freeze_new_name) hotkey_freeze_new_layout.addWidget(self.hotkey_freeze_new) hotkey_freeze_new_layout.addWidget(self.hotkey_freeze_new_status) hotkey_freeze_all_name = QLabel("Freeze All:") hotkey_freeze_all_name.setFixedWidth(75) self.hotkey_freeze_all_status = QLabel() self.hotkey_freeze_all = QLineEdit( self.prefs["hotkeys"]["value"]["freeze_all"]) self.hotkey_freeze_all.setFixedWidth(140) self.hotkey_freeze_all.editingFinished.connect( lambda: self.hotkey_freeze_all_update(self.hotkey_freeze_all.text( ))) self.hotkey_freeze_all.cursorPositionChanged.connect( self.hotkey_freeze_all_status.clear) hotkey_freeze_all_layout = QHBoxLayout() hotkey_freeze_all_layout.addWidget(hotkey_freeze_all_name) hotkey_freeze_all_layout.addWidget(self.hotkey_freeze_all) hotkey_freeze_all_layout.addWidget(self.hotkey_freeze_all_status) hotkey_restore_name = QLabel("Restore:") hotkey_restore_name.setFixedWidth(75) self.hotkey_restore_status = QLabel() self.hotkey_restore = QLineEdit( self.prefs["hotkeys"]["value"]["restore"]) self.hotkey_restore.setFixedWidth(140) self.hotkey_restore.editingFinished.connect( lambda: self.hotkey_restore_update(self.hotkey_restore.text())) self.hotkey_restore.cursorPositionChanged.connect( self.hotkey_restore_status.clear) hotkey_restore_layout = QHBoxLayout() hotkey_restore_layout.addWidget(hotkey_restore_name) hotkey_restore_layout.addWidget(self.hotkey_restore) hotkey_restore_layout.addWidget(self.hotkey_restore_status) self.hotkeys_statusbar = QLabel() self.hotkeys_statusbar.setWordWrap(True) self.hotkeys_statusbar.setText(hotkeys_statusbar_map[ self.prefs['enable_hotkeys']['value']]['text']) self.hotkeys_statusbar.setStyleSheet(hotkeys_statusbar_map[ self.prefs['enable_hotkeys']['value']]['style']) close_button = QPushButton("Close") close_button.setMaximumWidth(75) close_button.clicked.connect(self.close) hotkeys_statusbar_layout = QHBoxLayout() hotkeys_statusbar_layout.addWidget(self.hotkeys_statusbar) hotkeys_statusbar_layout.addWidget(close_button) hotkeys_layout = QVBoxLayout() hotkeys_layout.addLayout(hotkey_freeze_new_layout) hotkeys_layout.addLayout(hotkey_freeze_all_layout) hotkeys_layout.addLayout(hotkey_restore_layout) self.hotkeys_groupbox = QGroupBox() self.hotkeys_groupbox.setFlat(True) self.hotkeys_groupbox.setDisabled( not self.prefs["enable_hotkeys"]["value"]) self.hotkeys_groupbox.setLayout(hotkeys_layout) enable_hotkeys_outer_layout = QVBoxLayout() enable_hotkeys_outer_layout.addLayout(enable_hotkeys_layout) enable_hotkeys_outer_layout.addWidget(self.hotkeys_groupbox) enable_hotkeys_groupbox = QGroupBox("Enable Hotkeys") enable_hotkeys_groupbox.setLayout(enable_hotkeys_outer_layout) # Create main layout and add widgets main_layout = QVBoxLayout() main_layout.addWidget(include_minimized_groupbox) main_layout.addWidget(snap_to_grid_groupbox) main_layout.addWidget(fit_into_screen_groupbox) main_layout.addWidget(match_cutoff_groupbox) main_layout.addWidget(enable_hotkeys_groupbox) main_layout.addWidget(self.hotkeys_groupbox) #main_layout.addWidget(hotkeys_statusbar_groupbox) main_layout.addLayout(hotkeys_statusbar_layout) self.setLayout(main_layout) def include_minimized(self, isChecked): print(f'include_minimized: {isChecked}') self.prefs["include_minimized"]["value"] = isChecked def snap_to_grid(self, isChecked): print(f'snap_to_grid: {isChecked}') self.prefs["snap_to_grid"]["value"] = isChecked def fit_into_screen(self, isChecked): print(f'fit_into_screen: {isChecked}') self.prefs["fit_into_screen"]["value"] = isChecked def match_cutoff(self, value): rounded_value = round(value, 1) print(f'match_cutoff: {rounded_value}') self.prefs["match_cutoff"]["value"] = rounded_value def enable_hotkeys(self, isChecked): print(f'enable_hotkeys: {isChecked}') self.prefs["enable_hotkeys"]["value"] = isChecked self.hotkeys_groupbox.setDisabled(not isChecked) self.hotkeys_statusbar.setText( hotkeys_statusbar_map[isChecked]['text']) self.hotkeys_statusbar.setStyleSheet( hotkeys_statusbar_map[isChecked]['style']) def hotkey_freeze_new_update(self, text): print(f'hotkey_freeze_new: {text}') if re.match(self.hotkey_format_regex, text): self.prefs["hotkeys"]["value"]["freeze_new"] = text self.hotkey_freeze_new_status.setText(f'Updated!') self.hotkey_freeze_new_status.setStyleSheet("color: green;") else: self.hotkey_freeze_new.setText( self.prefs["hotkeys"]["value"]["freeze_new"]) self.hotkey_freeze_new_status.setText( 'Failed to update: unsupported format') self.hotkey_freeze_new_status.setStyleSheet("color: red;") def hotkey_freeze_all_update(self, text): print(f'hotkey_freeze_all: {text}') if re.match(self.hotkey_format_regex, text): self.prefs["hotkeys"]["value"]["freeze_all"] = text self.hotkey_freeze_all_status.setText(f'Updated!') self.hotkey_freeze_all_status.setStyleSheet("color: green;") else: self.hotkey_freeze_all.setText( self.prefs["hotkeys"]["value"]["freeze_all"]) self.hotkey_freeze_all_status.setText( 'Failed to update: unsupported format') self.hotkey_freeze_all_status.setStyleSheet("color: red;") def hotkey_restore_update(self, text): print(f'hotkey_restore: {text}') if re.match(self.hotkey_format_regex, text): self.prefs["hotkeys"]["value"]["restore"] = text self.hotkey_restore_status.setText(f'Updated!') self.hotkey_restore_status.setStyleSheet("color: green;") else: self.hotkey_restore.setText( self.prefs["hotkeys"]["value"]["restore"]) self.hotkey_restore_status.setText( 'Failed to update: unsupported format') self.hotkey_restore_status.setStyleSheet("color: red;")
class MyDockWidget(cutter.CutterDockWidget): def __init__(self, parent, action): super(MyDockWidget, self).__init__(parent, action) self.setObjectName("Capa explorer") self.setWindowTitle("Capa explorer") self._config = CutterBindings.Configuration.instance() self.model_data = CapaExplorerDataModel() self.range_model_proxy = CapaExplorerRangeProxyModel() self.range_model_proxy.setSourceModel(self.model_data) self.search_model_proxy = CapaExplorerSearchProxyModel() self.search_model_proxy.setSourceModel(self.range_model_proxy) self.create_view_tabs() self.create_menu() self.create_tree_tab_ui() self.create_view_attack() self.connect_signals() self.setWidget(self.tabs) self.show() def create_view_tabs(self): # Create tabs container self.tabs = QTabWidget() # Create the tabs self.tab_attack = QWidget(self.tabs) self.tab_tree_w_model = QWidget(self.tabs) self.tabs.addTab(self.tab_tree_w_model, "Tree View") self.tabs.addTab(self.tab_attack, "MITRE") def create_menu(self): # Define menu actions # Text, tooltip, function, enabled before file load self.disabled_menu_items = [] menu_actions = [ ("Load JSON file", '', self.cma_load_file, True), (), ("Auto rename functions", 'Auto renames functions according to capa detections, can result in very long function names.', self.cma_analyze_and_rename, False), ("Create flags", 'Creates flagspaces and flags from capa detections.', self.cma_create_flags, False), (), ("About", '', self.cma_display_about, True), ] self.capa_menu = QMenu() self.capa_menu.setToolTipsVisible(True) # Create qactions for action in menu_actions: if not len(action): # Create separator on empty self.capa_menu.addSeparator() continue a = QAction(self) a.setText(action[0]) a.setToolTip(action[1]) a.triggered.connect(action[2]) a.setEnabled(action[3]) if not action[3]: self.disabled_menu_items.append(a) self.capa_menu.addAction(a) # Create menu button font = QFont() font.setBold(True) self.btn_menu = QToolButton() self.btn_menu.setText('...') self.btn_menu.setFont(font) self.btn_menu.setPopupMode(QToolButton.InstantPopup) self.btn_menu.setMenu(self.capa_menu) self.btn_menu.setStyleSheet( 'QToolButton::menu-indicator { image: none; }') self.tabs.setCornerWidget(self.btn_menu, corner=Qt.TopRightCorner) def create_tree_tab_ui(self): self.capa_tree_view_layout = QVBoxLayout() self.capa_tree_view_layout.setAlignment(Qt.AlignTop) self.chk_fcn_scope = QCheckBox("Limit to Current function") #TODO: reset state on load file self.chk_fcn_scope.setChecked(False) self.chk_fcn_scope.stateChanged.connect( self.slot_checkbox_limit_by_changed) self.input_search = QLineEdit() self.input_search.setStyleSheet("margin:0px; padding:0px;") self.input_search.setPlaceholderText("search...") self.input_search.textChanged.connect( self.slot_limit_results_to_search) self.filter_controls_container = QGroupBox() self.filter_controls_container.setObjectName("scope") self.filter_controls_container.setFlat(True) self.filter_controls_container.setStyleSheet( "#scope{border:0px; padding:0px; margin:0px;subcontrol-origin: padding; subcontrol-position: left top;}" ) self.filter_controls_layout = QHBoxLayout( self.filter_controls_container) self.filter_controls_layout.setContentsMargins(0, 0, 0, 0) self.filter_controls_layout.addWidget(self.input_search) self.filter_controls_layout.addWidget(self.chk_fcn_scope) self.view_tree = CapaExplorerQtreeView(self.search_model_proxy) self.view_tree.setModel(self.search_model_proxy) # Make it look a little nicer when no results are loaded self.view_tree.header().setStretchLastSection(True) self.capa_tree_view_layout.addWidget(self.filter_controls_container) self.capa_tree_view_layout.addWidget(self.view_tree) self.tab_tree_w_model.setLayout(self.capa_tree_view_layout) def create_view_attack(self): table_headers = [ "ATT&CK Tactic", "ATT&CK Technique ", ] table = QTableWidget() table.setColumnCount(len(table_headers)) table.verticalHeader().setVisible(False) table.setSortingEnabled(False) table.setEditTriggers(QAbstractItemView.NoEditTriggers) table.setFocusPolicy(Qt.NoFocus) table.setSelectionMode(QAbstractItemView.NoSelection) table.setHorizontalHeaderLabels(table_headers) table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) table.horizontalHeader().setStretchLastSection(True) table.setShowGrid(False) table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) #table.setStyleSheet("QTableWidget::item { padding: 25px; }") attack_view_layout = QVBoxLayout() attack_view_layout.setAlignment(Qt.AlignTop) self.attack_table = table attack_view_layout.addWidget(self.attack_table) self.tab_attack.setLayout(attack_view_layout) return table def connect_signals(self): QObject.connect(cutter.core(), SIGNAL("functionRenamed(RVA, QString)"), self.model_data.refresh_function_names) QObject.connect(cutter.core(), SIGNAL("functionsChanged()"), self.model_data.refresh_function_names) QObject.connect(cutter.core(), SIGNAL("seekChanged(RVA)"), self.signal_shim_slot_checkbox_limit_by_changed) def render_new_table_header_item(self, text): """create new table header item with our style @param text: header text to display """ item = QTableWidgetItem(text) item.setForeground(self._config.getColor("graph.true")) font = QFont() font.setBold(True) item.setFont(font) return item def fill_attack_table(self, rules): tactics = collections.defaultdict(set) for key, rule in rules.items(): if not rule["meta"].get("att&ck"): continue for attack in rule["meta"]["att&ck"]: tactic, _, rest = attack.partition("::") if "::" in rest: technique, _, rest = rest.partition("::") subtechnique, _, id = rest.rpartition(" ") tactics[tactic].add((technique, subtechnique, id)) else: technique, _, id = rest.rpartition(" ") tactics[tactic].add((technique, id)) column_one = [] column_two = [] for (tactic, techniques) in sorted(tactics.items()): column_one.append(tactic.upper()) # add extra space when more than one technique column_one.extend(["" for i in range(len(techniques) - 1)]) for spec in sorted(techniques): if len(spec) == 2: technique, id = spec column_two.append("%s %s" % (technique, id)) elif len(spec) == 3: technique, subtechnique, id = spec column_two.append("%s::%s %s" % (technique, subtechnique, id)) else: raise RuntimeError("unexpected ATT&CK spec format") self.attack_table.setRowCount(max(len(column_one), len(column_two))) for (row, value) in enumerate(column_one): self.attack_table.setItem(row, 0, self.render_new_table_header_item(value)) for (row, value) in enumerate(column_two): self.attack_table.setItem(row, 1, QTableWidgetItem(value)) def enable_menu_items_after_load(self): # enables menu actions after file is loaded for action in self.disabled_menu_items: action.setEnabled(True) def slot_limit_results_to_search(self, text): """limit tree view results to search matches reset view after filter to maintain level 1 expansion """ self.search_model_proxy.set_query(text) self.view_tree.reset_ui(should_sort=False) def signal_shim_slot_checkbox_limit_by_changed(self): if self.chk_fcn_scope.isChecked(): self.slot_checkbox_limit_by_changed(Qt.Checked) def slot_checkbox_limit_by_changed(self, state): """slot activated if checkbox clicked if checked, configure function filter if screen location is located in function, otherwise clear filter @param state: checked state """ invoke_reset = True if state == Qt.Checked: minbound, maxbound = util.get_function_boundries_at_current_location( ) if self.range_model_proxy.min_ea == minbound and self.range_model_proxy.max_ea == maxbound: # Seek only changed within current function, avoid resetting tree invoke_reset = False self.limit_results_to_function((minbound, maxbound)) else: self.range_model_proxy.reset_address_range_filter() if invoke_reset: self.view_tree.reset_ui() def limit_results_to_function(self, f): """add filter to limit results to current function adds new address range filter to include function bounds, allowing basic blocks matched within a function to be included in the results @param f: (tuple (maxbound, minbound)) """ if f: self.range_model_proxy.add_address_range_filter(f[0], f[1]) else: # if function not exists don't display any results (assume address never -1) self.range_model_proxy.add_address_range_filter(-1, -1) # --- Menu Actions def cma_analyze_and_rename(self): message_box = QMessageBox() message_box.setStyleSheet("QLabel{min-width: 370px;}") message_box.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) message_box.setEscapeButton(QMessageBox.Cancel) message_box.setDefaultButton(QMessageBox.Ok) message_box.setWindowTitle('Warning') message_box.setText( 'Depending on the size of the binary and the' ' amount of \n' 'capa matches this feature can take some time to \n' 'complete and might make the UI freeze temporarily.') message_box.setInformativeText('Are you sure you want to proceed ?') ret = message_box.exec_() # Ok = 1024 if ret == 1024: self.model_data.auto_rename_functions() def cma_create_flags(self): self.model_data.create_flags() def cma_display_about(self): c = CAPAExplorerPlugin() info_text = ("{description}\n\n" "https://github.com/ninewayhandshake/capa-explorer\n\n" "Version: {version}\n" "Author: {author}\n" "License: Apache License 2.0\n").format( version=c.version, author=c.author, description=c.description, ) text = CAPAExplorerPlugin().name message_box = QMessageBox() message_box.setStyleSheet("QLabel{min-width: 370px;}") message_box.setWindowTitle('About') message_box.setText(text) message_box.setInformativeText(info_text) message_box.setStandardButtons(QMessageBox.Close) for i in message_box.findChildren(QLabel): i.setFocusPolicy(Qt.NoFocus) message_box.exec_() def cma_load_file(self): filename = QFileDialog.getOpenFileName() path = filename[0] if len(path): try: data = util.load_capa_json(path) self.fill_attack_table(data['rules']) self.model_data.clear() self.model_data.render_capa_doc(data) # Restore ability to scroll on last column self.view_tree.header().setStretchLastSection(False) self.view_tree.slot_resize_columns_to_content() self.enable_menu_items_after_load() except Exception as e: util.log('Could not load json file.') else: util.log('No file selected.')
class Window(QWidget): def __init__(self): super().__init__() # ----------style------------- self.font_type = "Arial" self.font_size = 10 self.font_color = "#676767" self.font_size2 = 12 self.font_color_black = "#f0f0f0" #--------------------------------- self.text8 = QTextEdit() self.text8.setReadOnly(True) self.check_text = False self.gbox6 = QGroupBox() try: f = open("plotter.txt", "x+") except: f = open("plotter.txt", "r") self.s = f.readline() f.close if self.s == "": f = open("plotter.txt", "w") f.write("dark") f.close() self.s = "dark" self.information_dialog() else: self.gbox6.setStyleSheet( "border:None;background-color:rgba(255,255,255,0)") self.text8.setStyleSheet( "border:None;background-color:rgba(255,255,255,0)") self.gbox6.setTitle("") self.text8.setText("") np.seterr(invalid='raise') self.setWindowTitle("XGrapher") self.setGeometry(500, 400, 900, 600) self.setMinimumSize(900, 600) pg.setConfigOption('background', (255, 255, 255, 0)) self.pw = pg.PlotWidget() self.pw.setXRange(0, 1) self.pw.setYRange(0, 1) self.pw.hideButtons() #--------------------------- self.list_errors = [] # -------------------------- self.a = False self.b = False self.c = False # ------------------------- self.d = False self.e = False self.f = False self.g = False self.after = False # ------------------------- self.check1 = False self.check2 = False self.check3 = False self.check_dot = False self.check_neg = False self.check_xrange = False self.check_yrange = False # ------------Labels----------------------------------------- self.label1 = QLabel() self.label1.setText("F(x):") self.label2 = QLabel() self.label2.setText("Min(x):") self.label3 = QLabel() self.label3.setText("Max(x):") self.label4 = QLabel() self.label5 = QLabel() self.label5.setText("< x <") self.label6 = QLabel() self.label6.setText("< y <") # --------------------------texteditors------------------ self.text1 = QLineEdit(self) self.text1.textChanged.connect(self.text1_response) self.text1.returnPressed.connect(self.focus_text1) self.text2 = QLineEdit(self) self.text2.textChanged.connect(self.text2_response) self.text2.returnPressed.connect(self.focus_text2) self.text3 = QLineEdit(self) self.text3.textChanged.connect(self.text3_response) self.text3.returnPressed.connect(self.focus_text3) self.text4 = QLineEdit() self.text4.textChanged.connect(self.text4_response) self.text4.returnPressed.connect(self.focus_text4) self.text5 = QLineEdit() self.text5.textChanged.connect(self.text5_response) self.text5.returnPressed.connect(self.focus_text5) self.text6 = QLineEdit() self.text6.textChanged.connect(self.text6_response) self.text6.returnPressed.connect(self.focus_text6) self.text7 = QLineEdit() self.text7.textChanged.connect(self.text7_response) self.text7.returnPressed.connect(self.focus_text7) # -------------------------------------------------------- self.button_2 = QPushButton() self.button_2.clicked.connect(self.auto_mode) self.button_save = QPushButton("Export Graph") self.button_help = QPushButton() self.button_help.clicked.connect(self.information_dialog) # ----------------------RadioButtons---------------------- self.rbutton1 = QRadioButton("Light") self.rbutton1.toggled.connect(self.light_mode) self.rbutton2 = QRadioButton("Dark") self.rbutton2.toggled.connect(self.dark_mode) # -------------------------------------------------------- self.setIcon() self.center() self.input_box() self.plot_box() self.vbox = QHBoxLayout() self.vbox.addWidget(self.gbox5) self.vbox.addSpacing(5) self.vbox.addWidget(self.plot) # self.setStyleSheet("background-color:rgb(32,32,32);") # self.setStyleSheet("background-color:rgb(240,240,240);") self.setLayout(self.vbox) if self.s == "dark": self.rbutton2.setChecked(True) else: self.rbutton1.setChecked(True) if self.s == "dark": self.dark_mode() else: self.light_mode() self.show() # --------------------------------------evalute------------------------------------------------------------------- self.replacements = { 'sin': 'np.sin', 'cos': 'np.cos', 'tan': 'np.tan', 'arccos': 'np.arccos', 'arcsin': 'np.arcsin', 'arctan': 'np.arctan', 'exp': 'np.exp', 'sqrt': 'np.sqrt', 'cbrt': 'np.cbrt', 'ln': 'np.log', "cosh": "np.cosh", "sinh": "np.cosh", "tanh": "np.cosh" } self.allowed_words = [ "x", "sin", "cos", "tan", "arccos", "arcsin", "arctan", "cosh", "sinh", "tanh", "exp", "sqrt", "cbrt", "log10", "ln" ] # ---------------------------------------------------------------------------------------------------------------- self.after = True def setIcon(self): appIcon = QIcon("close.ico") self.setWindowIcon(appIcon) def center(self): qRect = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qRect.moveCenter(centerPoint) self.move(qRect.topLeft()) def input_box(self): self.input = QGroupBox("Function") self.gbox = QGroupBox("Range") vbox_parent = QVBoxLayout() self.hbox_parent = QVBoxLayout() hbox1 = QHBoxLayout() hbox2 = QHBoxLayout() hbox3 = QHBoxLayout() hbox1.addWidget(self.label1) hbox1.addSpacing(17) hbox1.addWidget(self.text1) hbox2.addWidget(self.label2) hbox2.addSpacing(4) hbox2.addWidget(self.text2) hbox3.addWidget(self.label3) hbox3.addSpacing(0) hbox3.addWidget(self.text3) hbox_button = QHBoxLayout() hbox_button.addStretch(1) self.button = QPushButton("Reset") self.button.setFixedSize(70, 25) self.button.clicked.connect(self.reset) hbox_button.addWidget(self.button) vbox_parent.addLayout(hbox1) vbox_parent.addLayout(hbox2) vbox_parent.addLayout(hbox3) vbox_parent.addLayout(hbox_button) self.input.setLayout(vbox_parent) hbox4 = QHBoxLayout() hbox4.addWidget(self.text4) hbox4.addWidget(self.label5) hbox4.addWidget(self.text5) hbox5 = QHBoxLayout() hbox5.addWidget(self.text6) hbox5.addWidget(self.label6) hbox5.addWidget(self.text7) vbox3 = QVBoxLayout() vbox3.addWidget(self.button_2) vbox2 = QVBoxLayout() vbox2.addLayout(hbox4) vbox2.addLayout(hbox5) hbox6 = QHBoxLayout() hbox6.addLayout(vbox2) hbox6.addLayout(vbox3) self.gbox.setLayout(hbox6) #self.button_save.setFixedSize(200, 25) self.button_save.setFixedHeight(25) self.button_save.setFixedWidth(220) self.button_save.clicked.connect(self.export) hbox7 = QHBoxLayout() hbox7.addWidget(self.button_save) hbox7.addWidget(self.button_help) self.gbox3 = QGroupBox() self.gbox3.setFlat(True) self.gbox3.setStyleSheet("border: None") #self.gbox3.setLayout(hbox7) vbox3 = QVBoxLayout() vbox3.addWidget(self.gbox) self.gbox4 = QGroupBox("Status") hbox8 = QHBoxLayout() hbox8.addWidget(self.label4) self.gbox4.setLayout(hbox8) self.gbox_mode = QGroupBox("Style") vbox4 = QHBoxLayout() vbox4.addWidget(self.rbutton1) vbox4.addWidget(self.rbutton2) self.gbox_mode.setLayout(vbox4) hbox9 = QHBoxLayout() hbox9.addWidget(self.text8) self.gbox6.setLayout(hbox9) self.hbox_parent.addWidget(self.input) self.hbox_parent.addLayout(vbox3) self.hbox_parent.addLayout(hbox7) self.hbox_parent.addWidget(self.gbox6) self.hbox_parent.addWidget(self.gbox_mode) self.hbox_parent.addWidget(self.gbox4) self.gbox5 = QGroupBox() self.gbox5.setLayout(self.hbox_parent) def plot_box(self): self.plot = QGroupBox() layout = QVBoxLayout() self.pw.showGrid(True, True, 0.5) layout.addWidget(self.pw) self.plot.setLayout(layout) def text_restricted(self, str): if str != "": word = str[len(str) - 1] if re.match('[0-9-.]', word) is None: k = str.translate({ord(word): None}) str = k if word == "-" and len(str) > 1: k = str[1:].translate({ord(word): None}) str = str.replace(str[1:], k) if word == ".": i = 0 for v in str: if v == ".": i += 1 if i > 1: str = str[0:len(str) - 1] + "" if word == ".": self.check_dot = True else: str = "" return str def text1_response(self): if self.check1 == False: self.a = True self.plotx() else: self.check1 = False def text2_response(self): self.text2.setText(self.text_restricted(self.text2.text())) if self.check2 == False: self.b = True self.plotx() else: self.check2 = False def text3_response(self): self.text3.setText(self.text_restricted(self.text3.text())) if self.check3 == False: self.c = True self.plotx() else: self.check3 = False def text4_response(self): self.text4.setText(self.text_restricted(self.text4.text())) self.xrange() def text5_response(self): self.text5.setText(self.text_restricted(self.text5.text())) self.xrange() def text6_response(self): self.text6.setText(self.text_restricted(self.text6.text())) self.yrange() def text7_response(self): self.text7.setText(self.text_restricted(self.text7.text())) self.yrange() def xrange(self): if self.text4.text() == "" and self.text5.text() == "": self.error("No X Min Range") self.error("No X Max Range") self.error("Invalid X Range") self.f = False self.check_xrange = False if self.text1.text() == "" or self.text2.text( ) == "" or self.text3.text() == "": self.pw.setXRange(0, 1) else: self.pw.enableAutoRange(axis='x') elif self.text4.text() != "" and self.text5.text() != "": self.check_xrange = True if float(self.text4.text()) >= float(self.text5.text()): self.error_add("Invalid X Range") self.f = True else: self.pw.setXRange(float(self.text4.text()), float(self.text5.text())) self.error("No X Min Range") self.error("No X Max Range") self.error("Invalid X Range") self.f = False self.plotx() if self.text6.text() == "" or self.text7.text() == "": self.pw.enableAutoRange(axis='y') self.pw.setAutoVisible(y=True) else: if self.text4.text() == "" and self.check_xrange == True: self.error_add("No X Min Range") self.f = True self.pw.setXRange(0, 1) if self.text5.text() == "" and self.check_xrange == True: self.error_add("No X Max Range") self.f = True self.pw.setXRange(0, 1) if self.text6.text() != "" and self.text7.text() != "": self.pw.enableAutoRange(axis='x') self.pw.setAutoVisible(x=True) else: if self.d == True or self.e == True: self.pw.setYRange(0, 1) self.pw.setXRange(0, 1) else: self.pw.enableAutoRange() def yrange(self): if self.text6.text() == "" and self.text7.text() == "": self.error("No Y Min Range") self.error("No Y Max Range") self.error("Invalid Y Range") self.g = False self.check_yrange = False if self.text1.text() == "" or self.text2.text( ) == "" or self.text3.text() == "": self.pw.setYRange(0, 1) else: self.pw.enableAutoRange(axis='y') elif self.text6.text() != "" and self.text7.text() != "": self.check_yrange = True if float(self.text6.text()) >= float(self.text7.text()): self.error_add("Invalid Y Range") self.g = True else: self.pw.setYRange(float(self.text6.text()), float(self.text7.text())) self.error("No Y Min Range") self.error("No Y Max Range") self.error("Invalid Y Range") self.g = False self.plotx() if self.text4.text() == "" or self.text5.text() == "": self.pw.enableAutoRange(axis='x') self.pw.setAutoVisible(x=True) else: if self.text6.text() == "" and self.check_yrange == True: self.error_add("No Y Min Range") self.g = True self.pw.setYRange(0, 1) if self.text7.text() == "" and self.check_yrange == True: self.error_add("No Y Max Range") self.g = True self.pw.setYRange(0, 1) if self.text4.text() != "" and self.text5.text() != "": self.pw.enableAutoRange(axis='y') self.pw.setAutoVisible(y=True) else: if self.d == True or self.e == True: self.pw.setYRange(0, 1) self.pw.setXRange(0, 1) else: self.pw.enableAutoRange() def string2func(self, str): if str != "" and self.a == True and self.b == True and self.c == True: self.error("No Function to draw") self.d = False for word in re.findall('[a-zA-Z_]+', str): if word not in self.allowed_words: self.error_add("F(x) is not a Function of x") self.d = True else: self.d = False self.error('F(x) is not a Function of x') if word in self.replacements: str = str.replace(word, self.replacements[word]) if "^" in str: str = str.replace("^", "**") elif str == "" and self.b == True and self.c == True: self.error_add("No Function to draw") self.d = True self.pw.clear() def func(x): if str != "" and self.text2.text() != "" and self.text3.text( ) != "" and self.d == False: if self.d == False: try: if np.inf in eval(str): raise ZeroDivisionError if -np.inf in eval(str): raise ValueError except ZeroDivisionError: self.error_add("Cannot divide by Zero") self.d = True except FloatingPointError: self.error_addd("Undefined") self.d = True except ValueError: self.error_add("Math Error") self.d = True except: self.error_add("Syntax Error") self.d = True else: self.error("Cannot divide by Zero") self.error("Undefined") self.error("Math Error") self.error("Syntax Error") self.d = False return eval(str) return func def plotx(self): if self.text2.text() == "" and self.text3.text( ) == "" and self.text1.text( ) == "" and self.a == True and self.b == True and self.c == True: self.reset() func = self.string2func(self.text1.text()) if self.a == True and self.b == True and self.c == True and self.text2.text( ) != "" and self.text3.text() != "" and self.text1.text( ) != "" and self.d == False: if (self.text4.text() == "" and self.text5.text() == "") and ( self.text6.text() == "" and self.text7.text() == ""): self.pw.enableAutoRange() self.pw.clear() if (self.text2.text() == "-" or self.text3.text() == "-" or self.text3.text() == "." or self.text2.text() == "."): self.list_errors.append("Invalid Range") self.e = True else: min_num = float(self.text2.text()) max_num = float(self.text3.text()) if min_num >= max_num: self.error_add("Invalid Range") self.e = True else: range = np.linspace(min_num, max_num, 2000) if "x" not in self.text1.text( ) and self.text1.text() != "": try: if self.s == "light": self.pw.plot(range, np.ones(len(range)) * eval(self.text1.text()), pen=pg.mkPen(color=(140, 140, 140), width=2)) else: self.pw.plot(range, np.ones(len(range)) * eval(self.text1.text()), pen=pg.mkPen(color="w", width=2)) except ZeroDivisionError: self.error_add("Cannot divide by Zero") self.d = True except FloatingPointError: self.error_add("Undefined") self.d = True except ValueError: self.error_add("Math Error") self.d = True except: self.error_add("Syntax Error") self.d = True else: self.error("Cannot divide by Zero") self.error("Undefined") self.error("Math Error") self.error("Syntax Error") self.d = False else: y = func(range) if self.s == "light": self.pw.plot(range, y, pen=pg.mkPen(color=(140, 140, 140), width=2)) self.error("Invalid Range") self.error("No Min Value") self.error("No Max Value") else: self.pw.plot(range, y, pen=pg.mkPen(color="w", width=2)) self.error("Invalid Range") self.error("No Min Value") self.error("No Max Value") self.e = False else: if (self.text3.text() == "" and self.c == True): self.pw.clear() self.e = True self.error_add("No Max Value") elif (self.text3.text() != "" and self.c == True): self.error("No Max Value") if (self.text2.text() == "" and self.b == True): self.pw.clear() self.e = True self.error_add("No Min Value") elif (self.text2.text() != "" and self.b == True): self.error("No Min Value") def error(self, type): if type in self.list_errors: self.list_errors.remove(type) if len(self.list_errors) == 0: self.label4.setText("") else: self.label4.setText(self.list_errors[len(self.list_errors) - 1]) def error_add(self, error): if error in self.list_errors: pass else: self.list_errors.append(error) self.label4.setText(self.list_errors[len(self.list_errors) - 1]) def reset(self): self.pw.clear() if self.text4.text() == "" and self.text5.text( ) == "" and self.text6.text() == "" and self.text7.text() == "": self.pw.setXRange(0, 1) self.pw.setYRange(0, 1) self.check1 = True self.check2 = True self.check3 = True self.text1.setText("") self.text2.setText("") self.text3.setText("") self.a = False self.b = False self.c = False self.text1.setFocus() self.d = False self.e = False self.error("Invalid Range") self.error("No Min Value") self.error("No Max Value") self.error("Cannot divide by Zero") self.error("Undefined") self.error("Math Error") self.error("Syntax Error") self.error('F(x) is not a Function of x') self.error("No Function to draw") def focus_text1(self): self.text2.setFocus() def focus_text2(self): self.text3.setFocus() def focus_text3(self): self.text1.setFocus() def focus_text4(self): self.text5.setFocus() def focus_text5(self): self.text6.setFocus() def focus_text6(self): self.text7.setFocus() def focus_text7(self): self.text4.setFocus() def save(self): pass def information_dialog(self): if self.check_text == False: if self.s == "dark": self.gbox6.setTitle("Help") self.gbox6.setStyleSheet( "QGroupBox {border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.text8.setStyleSheet( "border:None;background-color:#383838;border:None;color: " + self.font_color_black) self.text8.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.text8.setText("--> The following operators must be used when writting the function:\n( - + / ^ ( ) ).\n\n--> The program supports the following functions and must be written as:" "\nsin(x),cos(x),tan(x),arccos(x),\narcsin(x),arctan(x),cosh(x),sinh(x),\ntanh(x),exp(x),sqrt(X),cbrt(x),\n" "log10(x),ln(x) and polynomial and rational functions." "\n\n--> The 'A' button in the Range box sets the x-axis and y-axis ranges to the appropriate values according to the values of the function.\n\n" \ "--> To close the Help box just click the help button beside the Export Graph.") else: self.gbox6.setTitle("Help") self.gbox6.setStyleSheet( "QGroupBox {border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.text8.setStyleSheet( "border:None;background-color:#f5f6f7;color: " + self.font_color) self.text8.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.text8.setText("--> The following operators must be used when writting the function:\n( - + / ^ ( ) ).\n\n--> The program supports the following functions and must be written as:" "\nsin(x),cos(x),tan(x),arccos(x),\narcsin(x),arctan(x),cosh(x),sinh(x),\ntanh(x),exp(x),sqrt(X),cbrt(x),\n" "log10(x),ln(x) and polynomial and rational functions." "\n\n--> The 'A' button in the Range box sets the x-axis and y-axis ranges to the appropriate values according to the values of the function.\n\n" \ "--> To close the Help box just click the help button beside the Export Graph.") self.check_text = True else: self.gbox6.setStyleSheet( "border:None;background-color:rgba(255,255,255,0)") self.text8.setStyleSheet( "border:None;background-color:rgba(255,255,255,0)") self.text8.setText("") self.gbox6.setTitle("") self.check_text = False def auto_mode(self): self.text4.setText("") self.text5.setText("") self.text6.setText("") self.text7.setText("") self.f = False self.g = False self.check_yrange == False self.check_xrange == False self.xrange() self.yrange() def dark_mode(self): self.input.setMaximumWidth(250) self.input.setFixedSize(250, 150) self.gbox.setMaximumWidth(250) self.gbox.setFixedSize(250, 90) self.gbox3.setMaximumWidth(250) self.gbox3.setFixedSize(250, 90) self.gbox4.setMaximumWidth(250) self.gbox4.setFixedSize(250, 45) self.gbox_mode.setMaximumWidth(250) self.gbox_mode.setFixedSize(250, 50) self.gbox5.setMaximumWidth(270) self.input.setObjectName("input") self.input.setStyleSheet( "QGroupBox#input{border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox#input::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox.setStyleSheet( "QGroupBox {border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox4.setStyleSheet( "QGroupBox {border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox_mode.setStyleSheet( "QGroupBox {border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.plot.setStyleSheet("color: " + self.font_color) self.setStyleSheet("background-color:#202020") self.label1.setStyleSheet( "background-color:#383838;border:None;color: " + self.font_color_black) self.label2.setStyleSheet( "background-color:#383838;border:None;color:" + self.font_color_black) self.label3.setStyleSheet( "background-color:#383838;border:None;color:" + self.font_color_black) self.label4.setStyleSheet( "background-color:#383838;border:None;color:" + self.font_color_black) self.label5.setStyleSheet( "background-color:#383838;border:None;color:" + self.font_color_black) self.label6.setStyleSheet( "background-color:#383838;border:None;color:" + self.font_color_black) self.rbutton1.setStyleSheet("background-color:#383838;color:" + self.font_color_black) self.rbutton2.setStyleSheet("background-color:#383838;color:" + self.font_color_black) self.rbutton1.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.rbutton2.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.label1.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label2.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label3.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label4.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label5.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label6.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text1.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text2.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text3.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text4.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text5.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text6.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.text7.setStyleSheet( "border:1px solid #5b5b5b;background-color:#383838;color:" + self.font_color_black) self.button_save.setStyleSheet( " QPushButton{border: 1px solid #f0f0f0;Text-align:center;background:#333333; color:#f0f0f0}" "QPushButton::hover{border: 1px solid #f0f0f0;Text-align:center;background:#2c2c2c}" "QPushButton::Pressed{border: 1px solid #f0f0f0;Text-align:center;background:#3d3c3c}" ) self.button.setStyleSheet( " QPushButton{border: 1px solid #f0f0f0;Text-align:center;background:#333333; color:#f0f0f0}" "QPushButton::hover{border: 1px solid #f0f0f0;Text-align:center;background:#2c2c2c}" "QPushButton::Pressed{border: 1px solid #f0f0f0;Text-align:center;background:#3d3c3c}" ) self.text1.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text2.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text3.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text4.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text5.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text6.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text7.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.gbox5.setObjectName("GroupBox") self.gbox5.setStyleSheet( "QGroupBox#GroupBox{border: None;background-color:#383838}") f = open("plotter.txt", "w") f.write("dark") f.close() self.s = "dark" self.pw.setBackground(background=None) if self.after == True: self.plotx() pixmap1 = QPixmap("auto-button_dark.png") button_icon1 = QIcon(pixmap1) self.button_2.setStyleSheet("border:none;background-color:#383838") self.button_2.setIcon(button_icon1) pixmap2 = QPixmap("help_dark.png") button_icon2 = QIcon(pixmap2) self.button_help.setIcon(button_icon2) self.button_help.setStyleSheet("border:none;background-color:#383838") if self.check_text == True: self.gbox6.setStyleSheet( "QGroupBox {border: 2px solid #3d3d3d;background-color:#383838;color: " + self.font_color_black + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.text8.setStyleSheet( "border:None;background-color:#383838;border:None;color: " + self.font_color_black) self.text8.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) def light_mode(self): self.input.setMaximumWidth(250) self.input.setFixedSize(250, 150) self.gbox.setMaximumWidth(250) self.gbox.setFixedSize(250, 90) self.gbox3.setMaximumWidth(250) self.gbox3.setFixedSize(250, 90) self.gbox4.setMaximumWidth(250) self.gbox4.setFixedSize(250, 45) self.gbox_mode.setMaximumWidth(250) self.gbox_mode.setFixedSize(250, 50) self.gbox5.setMaximumWidth(270) self.input.setObjectName("input") self.input.setStyleSheet( "QGroupBox#input{border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox#input::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox.setStyleSheet( "QGroupBox {border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox4.setStyleSheet( "QGroupBox {border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.gbox_mode.setStyleSheet( "QGroupBox {border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.plot.setStyleSheet("color: " + self.font_color) self.setStyleSheet("background-color:white;") self.label1.setStyleSheet("background-color:#f5f6f7;color: " + self.font_color) self.label2.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.label3.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.label4.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.label5.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.label6.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.rbutton1.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.rbutton2.setStyleSheet("background-color:#f5f6f7;color:" + self.font_color) self.rbutton1.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.rbutton2.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) self.label1.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label2.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label3.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label4.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label5.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.label6.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text1.setStyleSheet("background-color:white") self.text2.setStyleSheet("background-color:white") self.text3.setStyleSheet("background-color:white") self.text4.setStyleSheet("background-color:white") self.text5.setStyleSheet("background-color:white") self.text6.setStyleSheet("background-color:white") self.text7.setStyleSheet("background-color:white") self.button_save.setStyleSheet( " QPushButton{border: 1px solid #adadad;Text-align:center;background:#e1e1e1; color:black}" "QPushButton::hover{border: 1px solid #adadad;Text-align:center;background:#d8d7d7}" "QPushButton::Pressed{border: 1px solid #adadad;Text-align:center;background:#f5f6f7}" ) self.button.setStyleSheet( " QPushButton{border: 1px solid #adadad;Text-align:center;background:#e1e1e1; color:black}" "QPushButton::hover{border: 1px solid #adadad;Text-align:center;background:#d8d7d7}" "QPushButton::Pressed{border: 1px solid #adadad;Text-align:center;background:#f5f6f7}" ) self.text1.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text2.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text3.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text4.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text5.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text6.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.text7.setFont(QFont(self.font_type, self.font_size, QFont.Normal)) self.gbox5.setObjectName("GroupBox") self.gbox5.setStyleSheet( "QGroupBox#GroupBox{border: None;background-color:#f5f6f7}") f = open("plotter.txt", "w") f.write("light") f.close() self.s = "light" self.pw.setBackground(background=None) if self.after == True: self.plotx() pixmap2 = QPixmap("auto-button.png") button_icon2 = QIcon(pixmap2) self.button_2.setStyleSheet("border:none;background-color:#f5f6f7") self.button_2.setIcon(button_icon2) pixmap2 = QPixmap("help_light.png") button_icon2 = QIcon(pixmap2) self.button_help.setIcon(button_icon2) self.button_help.setStyleSheet("border:none;background-color:#f5f6f7") if self.check_text == True: self.gbox6.setStyleSheet( "QGroupBox {border: 2px solid #e6e6e6;background-color:#f5f6f7;color: " + self.font_color + ";margin-top: 6px;}" + "QGroupBox::title {subcontrol-origin:margin;left:8px;padding: 0px 0px 0px 0px;}" ) self.text8.setStyleSheet( "border:None;background-color:#f5f6f7;color: " + self.font_color) self.text8.setFont( QFont(self.font_type, self.font_size, QFont.Normal)) def export(self): self.exportdialog = exportDialog.ExportDialog(self.pw.plotItem.scene()) name = QFileDialog.getSaveFileName( self, 'Save File', "", "PNG (*.PNG;*.PNG);;CSV (*.CSV);;SVG(*.SVG)", "", QFileDialog.Options()) if name[0] != "": if "PNG" in name[1]: if self.s == "dark": self.pw.setBackground(background=(0, 0, 0)) else: self.pw.setBackground(background=(255, 255, 255)) exporter = pg.exporters.ImageExporter(self.pw.plotItem) exporter.export(name[0]) self.pw.setBackground(background=None) elif "CSV" in name[1]: exporter = pg.exporters.CSVExporter(self.pw.plotItem) exporter.export(name[0]) elif "SVG" in name[1]: if self.s == "dark": self.pw.setBackground(background=(0, 0, 0)) else: self.pw.setBackground(background=(255, 255, 255)) exporter = pg.exporters.SVGExporter(self.pw.plotItem) exporter.export(name[0]) self.pw.setBackground(background=None)
def initialise(self): self.settingsFile = os.path.join(self.findNukeHomeDir(), "deadline_settings.ini") print("Loading settings: " + self.settingsFile) # Initialize the submission settings. self.settings = QSettings(self.settingsFile, QSettings.IniFormat) print("Grabbing submitter info...") try: dcOutput = CallDeadlineCommand([ "-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Hiero/Main", "RepoDir:submission/Integration/Main", ], useDeadlineBg=True) output = json.loads(dcOutput, encoding="utf-8") except ValueError as e: print("Unable to get submitter info from Deadline:\n\n" + e.message) raise if output["ok"]: self.submissionInfo = output["result"] else: raise ValueError( "DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output["result"]) # Get the Deadline temp directory. deadlineHome = self.submissionInfo["UserHomeDir"].strip() self.deadlineTemp = os.path.join(deadlineHome, "temp") self.integrationDir = self.submissionInfo["RepoDirs"][ "submission/Integration/Main"].strip() # Get maximum priority. maximumPriority = self.submissionInfo["MaxPriority"] # Collect the pools and groups. pools = self.submissionInfo["Pools"] secondaryPools = [""] secondaryPools.extend(pools) groups = self.submissionInfo["Groups"] # Set up the other default arrays. onJobComplete = ("Nothing", "Archive", "Delete") nukeVersions = ("6.0", "6.1", "6.2", "6.3", "6.4", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", "9.4", "10.0", "10.1", "10.2", "10.3", "10.4", "11.0", "11.1", "11.2", "11.3") buildsToForce = ("None", "32bit", "64bit") # Main Window mainWindow = hiero.ui.mainWindow() dialog = QDialog(mainWindow) self.dialog = dialog dialog.setWindowTitle("Submit to Deadline (and render with Nuke)") # Main Layout topLayout = QVBoxLayout() dialog.setLayout(topLayout) tabWidget = QTabWidget(dialog) jobTab = QWidget() jobTabLayout = QVBoxLayout() jobTab.setLayout(jobTabLayout) # Job Info Layout jobInfoGroupBox = QGroupBox("Job Description") jobTabLayout.addWidget(jobInfoGroupBox) jobInfoLayout = QGridLayout() jobInfoGroupBox.setLayout(jobInfoLayout) # Job Name jobInfoLayout.addWidget(QLabel("Job Name"), 0, 0) jobNameWidget = QLineEdit(self.settings.value("JobName", "")) jobInfoLayout.addWidget(jobNameWidget, 0, 1) # Comment jobInfoLayout.addWidget(QLabel("Comment"), 1, 0) commentWidget = QLineEdit(self.settings.value("Comment", "")) jobInfoLayout.addWidget(commentWidget, 1, 1) # Department jobInfoLayout.addWidget(QLabel("Department"), 2, 0) departmentWidget = QLineEdit(self.settings.value("Department", "")) jobInfoLayout.addWidget(departmentWidget, 2, 1) # Job Options Layout jobOptionsGroupBox = QGroupBox("Job Options") jobTabLayout.addWidget(jobOptionsGroupBox) jobOptionsLayout = QGridLayout() jobOptionsGroupBox.setLayout(jobOptionsLayout) # Pool jobOptionsLayout.addWidget(QLabel("Pool"), 0, 0) poolWidget = QComboBox() for pool in pools: poolWidget.addItem(pool) defaultPool = self.settings.value("Pool", "none") defaultIndex = poolWidget.findText(defaultPool) if defaultIndex != -1: poolWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(poolWidget, 0, 1, 1, 3) # Secondary Pool jobOptionsLayout.addWidget(QLabel("Secondary Pool"), 1, 0) secondaryPoolWidget = QComboBox() for secondaryPool in secondaryPools: secondaryPoolWidget.addItem(secondaryPool) defaultSecondaryPool = self.settings.value("SecondaryPool", "") defaultIndex = secondaryPoolWidget.findText(defaultSecondaryPool) if defaultIndex != -1: secondaryPoolWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(secondaryPoolWidget, 1, 1, 1, 3) # Group jobOptionsLayout.addWidget(QLabel("Group"), 2, 0) groupWidget = QComboBox() for group in groups: groupWidget.addItem(group) defaultGroup = self.settings.value("Group", "none") defaultIndex = groupWidget.findText(defaultGroup) if defaultIndex != -1: groupWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(groupWidget, 2, 1, 1, 3) # Priority initPriority = int(self.settings.value("Priority", "50")) if initPriority > maximumPriority: initPriority = maximumPriority / 2 jobOptionsLayout.addWidget(QLabel("Priority"), 3, 0) priorityWidget = QSpinBox() priorityWidget.setRange(0, maximumPriority) priorityWidget.setValue(initPriority) jobOptionsLayout.addWidget(priorityWidget, 3, 1) # Task Timeout jobOptionsLayout.addWidget(QLabel("Task Timeout"), 4, 0) taskTimeoutWidget = QSpinBox() taskTimeoutWidget.setRange(0, 1000000) taskTimeoutWidget.setValue(int(self.settings.value("TaskTimeout", "0"))) jobOptionsLayout.addWidget(taskTimeoutWidget, 4, 1) # Auto Task Timeout autoTaskTimeoutWidget = QCheckBox("Enable Auto Task Timeout") autoTaskTimeoutWidget.setChecked( strtobool(self.settings.value("AutoTaskTimeout", "False"))) jobOptionsLayout.addWidget(autoTaskTimeoutWidget, 4, 2) # Concurrent Tasks jobOptionsLayout.addWidget(QLabel("Concurrent Tasks"), 5, 0) concurrentTasksWidget = QSpinBox() concurrentTasksWidget.setRange(1, 16) concurrentTasksWidget.setValue( int(self.settings.value("ConcurrentTasks", "1"))) jobOptionsLayout.addWidget(concurrentTasksWidget, 5, 1) # Limit Tasks To Slave's Task Limit limitConcurrentTasksWidget = QCheckBox( "Limit Tasks To Slave's Task Limit") limitConcurrentTasksWidget.setChecked( strtobool(self.settings.value("LimitConcurrentTasks", "True"))) jobOptionsLayout.addWidget(limitConcurrentTasksWidget, 5, 2) # Machine Limit jobOptionsLayout.addWidget(QLabel("Machine Limit"), 6, 0) machineLimitWidget = QSpinBox() machineLimitWidget.setRange(0, 1000000) machineLimitWidget.setValue( int(self.settings.value("MachineLimit", "1"))) jobOptionsLayout.addWidget(machineLimitWidget, 6, 1) # Machine List Is A Blacklist isBlacklistWidget = QCheckBox("Machine List Is A Blacklist") isBlacklistWidget.setChecked( strtobool(self.settings.value("IsBlacklist", "False"))) jobOptionsLayout.addWidget(isBlacklistWidget, 6, 2) # Machine List jobOptionsLayout.addWidget(QLabel("Machine List"), 7, 0) machineListWidget = QLineEdit(self.settings.value("MachineList", "")) jobOptionsLayout.addWidget(machineListWidget, 7, 1, 1, 2) def browseMachineList(): output = CallDeadlineCommand( ["-selectmachinelist", str(machineListWidget.text())], False) output = output.replace("\r", "").replace("\n", "") if output != "Action was cancelled by user": machineListWidget.setText(output) machineListButton = QPushButton("Browse") machineListButton.pressed.connect(browseMachineList) jobOptionsLayout.addWidget(machineListButton, 7, 3) # Limits jobOptionsLayout.addWidget(QLabel("Limits"), 8, 0) limitsWidget = QLineEdit(self.settings.value("Limits", "")) jobOptionsLayout.addWidget(limitsWidget, 8, 1, 1, 2) def browseLimitList(): output = CallDeadlineCommand( ["-selectlimitgroups", str(limitsWidget.text())], False) output = output.replace("\r", "").replace("\n", "") if output != "Action was cancelled by user": limitsWidget.setText(output) limitsButton = QPushButton("Browse") limitsButton.pressed.connect(browseLimitList) jobOptionsLayout.addWidget(limitsButton, 8, 3) # On Job Complete jobOptionsLayout.addWidget(QLabel("On Job Complete"), 9, 0) onJobCompleteWidget = QComboBox() for option in onJobComplete: onJobCompleteWidget.addItem(option) defaultOption = self.settings.value("OnJobComplete", "Nothing") defaultIndex = onJobCompleteWidget.findText(defaultOption) if defaultIndex != -1: onJobCompleteWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(onJobCompleteWidget, 9, 1) # Submit Job As Suspended submitSuspendedWidget = QCheckBox("Submit Job As Suspended") submitSuspendedWidget.setChecked( strtobool(self.settings.value("SubmitSuspended", "False"))) jobOptionsLayout.addWidget(submitSuspendedWidget, 9, 2) # Nuke Options nukeOptionsGroupBox = QGroupBox("Nuke Options") jobTabLayout.addWidget(nukeOptionsGroupBox) nukeOptionsLayout = QGridLayout() nukeOptionsGroupBox.setLayout(nukeOptionsLayout) # Version nukeOptionsLayout.addWidget(QLabel("Version"), 0, 0) versionWidget = QComboBox() for version in nukeVersions: versionWidget.addItem(version) defaultVersion = self.settings.value("Version", "7.0") defaultIndex = versionWidget.findText(defaultVersion) if defaultIndex != -1: versionWidget.setCurrentIndex(defaultIndex) nukeOptionsLayout.addWidget(versionWidget, 0, 1) # Submit Nuke Script File With Job submitScriptWidget = QCheckBox("Submit Nuke Script File With Job") submitScriptWidget.setChecked( strtobool(self.settings.value("SubmitScript", "False"))) nukeOptionsLayout.addWidget(submitScriptWidget, 0, 2) # Build To Force nukeOptionsLayout.addWidget(QLabel("Build To Force"), 1, 0) buildWidget = QComboBox() for build in buildsToForce: buildWidget.addItem(build) defaultBuild = self.settings.value("Build", "None") defaultIndex = buildWidget.findText(defaultBuild) if defaultIndex != -1: buildWidget.setCurrentIndex(defaultIndex) nukeOptionsLayout.addWidget(buildWidget, 1, 1) # Render With NukeX useNukeXWidget = QCheckBox("Render With NukeX") useNukeXWidget.setChecked( strtobool(self.settings.value("UseNukeX", "False"))) nukeOptionsLayout.addWidget(useNukeXWidget, 1, 2) # Max RAM Usage (MB) nukeOptionsLayout.addWidget(QLabel("Max RAM Usage (MB)"), 2, 0) memoryWidget = QSpinBox() memoryWidget.setRange(0, 5000) memoryWidget.setValue(int(self.settings.value("Memory", "0"))) nukeOptionsLayout.addWidget(memoryWidget, 2, 1) # Continue On Error continueOnErrorWidget = QCheckBox("Continue On Error") continueOnErrorWidget.setChecked( strtobool(self.settings.value("ContinueOnError", "False"))) nukeOptionsLayout.addWidget(continueOnErrorWidget, 2, 2) # Threads nukeOptionsLayout.addWidget(QLabel("Threads"), 3, 0) threadsWidget = QSpinBox() threadsWidget.setRange(0, 256) threadsWidget.setValue(int(self.settings.value("Threads", "0"))) nukeOptionsLayout.addWidget(threadsWidget, 3, 1) # Use Batch Mode batchModeWidget = QCheckBox("Use Batch Mode") batchModeWidget.setChecked( strtobool(self.settings.value("BatchMode", "False"))) nukeOptionsLayout.addWidget(batchModeWidget, 3, 2) # Frames Per Task nukeOptionsLayout.addWidget(QLabel("Frames Per Task"), 4, 0) framesPerTaskWidget = QSpinBox() framesPerTaskWidget.setRange(1, 1000000) framesPerTaskWidget.setValue( int(self.settings.value("FramesPerTask", "1"))) nukeOptionsLayout.addWidget(framesPerTaskWidget, 4, 1) nukeOptionsLayout.addWidget( QLabel("(this only affects non-movie jobs)"), 4, 2) tabWidget.addTab(jobTab, "Job Options") # Button Box (Extra work required to get the custom ordering we want) self.pipelineToolStatusLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) pipelineToolStatus = self.retrievePipelineToolStatus() self.updatePipelineToolStatusLabel(pipelineToolStatus) integrationButton = QPushButton("Pipeline Tools") integrationButton.clicked.connect(self.OpenIntegrationWindow) submitButton = QPushButton("Submit") submitButton.clicked.connect(dialog.accept) submitButton.setDefault(True) cancelButton = QPushButton("Cancel") cancelButton.clicked.connect(dialog.reject) buttonGroupBox = QGroupBox() buttonLayout = QGridLayout() buttonLayout.setColumnStretch( 0, 1) # Makes the pipeline status label expand, not the buttons buttonGroupBox.setLayout(buttonLayout) buttonGroupBox.setAlignment(Qt.AlignRight) buttonGroupBox.setFlat(True) buttonLayout.addWidget(self.pipelineToolStatusLabel, 0, 0) buttonLayout.addWidget(integrationButton, 0, 1) buttonLayout.addWidget(submitButton, 0, 2) buttonLayout.addWidget(cancelButton, 0, 3) topLayout.addWidget(tabWidget) topLayout.addWidget(buttonGroupBox) # Show the dialog. result = (dialog.exec_() == QDialog.DialogCode.Accepted) if result: # Need to pass integration dir path to render task self.settings.setValue("IntegrationDir", self.integrationDir) self.settings.setValue("JobName", jobNameWidget.text()) self.settings.setValue("Comment", commentWidget.text()) self.settings.setValue("Department", departmentWidget.text()) self.settings.setValue("Pool", poolWidget.currentText()) self.settings.setValue("SecondaryPool", secondaryPoolWidget.currentText()) self.settings.setValue("Group", groupWidget.currentText()) self.settings.setValue("Priority", priorityWidget.value()) self.settings.setValue("TaskTimeout", taskTimeoutWidget.value()) self.settings.setValue("AutoTaskTimeout", str(autoTaskTimeoutWidget.isChecked())) self.settings.setValue("ConcurrentTasks", concurrentTasksWidget.value()) self.settings.setValue("LimitConcurrentTasks", str(limitConcurrentTasksWidget.isChecked())) self.settings.setValue("MachineLimit", machineLimitWidget.value()) self.settings.setValue("IsBlacklist", str(isBlacklistWidget.isChecked())) self.settings.setValue("MachineList", machineListWidget.text()) self.settings.setValue("Limits", limitsWidget.text()) self.settings.setValue("OnJobComplete", onJobCompleteWidget.currentText()) self.settings.setValue("SubmitSuspended", str(submitSuspendedWidget.isChecked())) self.settings.setValue("Version", versionWidget.currentText()) self.settings.setValue("SubmitScript", str(submitScriptWidget.isChecked())) self.settings.setValue("Build", buildWidget.currentText()) self.settings.setValue("UseNukeX", str(useNukeXWidget.isChecked())) self.settings.setValue("FramesPerTask", framesPerTaskWidget.value()) self.settings.setValue("ContinueOnError", str(continueOnErrorWidget.isChecked())) self.settings.setValue("Threads", threadsWidget.value()) self.settings.setValue("BatchMode", str(batchModeWidget.isChecked())) self.settings.setValue("Memory", memoryWidget.value()) print("Saving settings: " + self.settingsFile) self.settings.sync() else: print("Submission canceled") self.settings = None # Not sure if there is a better way to stop the export process. This works, but it leaves all the tasks # in the Queued state. self.setError("Submission was canceled")