def addThreadList(self,threads): if not self.groupBoxThreadInfo: self.groupBoxThreadInfo = QGroupBox() self.threadInfo = QLabel("Thread Info.") self.groupBoxThreadInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") if not self.threadvbox: self.threadvbox = QVBoxLayout() if not self.listThread: self.listThread = QListWidget() self.listThread.setFixedWidth(200) self.listThread.setSelectionMode(QAbstractItemView.MultiSelection) QtCore.QObject.connect(self.listThread, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), self.toggleThreadDisplay) self.threadvbox.addWidget(self.threadInfo) self.threadvbox.addWidget(self.listThread) self.groupBoxThreadInfo.setLayout(self.threadvbox) self.addWidget(self.groupBoxThreadInfo) self.groupBoxThreadInfo.setSizePolicy(self.sizePolicy) for id in threads: item = QListWidgetItem(id) self.listThread.addItem(item)
def createRotableGroupBox(self): self.rotableGroupBox = QGroupBox("Rotable Widgets") self.rotableWidgets.append(QSpinBox()) self.rotableWidgets.append(QSlider()) self.rotableWidgets.append(QDial()) self.rotableWidgets.append(QProgressBar()) count = len(self.rotableWidgets) for i in range(count): self.rotableWidgets[i].valueChanged[int].\ connect(self.rotableWidgets[(i+1) % count].setValue) self.rotableLayout = QGridLayout() self.rotableGroupBox.setLayout(self.rotableLayout) self.rotateWidgets()
def createOptionsGroupBox(self): self.optionsGroupBox = QGroupBox("Options") buttonsOrientationLabel = QLabel("Orientation of buttons:") buttonsOrientationComboBox = QComboBox() buttonsOrientationComboBox.addItem("Horizontal", Qt.Horizontal) buttonsOrientationComboBox.addItem("Vertical", Qt.Vertical) buttonsOrientationComboBox.currentIndexChanged[int].connect(self.buttonsOrientationChanged) self.buttonsOrientationComboBox = buttonsOrientationComboBox optionsLayout = QGridLayout() optionsLayout.addWidget(buttonsOrientationLabel, 0, 0) optionsLayout.addWidget(self.buttonsOrientationComboBox, 0, 1) optionsLayout.setColumnStretch(2, 1) self.optionsGroupBox.setLayout(optionsLayout)
def create_gestures_group_box(self): gestures_group_box = QGroupBox() gridLayout = QGridLayout() icon_size = QSize(150, 100) path_to_images = '../gestures/photos/' button_nothing = QPushButton() button_nothing.clicked.connect( lambda: self.on_gesture_selection(Gestures.nothing)) button_nothing.setIcon(QIcon(path_to_images + 'nothing.jpg')) gridLayout.addWidget(button_nothing, 0, 0) self.gesture_buttons[Gestures.nothing] = button_nothing button_rock = QPushButton() button_rock.clicked.connect( lambda: self.on_gesture_selection(Gestures.rock)) button_rock.setIcon(QIcon(path_to_images + 'rock.jpg')) gridLayout.addWidget(button_rock, 0, 1) self.gesture_buttons[Gestures.rock] = button_rock button_scissors = QPushButton() button_scissors.clicked.connect( lambda: self.on_gesture_selection(Gestures.scissors)) button_scissors.setIcon(QIcon(path_to_images + 'scissors.jpg')) gridLayout.addWidget(button_scissors, 0, 2) self.gesture_buttons[Gestures.scissors] = button_scissors button_paper = QPushButton() button_paper.clicked.connect( lambda: self.on_gesture_selection(Gestures.paper)) button_paper.setIcon(QIcon(path_to_images + 'paper.jpg')) gridLayout.addWidget(button_paper, 1, 0) self.gesture_buttons[Gestures.paper] = button_paper button_ok = QPushButton() button_ok.clicked.connect( lambda: self.on_gesture_selection(Gestures.ok)) button_ok.setIcon(QIcon(path_to_images + 'ok.jpg')) gridLayout.addWidget(button_ok, 1, 1) self.gesture_buttons[Gestures.ok] = button_ok button_horns = QPushButton() button_horns.clicked.connect( lambda: self.on_gesture_selection(Gestures.horns)) button_horns.setIcon(QIcon(path_to_images + 'horns.jpg')) gridLayout.addWidget(button_horns, 1, 2) self.gesture_buttons[Gestures.horns] = button_horns button_shaka = QPushButton() button_shaka.clicked.connect( lambda: self.on_gesture_selection(Gestures.shaka)) button_shaka.setIcon(QIcon(path_to_images + 'shaka.jpg')) gridLayout.addWidget(button_shaka, 2, 0) self.gesture_buttons[Gestures.shaka] = button_shaka button_gun = QPushButton() button_gun.clicked.connect( lambda: self.on_gesture_selection(Gestures.gun)) button_gun.setIcon(QIcon(path_to_images + 'gun.jpg')) gridLayout.addWidget(button_gun, 2, 1) self.gesture_buttons[Gestures.gun] = button_gun button_thumb = QPushButton() button_thumb.clicked.connect( lambda: self.on_gesture_selection(Gestures.thumbs_up)) button_thumb.setIcon(QIcon(path_to_images + 'thumbs_up.jpg')) gridLayout.addWidget(button_thumb, 2, 2) self.gesture_buttons[Gestures.thumbs_up] = button_thumb for button in self.gesture_buttons.values(): button.setEnabled(False) button.setCheckable(True) button.setIconSize(icon_size) gestures_group_box.setLayout(gridLayout) return gestures_group_box
class QSettingsWindow(QDialog): def __init__(self, game: Game): super(QSettingsWindow, self).__init__() self.game = game self.pluginsPage = None self.pluginsOptionsPage = None self.campaign_management_page = QWidget() self.setModal(True) self.setWindowTitle("Settings") self.setWindowIcon(CONST.ICONS["Settings"]) self.setMinimumSize(600, 250) self.initUi() def initUi(self): self.layout = QGridLayout() self.categoryList = QListView() self.right_layout = QStackedLayout() self.categoryList.setMaximumWidth(175) self.categoryModel = QStandardItemModel(self.categoryList) self.categoryList.setIconSize(QSize(32, 32)) self.initDifficultyLayout() difficulty = QStandardItem("Difficulty") difficulty.setIcon(CONST.ICONS["Missile"]) difficulty.setEditable(False) difficulty.setSelectable(True) self.categoryModel.appendRow(difficulty) self.right_layout.addWidget(self.difficultyPage) self.init_campaign_management_layout() campaign_management = QStandardItem("Campaign Management") campaign_management.setIcon(CONST.ICONS["Money"]) campaign_management.setEditable(False) campaign_management.setSelectable(True) self.categoryModel.appendRow(campaign_management) self.right_layout.addWidget(self.campaign_management_page) self.initGeneratorLayout() generator = QStandardItem("Mission Generator") generator.setIcon(CONST.ICONS["Generator"]) generator.setEditable(False) generator.setSelectable(True) self.categoryModel.appendRow(generator) self.right_layout.addWidget(self.generatorPage) self.initCheatLayout() cheat = QStandardItem("Cheat Menu") cheat.setIcon(CONST.ICONS["Cheat"]) cheat.setEditable(False) cheat.setSelectable(True) self.categoryModel.appendRow(cheat) self.right_layout.addWidget(self.cheatPage) self.pluginsPage = PluginsPage() plugins = QStandardItem("LUA Plugins") plugins.setIcon(CONST.ICONS["Plugins"]) plugins.setEditable(False) plugins.setSelectable(True) self.categoryModel.appendRow(plugins) self.right_layout.addWidget(self.pluginsPage) self.pluginsOptionsPage = PluginOptionsPage() pluginsOptions = QStandardItem("LUA Plugins Options") pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"]) pluginsOptions.setEditable(False) pluginsOptions.setSelectable(True) self.categoryModel.appendRow(pluginsOptions) self.right_layout.addWidget(self.pluginsOptionsPage) self.categoryList.setSelectionBehavior(QAbstractItemView.SelectRows) self.categoryList.setModel(self.categoryModel) self.categoryList.selectionModel().setCurrentIndex( self.categoryList.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) self.categoryList.selectionModel().selectionChanged.connect( self.onSelectionChanged) self.layout.addWidget(self.categoryList, 0, 0, 1, 1) self.layout.addLayout(self.right_layout, 0, 1, 5, 1) self.setLayout(self.layout) def init(self): pass def initDifficultyLayout(self): self.difficultyPage = QWidget() self.difficultyLayout = QVBoxLayout() self.difficultyLayout.setAlignment(Qt.AlignTop) self.difficultyPage.setLayout(self.difficultyLayout) # DCS AI difficulty settings self.aiDifficultySettings = QGroupBox("AI Difficulty") self.aiDifficultyLayout = QGridLayout() self.playerCoalitionSkill = QComboBox() self.enemyCoalitionSkill = QComboBox() self.enemyAASkill = QComboBox() for skill in CONST.SKILL_OPTIONS: self.playerCoalitionSkill.addItem(skill) self.enemyCoalitionSkill.addItem(skill) self.enemyAASkill.addItem(skill) self.playerCoalitionSkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.player_skill)) self.enemyCoalitionSkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.enemy_skill)) self.enemyAASkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.enemy_vehicle_skill)) self.player_income = TenthsSpinSlider( "Player income multiplier", 1, 50, int(self.game.settings.player_income_multiplier * 10), ) self.player_income.spinner.valueChanged.connect(self.applySettings) self.enemy_income = TenthsSpinSlider( "Enemy income multiplier", 1, 50, int(self.game.settings.enemy_income_multiplier * 10), ) self.enemy_income.spinner.valueChanged.connect(self.applySettings) self.playerCoalitionSkill.currentIndexChanged.connect( self.applySettings) self.enemyCoalitionSkill.currentIndexChanged.connect( self.applySettings) self.enemyAASkill.currentIndexChanged.connect(self.applySettings) # Mission generation settings related to difficulty self.missionSettings = QGroupBox("Mission Difficulty") self.missionLayout = QGridLayout() self.manpads = QCheckBox() self.manpads.setChecked(self.game.settings.manpads) self.manpads.toggled.connect(self.applySettings) self.noNightMission = QCheckBox() self.noNightMission.setChecked(self.game.settings.night_disabled) self.noNightMission.toggled.connect(self.applySettings) # DCS Mission options self.missionRestrictionsSettings = QGroupBox("Mission Restrictions") self.missionRestrictionsLayout = QGridLayout() self.difficultyLabel = QComboBox() [self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS] self.difficultyLabel.setCurrentIndex( CONST.LABELS_OPTIONS.index(self.game.settings.labels)) self.difficultyLabel.currentIndexChanged.connect(self.applySettings) self.mapVisibiitySelection = QComboBox() self.mapVisibiitySelection.addItem("All", ForcedOptions.Views.All) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All: self.mapVisibiitySelection.setCurrentIndex(0) self.mapVisibiitySelection.addItem("Fog of War", ForcedOptions.Views.Allies) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies: self.mapVisibiitySelection.setCurrentIndex(1) self.mapVisibiitySelection.addItem("Allies Only", ForcedOptions.Views.OnlyAllies) if (self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies): self.mapVisibiitySelection.setCurrentIndex(2) self.mapVisibiitySelection.addItem("Own Aircraft Only", ForcedOptions.Views.MyAircraft) if (self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft): self.mapVisibiitySelection.setCurrentIndex(3) self.mapVisibiitySelection.addItem("Map Only", ForcedOptions.Views.OnlyMap) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap: self.mapVisibiitySelection.setCurrentIndex(4) self.mapVisibiitySelection.currentIndexChanged.connect( self.applySettings) self.ext_views = QCheckBox() self.ext_views.setChecked(self.game.settings.external_views_allowed) self.ext_views.toggled.connect(self.applySettings) self.aiDifficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0) self.aiDifficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight) self.aiDifficultyLayout.addWidget(QLabel("Enemy coalition skill"), 1, 0) self.aiDifficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight) self.aiDifficultyLayout.addWidget( QLabel("Enemy AA and vehicles skill"), 2, 0) self.aiDifficultyLayout.addWidget(self.enemyAASkill, 2, 1, Qt.AlignRight) self.aiDifficultyLayout.addLayout(self.player_income, 3, 0) self.aiDifficultyLayout.addLayout(self.enemy_income, 4, 0) self.aiDifficultySettings.setLayout(self.aiDifficultyLayout) self.difficultyLayout.addWidget(self.aiDifficultySettings) self.missionLayout.addWidget(QLabel("Manpads on frontlines"), 0, 0) self.missionLayout.addWidget(self.manpads, 0, 1, Qt.AlignRight) self.missionLayout.addWidget(QLabel("No night missions"), 1, 0) self.missionLayout.addWidget(self.noNightMission, 1, 1, Qt.AlignRight) self.missionSettings.setLayout(self.missionLayout) self.difficultyLayout.addWidget(self.missionSettings) self.missionRestrictionsLayout.addWidget(QLabel("In Game Labels"), 0, 0) self.missionRestrictionsLayout.addWidget(self.difficultyLabel, 0, 1, Qt.AlignRight) self.missionRestrictionsLayout.addWidget( QLabel("Map visibility options"), 1, 0) self.missionRestrictionsLayout.addWidget(self.mapVisibiitySelection, 1, 1, Qt.AlignRight) self.missionRestrictionsLayout.addWidget( QLabel("Allow external views"), 2, 0) self.missionRestrictionsLayout.addWidget(self.ext_views, 2, 1, Qt.AlignRight) self.missionRestrictionsSettings.setLayout( self.missionRestrictionsLayout) self.difficultyLayout.addWidget(self.missionRestrictionsSettings) def init_campaign_management_layout(self) -> None: campaign_layout = QVBoxLayout() campaign_layout.setAlignment(Qt.AlignTop) self.campaign_management_page.setLayout(campaign_layout) general = QGroupBox("General") campaign_layout.addWidget(general) general_layout = QGridLayout() general.setLayout(general_layout) def set_restict_weapons_by_date(value: bool) -> None: self.game.settings.restrict_weapons_by_date = value restrict_weapons = QCheckBox() restrict_weapons.setChecked( self.game.settings.restrict_weapons_by_date) restrict_weapons.toggled.connect(set_restict_weapons_by_date) tooltip_text = ( "Restricts weapon availability based on the campaign date. Data is " "extremely incomplete so does not affect all weapons.") restrict_weapons.setToolTip(tooltip_text) restrict_weapons_label = QLabel("Restrict weapons by date (WIP)") restrict_weapons_label.setToolTip(tooltip_text) general_layout.addWidget(restrict_weapons_label, 0, 0) general_layout.addWidget(restrict_weapons, 0, 1, Qt.AlignRight) automation = QGroupBox("HQ Automation") campaign_layout.addWidget(automation) automation_layout = QGridLayout() automation.setLayout(automation_layout) def set_runway_automation(value: bool) -> None: self.game.settings.automate_runway_repair = value def set_front_line_automation(value: bool) -> None: self.game.settings.automate_front_line_reinforcements = value def set_aircraft_automation(value: bool) -> None: self.game.settings.automate_aircraft_reinforcements = value runway_repair = QCheckBox() runway_repair.setChecked(self.game.settings.automate_runway_repair) runway_repair.toggled.connect(set_runway_automation) automation_layout.addWidget(QLabel("Automate runway repairs"), 0, 0) automation_layout.addWidget(runway_repair, 0, 1, Qt.AlignRight) front_line = QCheckBox() front_line.setChecked( self.game.settings.automate_front_line_reinforcements) front_line.toggled.connect(set_front_line_automation) automation_layout.addWidget(QLabel("Automate front-line purchases"), 1, 0) automation_layout.addWidget(front_line, 1, 1, Qt.AlignRight) aircraft = QCheckBox() aircraft.setChecked( self.game.settings.automate_aircraft_reinforcements) aircraft.toggled.connect(set_aircraft_automation) automation_layout.addWidget(QLabel("Automate aircraft purchases"), 2, 0) automation_layout.addWidget(aircraft, 2, 1, Qt.AlignRight) def initGeneratorLayout(self): self.generatorPage = QWidget() self.generatorLayout = QVBoxLayout() self.generatorLayout.setAlignment(Qt.AlignTop) self.generatorPage.setLayout(self.generatorLayout) self.gameplay = QGroupBox("Gameplay") self.gameplayLayout = QGridLayout() self.gameplayLayout.setAlignment(Qt.AlignTop) self.gameplay.setLayout(self.gameplayLayout) self.supercarrier = QCheckBox() self.supercarrier.setChecked(self.game.settings.supercarrier) self.supercarrier.toggled.connect(self.applySettings) self.generate_marks = QCheckBox() self.generate_marks.setChecked(self.game.settings.generate_marks) self.generate_marks.toggled.connect(self.applySettings) self.never_delay_players = QCheckBox() self.never_delay_players.setChecked( self.game.settings.never_delay_player_flights) self.never_delay_players.toggled.connect(self.applySettings) self.never_delay_players.setToolTip( "When checked, player flights with a delayed start time will be " "spawned immediately. AI wingmen may begin startup immediately.") self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0) self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0) self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Never delay player flights"), 2, 0) self.gameplayLayout.addWidget(self.never_delay_players, 2, 1, Qt.AlignRight) start_type_label = QLabel( "Default start type for AI aircraft:<br /><strong>Warning: " + "Any option other than Cold breaks OCA/Aircraft missions.</strong>" ) start_type_label.setToolTip(START_TYPE_TOOLTIP) start_type = StartTypeComboBox(self.game.settings) start_type.setCurrentText(self.game.settings.default_start_type) self.gameplayLayout.addWidget(start_type_label, 3, 0) self.gameplayLayout.addWidget(start_type, 3, 1) self.performance = QGroupBox("Performance") self.performanceLayout = QGridLayout() self.performanceLayout.setAlignment(Qt.AlignTop) self.performance.setLayout(self.performanceLayout) self.smoke = QCheckBox() self.smoke.setChecked(self.game.settings.perf_smoke_gen) self.smoke.toggled.connect(self.applySettings) self.red_alert = QCheckBox() self.red_alert.setChecked(self.game.settings.perf_red_alert_state) self.red_alert.toggled.connect(self.applySettings) self.arti = QCheckBox() self.arti.setChecked(self.game.settings.perf_artillery) self.arti.toggled.connect(self.applySettings) self.moving_units = QCheckBox() self.moving_units.setChecked(self.game.settings.perf_moving_units) self.moving_units.toggled.connect(self.applySettings) self.infantry = QCheckBox() self.infantry.setChecked(self.game.settings.perf_infantry) self.infantry.toggled.connect(self.applySettings) self.destroyed_units = QCheckBox() self.destroyed_units.setChecked( self.game.settings.perf_destroyed_units) self.destroyed_units.toggled.connect(self.applySettings) self.culling = QCheckBox() self.culling.setChecked(self.game.settings.perf_culling) self.culling.toggled.connect(self.applySettings) self.culling_distance = QSpinBox() self.culling_distance.setMinimum(10) self.culling_distance.setMaximum(10000) self.culling_distance.setValue( self.game.settings.perf_culling_distance) self.culling_distance.valueChanged.connect(self.applySettings) self.culling_do_not_cull_carrier = QCheckBox() self.culling_do_not_cull_carrier.setChecked( self.game.settings.perf_do_not_cull_carrier) self.culling_do_not_cull_carrier.toggled.connect(self.applySettings) self.performanceLayout.addWidget( QLabel("Smoke visual effect on frontline"), 0, 0) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("SAM starts in RED alert mode"), 1, 0) self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0) self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0) self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Generate infantry squads along vehicles"), 4, 0) self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Include destroyed units carcass"), 6, 0) self.performanceLayout.addWidget(self.destroyed_units, 6, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QHorizontalSeparationLine(), 7, 0, 1, 2) self.performanceLayout.addWidget( QLabel("Culling of distant units enabled"), 8, 0) self.performanceLayout.addWidget(self.culling, 8, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Culling distance (km)"), 9, 0) self.performanceLayout.addWidget(self.culling_distance, 9, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Do not cull carrier's surroundings"), 10, 0) self.performanceLayout.addWidget(self.culling_do_not_cull_carrier, 10, 1, alignment=Qt.AlignRight) self.generatorLayout.addWidget(self.gameplay) self.generatorLayout.addWidget( QLabel( "Disabling settings below may improve performance, but will impact the overall quality of the experience." )) self.generatorLayout.addWidget(self.performance) def initCheatLayout(self): self.cheatPage = QWidget() self.cheatLayout = QVBoxLayout() self.cheatPage.setLayout(self.cheatLayout) self.cheat_options = CheatSettingsBox(self.game, self.applySettings) self.cheatLayout.addWidget(self.cheat_options) self.moneyCheatBox = QGroupBox("Money Cheat") self.moneyCheatBox.setAlignment(Qt.AlignTop) self.moneyCheatBoxLayout = QGridLayout() self.moneyCheatBox.setLayout(self.moneyCheatBoxLayout) cheats_amounts = [25, 50, 100, 200, 500, 1000, -25, -50, -100, -200] for i, amount in enumerate(cheats_amounts): if amount > 0: btn = QPushButton("Cheat +" + str(amount) + "M") btn.setProperty("style", "btn-success") else: btn = QPushButton("Cheat " + str(amount) + "M") btn.setProperty("style", "btn-danger") btn.clicked.connect(self.cheatLambda(amount)) self.moneyCheatBoxLayout.addWidget(btn, i / 2, i % 2) self.cheatLayout.addWidget(self.moneyCheatBox, stretch=1) def cheatLambda(self, amount): return lambda: self.cheatMoney(amount) def cheatMoney(self, amount): logging.info("CHEATING FOR AMOUNT : " + str(amount) + "M") self.game.budget += amount if amount > 0: self.game.informations.append( Information( "CHEATER", "You are a cheater and you should feel bad", self.game.turn, )) else: self.game.informations.append( Information("CHEATER", "You are still a cheater !", self.game.turn)) GameUpdateSignal.get_instance().updateGame(self.game) def applySettings(self): self.game.settings.player_skill = CONST.SKILL_OPTIONS[ self.playerCoalitionSkill.currentIndex()] self.game.settings.enemy_skill = CONST.SKILL_OPTIONS[ self.enemyCoalitionSkill.currentIndex()] self.game.settings.enemy_vehicle_skill = CONST.SKILL_OPTIONS[ self.enemyAASkill.currentIndex()] self.game.settings.player_income_multiplier = self.player_income.value self.game.settings.enemy_income_multiplier = self.enemy_income.value self.game.settings.manpads = self.manpads.isChecked() self.game.settings.labels = CONST.LABELS_OPTIONS[ self.difficultyLabel.currentIndex()] self.game.settings.night_disabled = self.noNightMission.isChecked() self.game.settings.map_coalition_visibility = ( self.mapVisibiitySelection.currentData()) self.game.settings.external_views_allowed = self.ext_views.isChecked() self.game.settings.generate_marks = self.generate_marks.isChecked() self.game.settings.never_delay_player_flights = ( self.never_delay_players.isChecked()) self.game.settings.supercarrier = self.supercarrier.isChecked() self.game.settings.perf_red_alert_state = self.red_alert.isChecked() self.game.settings.perf_smoke_gen = self.smoke.isChecked() self.game.settings.perf_artillery = self.arti.isChecked() self.game.settings.perf_moving_units = self.moving_units.isChecked() self.game.settings.perf_infantry = self.infantry.isChecked() self.game.settings.perf_destroyed_units = self.destroyed_units.isChecked( ) self.game.settings.perf_culling = self.culling.isChecked() self.game.settings.perf_culling_distance = int( self.culling_distance.value()) self.game.settings.perf_do_not_cull_carrier = ( self.culling_do_not_cull_carrier.isChecked()) self.game.settings.show_red_ato = self.cheat_options.show_red_ato self.game.settings.enable_frontline_cheats = ( self.cheat_options.show_frontline_cheat) self.game.settings.enable_base_capture_cheat = ( self.cheat_options.show_base_capture_cheat) self.game.compute_conflicts_position() GameUpdateSignal.get_instance().updateGame(self.game) def onSelectionChanged(self): index = self.categoryList.selectionModel().currentIndex().row() self.right_layout.setCurrentIndex(index)
def init_campaign_management_layout(self) -> None: campaign_layout = QVBoxLayout() campaign_layout.setAlignment(Qt.AlignTop) self.campaign_management_page.setLayout(campaign_layout) general = QGroupBox("General") campaign_layout.addWidget(general) general_layout = QGridLayout() general.setLayout(general_layout) def set_restict_weapons_by_date(value: bool) -> None: self.game.settings.restrict_weapons_by_date = value restrict_weapons = QCheckBox() restrict_weapons.setChecked( self.game.settings.restrict_weapons_by_date) restrict_weapons.toggled.connect(set_restict_weapons_by_date) tooltip_text = ( "Restricts weapon availability based on the campaign date. Data is " "extremely incomplete so does not affect all weapons.") restrict_weapons.setToolTip(tooltip_text) restrict_weapons_label = QLabel("Restrict weapons by date (WIP)") restrict_weapons_label.setToolTip(tooltip_text) general_layout.addWidget(restrict_weapons_label, 0, 0) general_layout.addWidget(restrict_weapons, 0, 1, Qt.AlignRight) automation = QGroupBox("HQ Automation") campaign_layout.addWidget(automation) automation_layout = QGridLayout() automation.setLayout(automation_layout) def set_runway_automation(value: bool) -> None: self.game.settings.automate_runway_repair = value def set_front_line_automation(value: bool) -> None: self.game.settings.automate_front_line_reinforcements = value def set_aircraft_automation(value: bool) -> None: self.game.settings.automate_aircraft_reinforcements = value runway_repair = QCheckBox() runway_repair.setChecked(self.game.settings.automate_runway_repair) runway_repair.toggled.connect(set_runway_automation) automation_layout.addWidget(QLabel("Automate runway repairs"), 0, 0) automation_layout.addWidget(runway_repair, 0, 1, Qt.AlignRight) front_line = QCheckBox() front_line.setChecked( self.game.settings.automate_front_line_reinforcements) front_line.toggled.connect(set_front_line_automation) automation_layout.addWidget(QLabel("Automate front-line purchases"), 1, 0) automation_layout.addWidget(front_line, 1, 1, Qt.AlignRight) aircraft = QCheckBox() aircraft.setChecked( self.game.settings.automate_aircraft_reinforcements) aircraft.toggled.connect(set_aircraft_automation) automation_layout.addWidget(QLabel("Automate aircraft purchases"), 2, 0) automation_layout.addWidget(aircraft, 2, 1, Qt.AlignRight)
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 StreamFieldsWidget(QDialog): """ A stream widget containing schema-specific properties. """ def __init__(self, parent): super().__init__() self.setParent(parent) self.setLayout(QGridLayout()) self.setWindowModality(Qt.WindowModal) self.setModal(True) self.minimum_spinbox_value = 0 self.maximum_spinbox_value = 100_000_000 self.advanced_options_enabled = False self.hs00_unimplemented_label = QLabel( "hs00 (Event histograms) has not yet been fully implemented.") self.schema_label = QLabel("Schema: ") self.schema_combo = QComboBox() self.topic_label = QLabel("Topic: ") self.topic_line_edit = QLineEdit() self.topic_line_edit.setPlaceholderText( "[broker][:port, default=9092]/topic") self.source_label = QLabel("Source: ") self.source_line_edit = QLineEdit() self.array_size_label = QLabel("Array size") self.array_size_spinbox = QSpinBox() self.array_size_spinbox.setMaximum(np.iinfo(np.int32).max) self.type_label = QLabel("Type: ") self.type_combo = QComboBox() self.type_combo.addItems(F142_TYPES) self.type_combo.setCurrentText("double") self.value_units_edit = QLineEdit() self.value_units_label = QLabel("Value Units:") self.show_advanced_options_button = QPushButton( text="Show/hide advanced options") self.show_advanced_options_button.setCheckable(True) self.show_advanced_options_button.clicked.connect( self.advanced_options_button_clicked) self._set_up_f142_group_box() self._set_up_ev42_group_box() self.scalar_radio = QRadioButton(text="Scalar") self.scalar_radio.clicked.connect(partial(self._show_array_size, False)) self.scalar_radio.setChecked(True) self.scalar_radio.clicked.emit() self.array_radio = QRadioButton(text="Array") self.array_radio.clicked.connect(partial(self._show_array_size, True)) self.schema_combo.currentTextChanged.connect(self._schema_type_changed) self.schema_combo.addItems([e.value for e in WriterModules]) self.ok_button = QPushButton("OK") self.ok_button.clicked.connect(self.parent().close) self.layout().addWidget(self.schema_label, 0, 0) self.layout().addWidget(self.schema_combo, 0, 1) self.layout().addWidget(self.topic_label, 1, 0) self.layout().addWidget(self.topic_line_edit, 1, 1) self.layout().addWidget(self.source_label, 2, 0) self.layout().addWidget(self.source_line_edit, 2, 1) self.layout().addWidget(self.value_units_label, 3, 0) self.layout().addWidget(self.value_units_edit, 3, 1) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.layout().addWidget(self.type_label, 4, 0) self.layout().addWidget(self.type_combo, 4, 1) self.layout().addWidget(self.scalar_radio, 5, 0) self.layout().addWidget(self.array_radio, 5, 1) self.layout().addWidget(self.array_size_label, 6, 0) self.layout().addWidget(self.array_size_spinbox, 6, 1) self.layout().addWidget(self.hs00_unimplemented_label, 7, 0, 1, 2) # Spans both rows self.layout().addWidget(self.show_advanced_options_button, 8, 0, 1, 2) self.layout().addWidget(self.f142_advanced_group_box, 9, 0, 1, 2) self.layout().addWidget(self.ev42_advanced_group_box, 10, 0, 1, 2) self.layout().addWidget(self.ok_button, 11, 0, 1, 2) self._schema_type_changed(self.schema_combo.currentText()) def advanced_options_button_clicked(self): self._show_advanced_options( show=self.show_advanced_options_button.isChecked()) def _set_up_ev42_group_box(self): """ Sets up the UI for ev42 advanced options. """ self.ev42_nexus_elements = [ NEXUS_INDICES_INDEX_EVERY_MB, NEXUS_INDICES_INDEX_EVERY_KB, NEXUS_CHUNK_CHUNK_MB, NEXUS_CHUNK_CHUNK_KB, ] self.ev42_nexus_to_spinner_ui_element = {} self.ev42_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.ev42_advanced_group_box.setLayout(QFormLayout()) self.ev42_adc_pulse_debug_label = QLabel(ADC_PULSE_DEBUG) self.ev42_adc_pulse_debug_checkbox = QCheckBox() self.ev42_advanced_group_box.layout().addRow( self.ev42_adc_pulse_debug_label, self.ev42_adc_pulse_debug_checkbox) self.add_labels_and_spinboxes_for_advanced_options( self.ev42_nexus_elements, self.ev42_advanced_group_box, self.ev42_nexus_to_spinner_ui_element, ) def add_labels_and_spinboxes_for_advanced_options(self, elements, group_box, nexus_to_spinner): for nexus_string in elements: label = QLabel(nexus_string) spinner = QSpinBox() spinner.setRange(self.minimum_spinbox_value, self.maximum_spinbox_value) group_box.layout().addRow(label, spinner) nexus_to_spinner[nexus_string] = spinner def _set_up_f142_group_box(self): """ Sets up the UI for the f142 advanced options. """ self.f142_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.f142_advanced_group_box.setLayout(QFormLayout()) self.f142_nexus_to_spinner_ui_element = {} self.f142_nexus_elements = [ NEXUS_INDICES_INDEX_EVERY_MB, NEXUS_INDICES_INDEX_EVERY_KB, STORE_LATEST_INTO, ] self.add_labels_and_spinboxes_for_advanced_options( self.f142_nexus_elements, self.f142_advanced_group_box, self.f142_nexus_to_spinner_ui_element, ) def _show_advanced_options(self, show): schema = self.schema_combo.currentText() if schema == WriterModules.F142.value: self.f142_advanced_group_box.setVisible(show) elif schema == WriterModules.EV42.value: self.ev42_advanced_group_box.setVisible(show) self.advanced_options_enabled = show def _show_array_size(self, show: bool): self.array_size_spinbox.setVisible(show) self.array_size_label.setVisible(show) def _schema_type_changed(self, schema: str): self.parent().setWindowTitle(f"Editing {schema} stream field") self.hs00_unimplemented_label.setVisible(False) self.f142_advanced_group_box.setVisible(False) self.ev42_advanced_group_box.setVisible(False) self.show_advanced_options_button.setVisible(False) self.show_advanced_options_button.setChecked(False) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) if schema == WriterModules.F142.value: self.value_units_label.setVisible(True) self.value_units_edit.setVisible(True) self._set_edits_visible(True, True) self.show_advanced_options_button.setVisible(True) self.f142_advanced_group_box.setVisible(False) elif schema == WriterModules.EV42.value: self._set_edits_visible(True, False) self.show_advanced_options_button.setVisible(True) self.ev42_advanced_group_box.setVisible(False) elif schema == WriterModules.HS00.value: self._set_edits_visible(True, False) self.hs00_unimplemented_label.setVisible(True) elif schema == WriterModules.NS10.value: self._set_edits_visible(True, False, "nicos/<device>/<parameter>") elif (schema == WriterModules.TDCTIME.value or schema == WriterModules.SENV.value): self._set_edits_visible(True, False) def _set_edits_visible(self, source: bool, type: bool, source_hint=None): self.source_label.setVisible(source) self.source_line_edit.setVisible(source) self.type_label.setVisible(type) self.type_combo.setVisible(type) self.array_radio.setVisible(type) self.scalar_radio.setVisible(type) if source_hint: self.source_line_edit.setPlaceholderText(source_hint) else: self.source_line_edit.setPlaceholderText("") def get_stream_group(self) -> h5py.Group: """ Create the stream group with a temporary in-memory HDF5 file. :return: The created HDF group. """ temp_file = create_temporary_in_memory_file() group = temp_file.create_group("children") group.create_dataset(name="type", dtype=STRING_DTYPE, data="stream") stream_group = group.create_group( self.parent().parent().field_name_edit.text()) stream_group.attrs[CommonAttrs.NX_CLASS] = CommonAttrs.NC_STREAM stream_group.create_dataset(name="topic", dtype=STRING_DTYPE, data=self.topic_line_edit.text()) stream_group.create_dataset( name="writer_module", dtype=STRING_DTYPE, data=self.schema_combo.currentText(), ) schema = self.schema_combo.currentText() stream_group.create_dataset("source", dtype=STRING_DTYPE, data=self.source_line_edit.text()) if schema == WriterModules.F142.value: self._create_f142_fields(stream_group) elif schema == WriterModules.EV42.value: self._create_ev42_fields(stream_group) return stream_group def _create_ev42_fields(self, stream_group: h5py.Group): """ Create ev42 fields in the given group if advanced options are specified. :param stream_group: The group to apply fields to. """ if self.advanced_options_enabled: if self.ev42_adc_pulse_debug_checkbox.isChecked(): stream_group.create_dataset( ADC_PULSE_DEBUG, dtype=bool, data=self.ev42_adc_pulse_debug_checkbox.isChecked(), ) self._create_dataset_from_spinner( stream_group, self.ev42_nexus_to_spinner_ui_element) def _create_f142_fields(self, stream_group: h5py.Group): """ Create f142 fields in the given group if advanced options are specified. :param stream_group: The group to apply fields to. """ stream_group.create_dataset("type", dtype=STRING_DTYPE, data=self.type_combo.currentText()) if self.array_radio.isChecked(): stream_group.create_dataset("array_size", data=self.array_size_spinbox.value()) if self.value_units_edit.text(): stream_group.create_dataset("value_units", data=self.value_units_edit.text()) if self.advanced_options_enabled: self._create_dataset_from_spinner( stream_group, self.f142_nexus_to_spinner_ui_element) @staticmethod def _create_dataset_from_spinner(stream_group: h5py.Group, nexus_to_spinner_dict: Dict[str, QSpinBox]): for (nexus_string, ui_element) in nexus_to_spinner_dict.items(): if ui_element.value() > 0: stream_group.create_dataset(nexus_string, dtype=int, data=ui_element.value()) def fill_in_existing_ev42_fields(self, field: h5py.Group): """ Fill in specific existing ev42 fields into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ all_ev42_elements = list(self.ev42_nexus_elements) all_ev42_elements.append(ADC_PULSE_DEBUG) if check_if_advanced_options_should_be_enabled(all_ev42_elements, field): self._show_advanced_options(True) if ADC_PULSE_DEBUG in field.keys(): self.ev42_adc_pulse_debug_checkbox.setChecked( bool(field[ADC_PULSE_DEBUG][()])) fill_in_advanced_options( self.ev42_nexus_to_spinner_ui_element.items(), field) def fill_in_existing_f142_fields(self, field: h5py.Group): """ Fill in specific existing f142 fields into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ self.type_combo.setCurrentText(field["type"][()]) if "array_size" in field.keys(): self.array_radio.setChecked(True) self.scalar_radio.setChecked(False) self.array_size_spinbox.setValue(field["array_size"][()]) else: self.array_radio.setChecked(False) self.scalar_radio.setChecked(True) if check_if_advanced_options_should_be_enabled( self.f142_nexus_elements, field): self._show_advanced_options(True) fill_in_advanced_options( self.f142_nexus_to_spinner_ui_element.items(), field) def update_existing_stream_info(self, field: h5py.Group): """ Fill in stream fields and properties into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ schema = field["writer_module"][()] self.schema_combo.setCurrentText(str(schema)) self.topic_line_edit.setText(str(field["topic"][()])) self.source_line_edit.setText(str(field["source"][()])) if schema == WriterModules.F142.value: self.fill_in_existing_f142_fields(field) elif schema == WriterModules.EV42.value: self.fill_in_existing_ev42_fields(field)
def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("Spot Extractor") menuBar = self.buildMenuBar() widget = QWidget(self) layout = QGridLayout(widget) # Main Image Window self.scrollArea = QScrollArea() self.imageLabel = ImageLabel(self) self.scrollArea.setWidget(self.imageLabel) # Text Label for Lot Name self.lotNameTextField = QLineEdit() self.lotNameTextField.setFixedWidth(300) # Spot List self.spotList = SpotListWidget(self) # Image Box Layout imageGroupBox = QGroupBox("Image") imageLayout = QHBoxLayout() imageLayout.addWidget(self.scrollArea) imageGroupBox.setLayout(imageLayout) # Spot List Box Layout rightGroupBox = QGroupBox() rightGroupBox.setMaximumWidth(300) rightGroupLayout = QVBoxLayout() lotNameGroupBox = QGroupBox("Lot Name") lotNameLayout = QHBoxLayout() lotNameLayout.addWidget(self.lotNameTextField) lotNameGroupBox.setLayout(lotNameLayout) spotsGroupBox = QGroupBox("Spot List") spotsLayout = QHBoxLayout() spotsLayout.addWidget(self.spotList) spotsGroupBox.setLayout(spotsLayout) rightGroupLayout.addWidget(lotNameGroupBox) rightGroupLayout.addWidget(spotsGroupBox) rightGroupBox.setLayout(rightGroupLayout) # Control Buttons Box Layout horizontalGroupBox = QGroupBox("Control Buttons") controlButtonLayout = QHBoxLayout() checkAllButton = QPushButton("Check All") uncheckAllButton = QPushButton("Uncheck All") deleteCheckedButton = QPushButton("Delete Checked") checkAllButton.clicked.connect(self.checkAll) uncheckAllButton.clicked.connect(self.uncheckAll) deleteCheckedButton.clicked.connect(self.deleteAllChecked) controlButtonLayout.addWidget(checkAllButton) controlButtonLayout.addWidget(uncheckAllButton) controlButtonLayout.addWidget(deleteCheckedButton) horizontalGroupBox.setLayout(controlButtonLayout) layout.addWidget(imageGroupBox, 0, 0) layout.addWidget(rightGroupBox, 0, 1) layout.addWidget(horizontalGroupBox, 1, 0, 1, 2) self.setMenuBar(menuBar) self.setLayout(layout) self.setCentralWidget(widget)
class QGroundObjectMenu(QDialog): changed = QtCore.Signal() def __init__(self, parent, ground_object: TheaterGroundObject, buildings: [], cp: ControlPoint, game: Game): super(QGroundObjectMenu, self).__init__(parent) self.setMinimumWidth(350) self.ground_object = ground_object self.buildings = buildings self.cp = cp self.game = game self.setWindowTitle("Location " + self.ground_object.obj_name) self.setWindowIcon(EVENT_ICONS["capture"]) self.intelBox = QGroupBox("Units :") self.buildingBox = QGroupBox("Buildings :") self.intelLayout = QGridLayout() self.buildingsLayout = QGridLayout() self.sell_all_button = None self.total_value = 0 self.init_ui() def init_ui(self): self.mainLayout = QVBoxLayout() self.budget = QBudgetBox(self.game) self.budget.setGame(self.game) self.doLayout() if self.ground_object.dcs_identifier == "AA": self.mainLayout.addWidget(self.intelBox) else: self.mainLayout.addWidget(self.buildingBox) self.actionLayout = QHBoxLayout() self.sell_all_button = QPushButton("Disband (+" + str(self.total_value) + "M)") self.sell_all_button.clicked.connect(self.sell_all) self.sell_all_button.setProperty("style", "btn-danger") self.buy_replace = QPushButton("Buy/Replace") self.buy_replace.clicked.connect(self.buy_group) self.buy_replace.setProperty("style", "btn-success") if self.total_value > 0: self.actionLayout.addWidget(self.sell_all_button) self.actionLayout.addWidget(self.buy_replace) if self.cp.captured and self.ground_object.dcs_identifier == "AA": self.mainLayout.addLayout(self.actionLayout) self.setLayout(self.mainLayout) def doLayout(self): self.update_total_value() self.intelBox = QGroupBox("Units :") self.intelLayout = QGridLayout() i = 0 for g in self.ground_object.groups: if not hasattr(g, "units_losts"): g.units_losts = [] for u in g.units: self.intelLayout.addWidget( QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) + "</b>"), i, 0) i = i + 1 for u in g.units_losts: utype = unit_type_of(u) if utype in PRICES: price = PRICES[utype] else: price = 6 self.intelLayout.addWidget( QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) + "</b> [DEAD]"), i, 0) if self.cp.captured: repair = QPushButton("Repair [" + str(price) + "M]") repair.setProperty("style", "btn-success") repair.clicked.connect( lambda u=u, g=g, p=price: self.repair_unit(g, u, p)) self.intelLayout.addWidget(repair, i, 1) i = i + 1 stretch = QVBoxLayout() stretch.addStretch() self.intelLayout.addLayout(stretch, i, 0) self.buildingBox = QGroupBox("Buildings :") self.buildingsLayout = QGridLayout() j = 0 for i, building in enumerate(self.buildings): if building.dcs_identifier not in FORTIFICATION_BUILDINGS: self.buildingsLayout.addWidget( QBuildingInfo(building, self.ground_object), j / 3, j % 3) j = j + 1 self.buildingBox.setLayout(self.buildingsLayout) self.intelBox.setLayout(self.intelLayout) def do_refresh_layout(self): try: for i in range(self.mainLayout.count()): item = self.mainLayout.itemAt(i) if item is not None and item.widget() is not None: item.widget().setParent(None) self.sell_all_button.setParent(None) self.buy_replace.setParent(None) self.actionLayout.setParent(None) self.doLayout() if self.ground_object.dcs_identifier == "AA": self.mainLayout.addWidget(self.intelBox) else: self.mainLayout.addWidget(self.buildingBox) self.actionLayout = QHBoxLayout() if self.total_value > 0: self.actionLayout.addWidget(self.sell_all_button) self.actionLayout.addWidget(self.buy_replace) if self.cp.captured and self.ground_object.dcs_identifier == "AA": self.mainLayout.addLayout(self.actionLayout) except Exception as e: print(e) self.update_total_value() self.changed.emit() def update_total_value(self): total_value = 0 for group in self.ground_object.groups: for u in group.units: utype = unit_type_of(u) if utype in PRICES: total_value = total_value + PRICES[utype] else: total_value = total_value + 1 if self.sell_all_button is not None: self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)") self.total_value = total_value def repair_unit(self, group, unit, price): if self.game.budget > price: self.game.budget -= price group.units_losts = [ u for u in group.units_losts if u.id != unit.id ] group.units.append(unit) GameUpdateSignal.get_instance().updateGame(self.game) # Remove destroyed units in the vicinity destroyed_units = self.game.get_destroyed_units() for d in destroyed_units: p = Point(d["x"], d["z"]) if p.distance_to_point(unit.position) < 15: destroyed_units.remove(d) logging.info("Removed destroyed units " + str(d)) logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type)) self.do_refresh_layout() self.changed.emit() def sell_all(self): self.update_total_value() self.game.budget = self.game.budget + self.total_value self.ground_object.groups = [] self.do_refresh_layout() GameUpdateSignal.get_instance().updateBudget(self.game) def buy_group(self): self.subwindow = QBuyGroupForGroundObjectDialog( self, self.ground_object, self.cp, self.game, self.total_value) self.subwindow.changed.connect(self.do_refresh_layout) self.subwindow.show()
def initUI(self): self.layout = QVBoxLayout() header = QLabel(self) header.setGeometry(0, 0, 655, 106) pixmap = QPixmap("./resources/ui/debriefing.png") header.setPixmap(pixmap) self.layout.addWidget(header) self.layout.addStretch() # Result #if self.gameEvent.is_successfull(self.debriefing): # title = QLabel("<b>Operation end !</b>") # title.setProperty("style", "title-success") #else: # title = QLabel("<b>Operation end !</b>") # title.setProperty("style", "title-danger") title = QLabel("<b>Casualty report</b>") self.layout.addWidget(title) # Player lost units lostUnits = QGroupBox(self.game.player_country + "'s lost units :") lostUnitsLayout = QGridLayout() lostUnits.setLayout(lostUnitsLayout) row = 0 for unit_type, count in self.debriefing.player_dead_aircraft_dict.items( ): try: lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except: print("Issue adding " + str(unit_type) + " to debriefing information") for unit_type, count in self.debriefing.player_dead_units_dict.items(): try: lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except: print("Issue adding " + str(unit_type) + " to debriefing information") for building, count in self.debriefing.player_dead_buildings_dict.items( ): try: lostUnitsLayout.addWidget(QLabel(building, row, 0)) lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except: print("Issue adding " + str(building) + " to debriefing information") self.layout.addWidget(lostUnits) # Enemy lost units enemylostUnits = QGroupBox(self.game.enemy_country + "'s lost units :") enemylostUnitsLayout = QGridLayout() enemylostUnits.setLayout(enemylostUnitsLayout) #row = 0 #if self.debriefing.destroyed_objects: # enemylostUnitsLayout.addWidget(QLabel("Ground assets"), row, 0) # enemylostUnitsLayout.addWidget(QLabel("{}".format(len(self.debriefing.destroyed_objects))), row, 1) # row += 1 for unit_type, count in self.debriefing.enemy_dead_aircraft_dict.items( ): if count == 0: continue try: enemylostUnitsLayout.addWidget( QLabel(db.unit_type_name(unit_type)), row, 0) enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except: print("Issue adding " + str(unit_type) + " to debriefing information") for unit_type, count in self.debriefing.enemy_dead_units_dict.items(): if count == 0: continue enemylostUnitsLayout.addWidget( QLabel(db.unit_type_name(unit_type)), row, 0) enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 for building, count in self.debriefing.enemy_dead_buildings_dict.items( ): try: enemylostUnitsLayout.addWidget(QLabel(building), row, 0) enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except: print("Issue adding " + str(building) + " to debriefing information") self.layout.addWidget(enemylostUnits) # confirm button okay = QPushButton("Okay") okay.clicked.connect(self.close) self.layout.addWidget(okay) self.setLayout(self.layout)
def __init__(self, m1m3): super().__init__() self.m1m3 = m1m3 self.xIndex = self.yIndex = self.zIndex = self.sIndex = self.testedId = None self._testRunning = False actuatorBox = QGroupBox("Actuator") self.actuatorsTable = QTableWidget( max([row[FATABLE_ID] for row in FATABLE]) % 100, 12 ) self.actuatorsTable.setShowGrid(False) def setNone(r, c): item = QTableWidgetItem("") item.setFlags(Qt.NoItemFlags) self.actuatorsTable.setItem(r, c, item) for i in range(4): mr = min( [ row[FATABLE_ID] for row in FATABLE if row[FATABLE_ID] > (100 + 100 * i) ] ) for r in range(mr): for c in range(i * 3, (i * 3) + 2): setNone(r, c) for tr in range(len(FATABLE)): actuatorId = FATABLE[tr][FATABLE_ID] row = (actuatorId % 100) - 1 colOffset = 3 * (int(actuatorId / 100) - 1) def getItem(text): item = QTableWidgetItem(text) item.setData(Qt.UserRole, actuatorId) return item self.actuatorsTable.setItem(row, 0 + colOffset, getItem(str(actuatorId))) self.actuatorsTable.setItem(row, 1 + colOffset, getItem("P")) if FATABLE[tr][FATABLE_SINDEX] is None: setNone(row, 2 + colOffset) else: self.actuatorsTable.setItem( row, 2 + colOffset, getItem("Y" if (FATABLE[tr][FATABLE_XINDEX] is None) else "X"), ) self.actuatorsTable.horizontalHeader().hide() self.actuatorsTable.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents ) self.actuatorsTable.horizontalHeader().setStretchLastSection(False) self.actuatorsTable.verticalHeader().hide() self.actuatorsTable.verticalHeader().setSectionResizeMode( QHeaderView.ResizeToContents ) self.actuatorsTable.itemSelectionChanged.connect(self.itemSelectionChanged) self.actuatorsTable.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.MinimumExpanding ) self.actuatorsTable.setFixedWidth( sum([self.actuatorsTable.columnWidth(c) for c in range(12)]) + self.actuatorsTable.verticalScrollBar().geometry().height() / 2 + 1 ) actuatorLayout = QVBoxLayout() actuatorLayout.addWidget(self.actuatorsTable) actuatorBox.setLayout(actuatorLayout) def testPB(): pb = QProgressBar() pb.setMaximum(6) return pb self.primaryPB = testPB() self.primaryLabelPB = QLabel("Primary") self.secondaryPB = testPB() self.secondaryLabelPB = QLabel("Seconday") self.progressGroup = QGroupBox("Test progress") progressLayout = QGridLayout() progressLayout.addWidget(self.primaryLabelPB, 0, 0) progressLayout.addWidget(self.primaryPB, 0, 1) progressLayout.addWidget(self.secondaryLabelPB, 1, 0) progressLayout.addWidget(self.secondaryPB, 1, 1) # progressLayout.addStretch(1) self.progressGroup.setLayout(progressLayout) self.progressGroup.setMaximumWidth(410) self.chart = None self.chart_view = TimeChartView() self.chart_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def makeButton(text, clicked): button = QPushButton(text) button.setEnabled(False) button.clicked.connect(clicked) return button self.bumpTestAllButton = makeButton("Bump test all", self.bumpTestAll) self.bumpTestButton = makeButton("Run bump test", self.issueCommandBumpTest) self.killBumpTestButton = makeButton( "Stop bump test", self.issueCommandKillBumpTest ) self.buttonLayout = QHBoxLayout() self.buttonLayout.addWidget(self.bumpTestAllButton) self.buttonLayout.addWidget(self.bumpTestButton) self.buttonLayout.addWidget(self.killBumpTestButton) self.layout = QVBoxLayout() self.forms = QHBoxLayout() self.forms.addWidget(actuatorBox) self.forms.addWidget(self.progressGroup) self.forms.addWidget(SALLog.Widget(self.m1m3)) self.layout.addLayout(self.forms) self.layout.addWidget(self.chart_view) self.layout.addLayout(self.buttonLayout) self.setLayout(self.layout) self.m1m3.detailedState.connect(self.detailedState) self.m1m3.forceActuatorBumpTestStatus.connect(self.forceActuatorBumpTestStatus)
class OptionsDock(QDockWidget): def __init__(self, model, FM, parent=None): super(OptionsDock, self).__init__(parent) self.model = model self.FM = FM self.mw = parent self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) # Doesn't work? self.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea) # Create Controls self.createOriginBox() self.createOptionsBox() self.createResolutionBox() # Create submit button self.applyButton = QPushButton("Apply Changes") self.applyButton.setMinimumHeight(self.FM.height() * 1.6) # Mac bug fix self.applyButton.clicked.connect(self.mw.applyChanges) # Create Zoom box self.zoomBox = QSpinBox() self.zoomBox.setSuffix(' %') self.zoomBox.setRange(25, 2000) self.zoomBox.setValue(100) self.zoomBox.setSingleStep(25) self.zoomBox.valueChanged.connect(self.mw.editZoom) self.zoomLayout = QHBoxLayout() self.zoomLayout.addWidget(QLabel('Zoom:')) self.zoomLayout.addWidget(self.zoomBox) self.zoomLayout.setContentsMargins(0, 0, 0, 0) self.zoomWidget = QWidget() self.zoomWidget.setLayout(self.zoomLayout) # Create Layout self.dockLayout = QVBoxLayout() self.dockLayout.addWidget(self.originGroupBox) self.dockLayout.addWidget(self.optionsGroupBox) self.dockLayout.addWidget(self.resGroupBox) self.dockLayout.addWidget(self.applyButton) self.dockLayout.addStretch() self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.zoomWidget) self.optionsWidget = QWidget() self.optionsWidget.setLayout(self.dockLayout) self.setWidget(self.optionsWidget) def createOriginBox(self): # X Origin self.xOrBox = QDoubleSpinBox() self.xOrBox.setDecimals(9) self.xOrBox.setRange(-99999, 99999) self.xOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 0)) # Y Origin self.yOrBox = QDoubleSpinBox() self.yOrBox.setDecimals(9) self.yOrBox.setRange(-99999, 99999) self.yOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 1)) # Z Origin self.zOrBox = QDoubleSpinBox() self.zOrBox.setDecimals(9) self.zOrBox.setRange(-99999, 99999) self.zOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 2)) # Origin Form Layout self.orLayout = QFormLayout() self.orLayout.addRow('X:', self.xOrBox) self.orLayout.addRow('Y:', self.yOrBox) self.orLayout.addRow('Z:', self.zOrBox) #self.orLayout.setVerticalSpacing(4) self.orLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.orLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Origin Group Box self.originGroupBox = QGroupBox('Origin') self.originGroupBox.setLayout(self.orLayout) def createOptionsBox(self): # Width self.widthBox = QDoubleSpinBox(self) self.widthBox.setRange(.1, 99999) self.widthBox.valueChanged.connect(self.mw.editWidth) # Height self.heightBox = QDoubleSpinBox(self) self.heightBox.setRange(.1, 99999) self.heightBox.valueChanged.connect(self.mw.editHeight) # ColorBy self.colorbyBox = QComboBox(self) self.colorbyBox.addItem("material") self.colorbyBox.addItem("cell") self.colorbyBox.currentTextChanged[str].connect(self.mw.editColorBy) # Alpha self.plotAlphaBox = QDoubleSpinBox(self) self.plotAlphaBox.setValue(self.model.activeView.plotAlpha) self.plotAlphaBox.setSingleStep(0.05) self.plotAlphaBox.setDecimals(2) self.plotAlphaBox.setRange(0.0, 1.0) self.plotAlphaBox.valueChanged.connect(self.mw.editPlotAlpha) # Basis self.basisBox = QComboBox(self) self.basisBox.addItem("xy") self.basisBox.addItem("xz") self.basisBox.addItem("yz") self.basisBox.currentTextChanged.connect(self.mw.editBasis) # Advanced Color Options self.colorOptionsButton = QPushButton('Color Options...') self.colorOptionsButton.setMinimumHeight(self.FM.height() * 1.6) self.colorOptionsButton.clicked.connect(self.mw.showColorDialog) # Options Form Layout self.opLayout = QFormLayout() self.opLayout.addRow('Width:', self.widthBox) self.opLayout.addRow('Height:', self.heightBox) self.opLayout.addRow('Basis:', self.basisBox) self.opLayout.addRow('Color By:', self.colorbyBox) self.opLayout.addRow('Plot alpha:', self.plotAlphaBox) self.opLayout.addRow(self.colorOptionsButton) self.opLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.opLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Options Group Box self.optionsGroupBox = QGroupBox('Options') self.optionsGroupBox.setLayout(self.opLayout) def createResolutionBox(self): # Horizontal Resolution self.hResBox = QSpinBox(self) self.hResBox.setRange(1, 99999) self.hResBox.setSingleStep(25) self.hResBox.setSuffix(' px') self.hResBox.valueChanged.connect(self.mw.editHRes) # Vertical Resolution self.vResLabel = QLabel('Pixel Height:') self.vResBox = QSpinBox(self) self.vResBox.setRange(1, 99999) self.vResBox.setSingleStep(25) self.vResBox.setSuffix(' px') self.vResBox.valueChanged.connect(self.mw.editVRes) # Ratio checkbox self.ratioCheck = QCheckBox("Fixed Aspect Ratio", self) self.ratioCheck.stateChanged.connect(self.mw.toggleAspectLock) # Resolution Form Layout self.resLayout = QFormLayout() self.resLayout.addRow(self.ratioCheck) self.resLayout.addRow('Pixel Width:', self.hResBox) self.resLayout.addRow(self.vResLabel, self.vResBox) self.resLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.resLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Resolution Group Box self.resGroupBox = QGroupBox("Resolution") self.resGroupBox.setLayout(self.resLayout) def updateDock(self): self.updateOrigin() self.updateWidth() self.updateHeight() self.updateColorBy() self.updatePlotAlpha() self.updateBasis() self.updateAspectLock() self.updateHRes() self.updateVRes() def updateOrigin(self): self.xOrBox.setValue(self.model.activeView.origin[0]) self.yOrBox.setValue(self.model.activeView.origin[1]) self.zOrBox.setValue(self.model.activeView.origin[2]) def updateWidth(self): self.widthBox.setValue(self.model.activeView.width) def updateHeight(self): self.heightBox.setValue(self.model.activeView.height) def updateColorBy(self): self.colorbyBox.setCurrentText(self.model.activeView.colorby) def updatePlotAlpha(self): self.plotAlphaBox.setValue(self.model.activeView.plotAlpha) def updateBasis(self): self.basisBox.setCurrentText(self.model.activeView.basis) def updateAspectLock(self): if self.model.activeView.aspectLock: self.ratioCheck.setChecked(True) self.vResBox.setDisabled(True) self.vResLabel.setDisabled(True) else: self.ratioCheck.setChecked(False) self.vResBox.setDisabled(False) self.vResLabel.setDisabled(False) def updateHRes(self): self.hResBox.setValue(self.model.activeView.h_res) def updateVRes(self): self.vResBox.setValue(self.model.activeView.v_res) def revertToCurrent(self): cv = self.model.currentView self.xOrBox.setValue(cv.origin[0]) self.yOrBox.setValue(cv.origin[1]) self.zOrBox.setValue(cv.origin[2]) self.widthBox.setValue(cv.width) self.heightBox.setValue(cv.height) def resizeEvent(self, event): self.mw.resizeEvent(event) def hideEvent(self, event): self.mw.resizeEvent(event) def showEvent(self, event): self.mw.resizeEvent(event) def moveEvent(self, event): self.mw.resizeEvent(event)
class ForceActuatorBumpTestPageWidget(QWidget): """ Enable user to select actuator for bump test. Show graphs depicting actual demand and measured forces. Shows button to run a bump test and stop any running bump test. Parameters ---------- m1m3 : `SALComm object` SALComm communication object. """ def __init__(self, m1m3): super().__init__() self.m1m3 = m1m3 self.xIndex = self.yIndex = self.zIndex = self.sIndex = self.testedId = None self._testRunning = False actuatorBox = QGroupBox("Actuator") self.actuatorsTable = QTableWidget( max([row[FATABLE_ID] for row in FATABLE]) % 100, 12 ) self.actuatorsTable.setShowGrid(False) def setNone(r, c): item = QTableWidgetItem("") item.setFlags(Qt.NoItemFlags) self.actuatorsTable.setItem(r, c, item) for i in range(4): mr = min( [ row[FATABLE_ID] for row in FATABLE if row[FATABLE_ID] > (100 + 100 * i) ] ) for r in range(mr): for c in range(i * 3, (i * 3) + 2): setNone(r, c) for tr in range(len(FATABLE)): actuatorId = FATABLE[tr][FATABLE_ID] row = (actuatorId % 100) - 1 colOffset = 3 * (int(actuatorId / 100) - 1) def getItem(text): item = QTableWidgetItem(text) item.setData(Qt.UserRole, actuatorId) return item self.actuatorsTable.setItem(row, 0 + colOffset, getItem(str(actuatorId))) self.actuatorsTable.setItem(row, 1 + colOffset, getItem("P")) if FATABLE[tr][FATABLE_SINDEX] is None: setNone(row, 2 + colOffset) else: self.actuatorsTable.setItem( row, 2 + colOffset, getItem("Y" if (FATABLE[tr][FATABLE_XINDEX] is None) else "X"), ) self.actuatorsTable.horizontalHeader().hide() self.actuatorsTable.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents ) self.actuatorsTable.horizontalHeader().setStretchLastSection(False) self.actuatorsTable.verticalHeader().hide() self.actuatorsTable.verticalHeader().setSectionResizeMode( QHeaderView.ResizeToContents ) self.actuatorsTable.itemSelectionChanged.connect(self.itemSelectionChanged) self.actuatorsTable.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.MinimumExpanding ) self.actuatorsTable.setFixedWidth( sum([self.actuatorsTable.columnWidth(c) for c in range(12)]) + self.actuatorsTable.verticalScrollBar().geometry().height() / 2 + 1 ) actuatorLayout = QVBoxLayout() actuatorLayout.addWidget(self.actuatorsTable) actuatorBox.setLayout(actuatorLayout) def testPB(): pb = QProgressBar() pb.setMaximum(6) return pb self.primaryPB = testPB() self.primaryLabelPB = QLabel("Primary") self.secondaryPB = testPB() self.secondaryLabelPB = QLabel("Seconday") self.progressGroup = QGroupBox("Test progress") progressLayout = QGridLayout() progressLayout.addWidget(self.primaryLabelPB, 0, 0) progressLayout.addWidget(self.primaryPB, 0, 1) progressLayout.addWidget(self.secondaryLabelPB, 1, 0) progressLayout.addWidget(self.secondaryPB, 1, 1) # progressLayout.addStretch(1) self.progressGroup.setLayout(progressLayout) self.progressGroup.setMaximumWidth(410) self.chart = None self.chart_view = TimeChartView() self.chart_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def makeButton(text, clicked): button = QPushButton(text) button.setEnabled(False) button.clicked.connect(clicked) return button self.bumpTestAllButton = makeButton("Bump test all", self.bumpTestAll) self.bumpTestButton = makeButton("Run bump test", self.issueCommandBumpTest) self.killBumpTestButton = makeButton( "Stop bump test", self.issueCommandKillBumpTest ) self.buttonLayout = QHBoxLayout() self.buttonLayout.addWidget(self.bumpTestAllButton) self.buttonLayout.addWidget(self.bumpTestButton) self.buttonLayout.addWidget(self.killBumpTestButton) self.layout = QVBoxLayout() self.forms = QHBoxLayout() self.forms.addWidget(actuatorBox) self.forms.addWidget(self.progressGroup) self.forms.addWidget(SALLog.Widget(self.m1m3)) self.layout.addLayout(self.forms) self.layout.addWidget(self.chart_view) self.layout.addLayout(self.buttonLayout) self.setLayout(self.layout) self.m1m3.detailedState.connect(self.detailedState) self.m1m3.forceActuatorBumpTestStatus.connect(self.forceActuatorBumpTestStatus) @Slot() def itemSelectionChanged(self): """Called when an actuator is selected from the list.""" items = self.actuatorsTable.selectedItems() if len(items) == 0: return if len(items) > 1: actuators = f"{items[0].data(Qt.UserRole)}..{items[-1].data(Qt.UserRole)}" else: actuators = f"{items[0].data(Qt.UserRole)}" self.bumpTestButton.setEnabled(not (self._anyCylinderRunning())) self.bumpTestButton.setText(f"Run bump test for FA ID {actuators}") def toggledTest(self, toggled): """Called when primary or secondary tests check box are toggled.""" self.bumpTestButton.setEnabled( self.actuatorsTable.currentItem() is not None and not (self._anyCylinderRunning()) ) @asyncSlot() async def bumpTestAll(self): for i in range(4): colOffset = i * 3 self.actuatorsTable.setRangeSelected( QTableWidgetSelectionRange( 0, 1 + colOffset, self.actuatorsTable.rowCount() - 1, 2 + colOffset, ), False, ) self.actuatorsTable.setRangeSelected( QTableWidgetSelectionRange( 0, colOffset, self.actuatorsTable.rowCount() - 1, colOffset ), True, ) await self._testItem(self.actuatorsTable.selectedItems()[0]) self.bumpTestButton.setEnabled(False) @asyncSlot() async def issueCommandBumpTest(self): """Call M1M3 bump test command.""" await self._testItem(self.actuatorsTable.selectedItems()[0]) async def _testItem(self, item): self.actuatorsTable.scrollToItem(item) self.testedId = item.data(Qt.UserRole) item.setSelected(False) self.zIndex = actuatorIDToIndex(self.testedId) self.xIndex = FATABLE[self.zIndex][FATABLE_XINDEX] self.yIndex = FATABLE[self.zIndex][FATABLE_YINDEX] self.sIndex = FATABLE[self.zIndex][FATABLE_SINDEX] items = [] if self.xIndex is not None: items.append("X") if self.yIndex is not None: items.append("Y") items.append("Z") items = ( list(map(lambda s: "Applied " + s, items)) + [None] + list(map(lambda s: "Measured " + s, items)) ) if self.chart is not None: self.chart.clearData() self.chart = TimeChart({"Force (N)": items}) self.chart_view.setChart(self.chart) self.progressGroup.setTitle(f"Test progress {self.testedId}") if self.sIndex is not None: self.secondaryLabelPB.setText("Y" if self.xIndex is None else "X") await self.m1m3.remote.cmd_forceActuatorBumpTest.set_start( actuatorId=self.testedId, testPrimary=not (item.text() == "X" or item.text() == "Y"), testSecondary=not (item.text() == "P") and self.sIndex is not None, ) self.killBumpTestButton.setText(f"Stop bump test FA ID {self.testedId}") @asyncSlot() async def issueCommandKillBumpTest(self): """Kill bump test.""" self.actuatorsTable.setRangeSelected( QTableWidgetSelectionRange(0, 0, self.actuatorsTable.rowCount() - 1, 11), False, ) await self.m1m3.remote.cmd_killForceActuatorBumpTest.start() @Slot(map) def detailedState(self, data): """Called when detailedState event is received. Intercept to enable/disable form buttons.""" if data.detailedState == MTM1M3.DetailedState.PARKEDENGINEERING: self.bumpTestAllButton.setEnabled(True) self.bumpTestButton.setEnabled( self.actuatorsTable.currentItem() is not None ) self.killBumpTestButton.setEnabled(False) self.xIndex = self.yIndex = self.zIndex = None else: self.bumpTestAllButton.setEnabled(False) self.bumpTestButton.setEnabled(False) self.killBumpTestButton.setEnabled(False) @Slot(map) def appliedForces(self, data): """Adds applied forces to graph.""" chartData = [] if self.xIndex is not None: chartData.append(data.xForces[self.xIndex]) if self.yIndex is not None: chartData.append(data.yForces[self.yIndex]) if self.zIndex is not None: chartData.append(data.zForces[self.zIndex]) self.chart.append(data.timestamp, chartData, cache_index=0) @Slot(map) def forceActuatorData(self, data): """Adds measured forces to graph.""" chartData = [] if self.xIndex is not None: chartData.append(data.xForce[self.xIndex]) if self.yIndex is not None: chartData.append(data.yForce[self.yIndex]) if self.zIndex is not None: chartData.append(data.zForce[self.zIndex]) self.chart.append(data.timestamp, chartData, cache_index=1) @asyncSlot(map) async def forceActuatorBumpTestStatus(self, data): """Received when an actuator finish/start running bump tests or the actuator reports progress of the bump test.""" testProgress = [ "Not tested", "Testing start zero", "Testing positive", "Positive wait zero", "Testing negative", "Negative wait zero", "Passed", "Failed", ] # test progress if self.zIndex is not None: self.primaryPB.setEnabled(True) val = data.primaryTest[self.zIndex] self.primaryPB.setFormat(f"ID {self.testedId} - {testProgress[val]} - %v") self.primaryPB.setValue(min(6, val)) else: self.primaryPB.setEnabled(False) if self.sIndex is not None: self.secondaryPB.setEnabled(True) val = data.secondaryTest[self.sIndex] self.secondaryPB.setFormat(f"ID {self.testedId} - {testProgress[val]} - %v") self.secondaryPB.setValue(min(6, val)) else: self.secondaryPB.setEnabled(False) # list display for index in range(156): actuatorId = FATABLE[index][FATABLE_ID] row = (actuatorId % 100) - 1 colOffset = 3 * (int(actuatorId / 100) - 1) def getColor(value): if value == 6: return Qt.green elif value == 7: return Qt.red elif not (value == 0): return Qt.magenta return Qt.transparent pColor = getColor(data.primaryTest[index]) self.actuatorsTable.item(row, colOffset + 1).setBackground(pColor) sIndex = FATABLE[index][FATABLE_SINDEX] if sIndex is not None: sColor = getColor(data.secondaryTest[sIndex]) self.actuatorsTable.item(row, colOffset + 2).setBackground(sColor) if pColor == sColor: self.actuatorsTable.item(row, colOffset).setBackground(pColor) else: self.actuatorsTable.item(row, colOffset).setBackground(pColor) # no tests running.. if data.actuatorId < 0: selected = self.actuatorsTable.selectedItems() if len(selected) > 0: await self._testItem(selected[0]) elif self._testRunning: self.bumpTestAllButton.setEnabled(True) self.bumpTestButton.setEnabled( self.actuatorsTable.currentItem() is not None and self._anyCylinder() ) self.killBumpTestButton.setEnabled(False) self.xIndex = self.yIndex = self.zIndex = None self.m1m3.appliedForces.disconnect(self.appliedForces) self.m1m3.forceActuatorData.disconnect(self.forceActuatorData) self._testRunning = False elif self._testRunning is False: self.bumpTestButton.setEnabled(False) self.killBumpTestButton.setEnabled(True) self.m1m3.appliedForces.connect(self.appliedForces) self.m1m3.forceActuatorData.connect(self.forceActuatorData) self._testRunning = True # helper functions. Helps correctly enable/disable Run bump test button. def _anyCylinderRunning(self): return self._testRunning is True and self._anyCylinder() def _anyCylinder(self): return len(self.actuatorsTable.selectedItems()) > 0
class DLPSettingsGUI(QWidget): def __init__(self, dlp_controller=None, dlp_slicer=None, parent=None): QWidget.__init__(self, parent) self.parent = parent self.dlp_controller = dlp_controller self.dlp_slicer = dlp_slicer self.dlp_color_calibrator = DLPColorCalibrator() self.dlp_color_calibrator.analysis_completed_signal.connect(self.update_charts) self.__printer_parameters_list = {} self.__slicer_parameters_list = {} self.data_fit_chart_view = None self.data_fit_chart = None self.main_layout = QHBoxLayout() self.__init_table_widget__() self.__init_color_calibration_widget() self.__default_parameters_widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding) self.main_layout.addWidget(self.__default_parameters_widget, stretch=1) self.main_layout.addWidget(self.__color_calibration_widget, stretch=2) self.setLayout(self.main_layout) self.main_layout.update() def __init_color_calibration_widget(self, parent=None): self.__color_calibration_widget = QGroupBox("Color Correction Options", parent) self.__color_calibration_widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) color_calibration_layout = QVBoxLayout(self.__color_calibration_widget) chart_widget = QWidget(self.__color_calibration_widget) chart_layout = QGridLayout(chart_widget) self.data_fit_chart = QtCharts.QChart() self.data_fit_chart_view = QtCharts.QChartView(self.data_fit_chart) self.axis_x = QtCharts.QValueAxis() self.axis_x.setTitleText("Pixel Intensity") self.axis_x.setRange(0, 1) self.data_fit_chart.addAxis(self.axis_x, Qt.AlignBottom) self.axis_y = QtCharts.QValueAxis() self.axis_y.setTitleText("Voxel Height (\u03BCm)") self.axis_y.setRange(0, 10) self.data_fit_chart.addAxis(self.axis_y, Qt.AlignLeft) chart_layout.addWidget(self.data_fit_chart_view, 0, 0, 1, 4) chart_widget.setLayout(chart_layout) buttons_widget = QWidget(self.__color_calibration_widget) buttons_layout = QHBoxLayout(buttons_widget) analyze_data_button = QPushButton("Analyze Data") analyze_data_button.clicked.connect(self.analyze_images) self.parameters_estimation_label = QLabel(f'Estimated parameters: \u03B1 = {self.dlp_color_calibrator.optimized_parameters[0]:.3f}, \u03B2 = {self.dlp_color_calibrator.optimized_parameters[1]:.3f}, \u03B3 = {self.dlp_color_calibrator.optimized_parameters[2]:.3f}', buttons_widget) buttons_layout.addWidget(analyze_data_button) buttons_layout.addWidget(self.parameters_estimation_label) buttons_widget.setLayout(buttons_layout) color_calibration_layout.addWidget(chart_widget) color_calibration_layout.addWidget(buttons_widget) self.__color_calibration_widget.setLayout(color_calibration_layout) @Slot() def analyze_images(self): file_names = QFileDialog.getOpenFileNames(caption='Select data', dir='../measured_data/grayscale_measured_data', filter="Image Files (*.asc)") self.dlp_color_calibrator.analyze_data_files(file_names[0]) @Slot() def update_charts(self): self.data_fit_chart = QtCharts.QChart() self.data_fit_chart.setAnimationOptions(QtCharts.QChart.AllAnimations) self.add_series(self.data_fit_chart, "Measured Data", self.dlp_color_calibrator.input_values, self.dlp_color_calibrator.average_data) self.add_series(self.data_fit_chart, "Fitted Curve", self.dlp_color_calibrator.input_values, self.dlp_color_calibrator.fitted_curve) self.add_series(self.data_fit_chart, "Predicted Result", self.dlp_color_calibrator.input_values, self.dlp_color_calibrator.corrected_output_values) series = self.data_fit_chart.series() self.data_fit_chart.addAxis(self.axis_x, Qt.AlignBottom) self.axis_y.setRange(0, self.dlp_color_calibrator.measured_thickness) self.data_fit_chart.addAxis(self.axis_y, Qt.AlignLeft) for s in series: s.attachAxis(self.axis_x) s.attachAxis(self.axis_y) self.data_fit_chart_view.setRenderHint(QPainter.Antialiasing) self.data_fit_chart_view.setChart(self.data_fit_chart) self.parameters_estimation_label.setText(f'Estimated parameters: \u03B1 = {self.dlp_color_calibrator.optimized_parameters[0]:.3f}, \u03B2 = {self.dlp_color_calibrator.optimized_parameters[1]:.3f}, \u03B3 = {self.dlp_color_calibrator.optimized_parameters[2]:.3f}') def add_series(self, chart, title, x, y): series = QtCharts.QLineSeries() series.setName(title) for idx, elem in enumerate(x): series.append(x[idx], y[idx]) chart.addSeries(series) def __init_table_widget__(self, parent=None): self.__default_parameters_widget = QGroupBox("Default Parameters", parent) self.printer_parameters_list = self.dlp_controller.get_default_parameters() self.table_view = QTableView() self.table_model = self.MyTableModel(parent=self.__default_parameters_widget, data_list=self.printer_parameters_list) self.table_view.setModel(self.table_model) self.table_view.horizontalHeader().setVisible(False) self.table_view.verticalHeader().setVisible(False) self.table_view.horizontalHeader().setStretchLastSection(False) # self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table_view.resizeColumnsToContents() self.table_view.update() # self.table_view.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # self.table_view.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) apply_button = QPushButton("Apply Changes", self.__default_parameters_widget) apply_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) apply_button.clicked.connect(self.dlp_controller.save_default_parameters) default_parameters_layout = QVBoxLayout(self.__default_parameters_widget) default_parameters_layout.addWidget(self.table_view) default_parameters_layout.addWidget(apply_button) self.__default_parameters_widget.setLayout(default_parameters_layout) self.__default_parameters_widget.updateGeometry() # default_parameters_layout.update() # QGuiApplication.processEvents() @Slot() def __adjust_table_size__(self): self.table_view.resizeColumnToContents(0) self.table_view.resizeColumnToContents(1) rect = self.table_view.geometry() rect.setWidth(1 + self.table_view.verticalHeader().width() + self.table_view.columnWidth(0) + self.table_view.columnWidth(1) + self.table_view.verticalScrollBar().width()) self.table_view.setGeometry(rect) self.table_view.resize(rect.width(), rect.height()) class MyTableModel(QAbstractTableModel): def __init__(self, parent, data_list, *args): QAbstractTableModel.__init__(self, parent, *args) self.parent = parent self.data_list = data_list def rowCount(self, parent): return len(self.data_list) def columnCount(self, parent): return 2 def data(self, index, role): if not index.isValid(): return None elif role != Qt.DisplayRole: return None return list(self.data_list.items())[index.row()][index.column()] def setData(self, index, value, role): if role == Qt.EditRole: if not index.isValid(): return False elif index.column() == 0: return False else: key = list(self.data_list.items())[index.row()][0] old_value = list(self.data_list.items())[index.row()][1] try: if isinstance(old_value, float): self.data_list[key] = float(value) elif isinstance(old_value, bool): if value == "True" or value == "true": self.data_list[key] = True else: self.data_list[key] = False elif isinstance(old_value, str): if index.row() == 0: if not (value.upper() == "TOP-DOWN" or value.upper() == "BOTTOM-UP"): return False self.data_list[key] = str(value).upper() except ValueError: return False self.dataChanged.emit(index, index) return True else: return False def flags(self, index): if not index.isValid() or index.column() == 0: return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
class JobInfoPanel(QWidget): def __init__(self): super().__init__() self.setStyleSheet(Style.INFO_PANEL.value) self.__create_title() self.__create_description_groupbox() self.__create_res_req_groupbox() self.__create_contact_groupbox() main_layout = QVBoxLayout() main_layout.addWidget(self.title) main_layout.addWidget(self.horizontal_groupbox) main_layout.addLayout(self.restrictions_layout) main_layout.addLayout(self.contact_layout) self.setLayout(main_layout) def __create_title(self): self.title = QLabel() self.title.setAlignment(Qt.AlignCenter) font = QFont() font.setPixelSize(17) self.title.setFont(font) self.title.setStyleSheet( 'padding: 12px; border: 1px solid #212121; border-radius: 10px;') def __create_description_groupbox(self): self.horizontal_groupbox = QGroupBox('Description:') self.description = QTextEdit() self.description.setReadOnly(True) layout = QHBoxLayout() layout.addWidget(self.description, Style.TWO_THIRDS.value) window = QWidget() window.setMinimumSize(320, 625) self.web = QWebEngineView(window) self.web.setHtml(self.generate_html_map(0, 0)) self.webpage = self.web.page() layout.addWidget(self.web, Style.ONE_THIRD.value) self.horizontal_groupbox.setLayout(layout) def generate_html_map(self, lat, lng): initialize = ( "var earth; var marker; var zoomLevel = 3.5;" "function initialize() {" " var options={zoom: zoomLevel, position: [" + str(lat) + "," + str(lng) + "]};" " earth = new WE.map('earth_div', options);" " WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(earth);" f" add_marker({lat}, {lng})" "}" "function rm_marker() {marker.removeFrom(earth)}" "function add_marker(lat,lng) {" " try {rm_marker()} catch(e) {}" " marker = WE.marker([lat,lng]).addTo(earth);" " earth.setView([lat,lng], zoomLevel);}") style = ''' html, body { padding: 0; margin: 0; } #earth_div { top: 0; right: 0; bottom: 0; left: 0; position: absolute !important; } ''' return f''' <!DOCTYPE HTML> <html> <head> <script src="http://www.webglearth.com/v2/api.js"></script> <script>{initialize}</script> <style>{style}</style> </head> <body onload="initialize()"> <div id="earth_div"></div> </body> </html> ''' def __create_res_req_groupbox(self): hbox = QHBoxLayout() restrictions_group = QGroupBox('Restrictions:') self.restrictions = QTextEdit() self.restrictions.setReadOnly(True) hbox.addWidget(self.restrictions) restrictions_group.setLayout(hbox) hbox = QHBoxLayout() requirements_group = QGroupBox('Requirements:') self.requirements = QTextEdit() self.requirements.setReadOnly(True) hbox.addWidget(self.requirements) requirements_group.setLayout(hbox) self.restrictions_layout = QHBoxLayout() self.restrictions_layout.addWidget(requirements_group, Style.TWO_THIRDS.value) self.restrictions_layout.addWidget(restrictions_group, Style.ONE_THIRD.value) def __create_contact_groupbox(self): hbox = QHBoxLayout() about_group = QGroupBox('About:') self.about = QTextEdit() self.about.setReadOnly(True) hbox.addWidget(self.about) about_group.setLayout(hbox) layout = QFormLayout() contact_group = QGroupBox('Contact Info') self.contact_name = QLabel() self.contact_email = QLabel() self.contact_website = QPushButton('Apply') self.contact_website.clicked.connect( lambda: webbrowser.open(self.apply_website)) layout.addRow(QLabel('Contact:'), self.contact_name) layout.addRow(QLabel('Email:'), self.contact_email) layout.addRow(QLabel('Website:'), self.contact_website) contact_group.setLayout(layout) self.contact_layout = QHBoxLayout() self.contact_layout.addWidget(about_group, Style.TWO_THIRDS.value) self.contact_layout.addWidget(contact_group, Style.ONE_THIRD.value) def set_contact_info(self, contact: str, email: str, website: str, company: str): self.contact_name.setText(contact) self.contact_email.setText(email) self.apply_website = website if self.apply_website is None: self.apply_website = f'https://duckduckgo.com/?q={company}&t=ffab&ia=web'
def plot_box(self): self.plot = QGroupBox() layout = QVBoxLayout() self.pw.showGrid(True, True, 0.5) layout.addWidget(self.pw) self.plot.setLayout(layout)
class Dialog(QDialog): def __init__(self): super(Dialog, self).__init__() self.rotableWidgets = [] self.createRotableGroupBox() self.createOptionsGroupBox() self.createButtonBox() mainLayout = QGridLayout() mainLayout.addWidget(self.rotableGroupBox, 0, 0) mainLayout.addWidget(self.optionsGroupBox, 1, 0) mainLayout.addWidget(self.buttonBox, 2, 0) mainLayout.setSizeConstraint(QLayout.SetMinimumSize) self.mainLayout = mainLayout self.setLayout(self.mainLayout) self.setWindowTitle("Dynamic Layouts") def rotateWidgets(self): count = len(self.rotableWidgets) if count % 2 == 1: raise AssertionError("Number of widgets must be even") for widget in self.rotableWidgets: self.rotableLayout.removeWidget(widget) self.rotableWidgets.append(self.rotableWidgets.pop(0)) for i in range(count//2): self.rotableLayout.addWidget(self.rotableWidgets[count - i - 1], 0, i) self.rotableLayout.addWidget(self.rotableWidgets[i], 1, i) def buttonsOrientationChanged(self, index): self.mainLayout.setSizeConstraint(QLayout.SetNoConstraint) self.setMinimumSize(0, 0) orientation = Qt.Orientation(int(self.buttonsOrientationComboBox.itemData(index))) if orientation == self.buttonBox.orientation(): return self.mainLayout.removeWidget(self.buttonBox) spacing = self.mainLayout.spacing() oldSizeHint = self.buttonBox.sizeHint() + QSize(spacing, spacing) self.buttonBox.setOrientation(orientation) newSizeHint = self.buttonBox.sizeHint() + QSize(spacing, spacing) if orientation == Qt.Horizontal: self.mainLayout.addWidget(self.buttonBox, 2, 0) self.resize(self.size() + QSize(-oldSizeHint.width(), newSizeHint.height())) else: self.mainLayout.addWidget(self.buttonBox, 0, 3, 2, 1) self.resize(self.size() + QSize(newSizeHint.width(), -oldSizeHint.height())) self.mainLayout.setSizeConstraint(QLayout.SetDefaultConstraint) def show_help(self): QMessageBox.information(self, "Dynamic Layouts Help", "This example shows how to change layouts " "dynamically.") def createRotableGroupBox(self): self.rotableGroupBox = QGroupBox("Rotable Widgets") self.rotableWidgets.append(QSpinBox()) self.rotableWidgets.append(QSlider()) self.rotableWidgets.append(QDial()) self.rotableWidgets.append(QProgressBar()) count = len(self.rotableWidgets) for i in range(count): self.rotableWidgets[i].valueChanged[int].\ connect(self.rotableWidgets[(i+1) % count].setValue) self.rotableLayout = QGridLayout() self.rotableGroupBox.setLayout(self.rotableLayout) self.rotateWidgets() def createOptionsGroupBox(self): self.optionsGroupBox = QGroupBox("Options") buttonsOrientationLabel = QLabel("Orientation of buttons:") buttonsOrientationComboBox = QComboBox() buttonsOrientationComboBox.addItem("Horizontal", Qt.Horizontal) buttonsOrientationComboBox.addItem("Vertical", Qt.Vertical) buttonsOrientationComboBox.currentIndexChanged[int].connect(self.buttonsOrientationChanged) self.buttonsOrientationComboBox = buttonsOrientationComboBox optionsLayout = QGridLayout() optionsLayout.addWidget(buttonsOrientationLabel, 0, 0) optionsLayout.addWidget(self.buttonsOrientationComboBox, 0, 1) optionsLayout.setColumnStretch(2, 1) self.optionsGroupBox.setLayout(optionsLayout) def createButtonBox(self): self.buttonBox = QDialogButtonBox() closeButton = self.buttonBox.addButton(QDialogButtonBox.Close) helpButton = self.buttonBox.addButton(QDialogButtonBox.Help) rotateWidgetsButton = self.buttonBox.addButton("Rotate &Widgets", QDialogButtonBox.ActionRole) rotateWidgetsButton.clicked.connect(self.rotateWidgets) closeButton.clicked.connect(self.close) helpButton.clicked.connect(self.show_help)
def initUI(self): self.layout = QVBoxLayout() header = QLabel(self) header.setGeometry(0, 0, 655, 106) pixmap = QPixmap("./resources/ui/debriefing.png") header.setPixmap(pixmap) self.layout.addWidget(header) self.layout.addStretch() title = QLabel("<b>Casualty report</b>") self.layout.addWidget(title) # Player lost units lostUnits = QGroupBox( f"{self.debriefing.player_country}'s lost units:") lostUnitsLayout = QGridLayout() lostUnits.setLayout(lostUnitsLayout) row = 0 player_air_losses = self.debriefing.air_losses.by_type(player=True) for unit_type, count in player_air_losses.items(): try: lostUnitsLayout.addWidget( QLabel(db.unit_get_expanded_info(self.debriefing.player_country, unit_type, 'name')), row, 0) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: logging.exception( f"Issue adding {unit_type} to debriefing information") front_line_losses = self.debriefing.front_line_losses_by_type( player=True ) for unit_type, count in front_line_losses.items(): try: lostUnitsLayout.addWidget( QLabel(db.unit_type_name(unit_type)), row, 0) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: logging.exception( f"Issue adding {unit_type} to debriefing information") building_losses = self.debriefing.building_losses_by_type(player=True) for building, count in building_losses.items(): try: lostUnitsLayout.addWidget(QLabel(building), row, 0) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: logging.exception( f"Issue adding {building} to debriefing information") self.layout.addWidget(lostUnits) # Enemy lost units enemylostUnits = QGroupBox( f"{self.debriefing.enemy_country}'s lost units:") enemylostUnitsLayout = QGridLayout() enemylostUnits.setLayout(enemylostUnitsLayout) enemy_air_losses = self.debriefing.air_losses.by_type(player=False) for unit_type, count in enemy_air_losses.items(): try: enemylostUnitsLayout.addWidget( QLabel(db.unit_get_expanded_info(self.debriefing.enemy_country, unit_type, 'name')), row, 0) enemylostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: logging.exception( f"Issue adding {unit_type} to debriefing information") front_line_losses = self.debriefing.front_line_losses_by_type( player=False ) for unit_type, count in front_line_losses.items(): if count == 0: continue enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 building_losses = self.debriefing.building_losses_by_type(player=False) for building, count in building_losses.items(): try: enemylostUnitsLayout.addWidget(QLabel(building), row, 0) enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except AttributeError: logging.exception( f"Issue adding {building} to debriefing information") self.layout.addWidget(enemylostUnits) # TODO: Display dead ground object units and runways. # confirm button okay = QPushButton("Okay") okay.clicked.connect(self.close) self.layout.addWidget(okay) self.setLayout(self.layout)
def setupUi(self, MainWindow): def fun(spoiler): if spoiler.isOpened(): spoiler.close() else: spoiler.open() # app = QApplication(sys.argv) w = QMainWindow() cw = QWidget() mainLayout = QHBoxLayout() cw.setLayout(mainLayout) gridLayout = QGridLayout() spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem1, 0, 0, 1, 1) pushButton1 = QPushButton() pushButton1.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) pushButton1.setText("") # gridLayout.addWidget(pushButton1, 0, 1, 1, 1) self.label1 = QLabel() self.label1.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.label1.setObjectName("") gridLayout.addWidget(self.label1, 0, 2, 1, 1) datePicker1 = DatePicker() gridLayout.addWidget(datePicker1, 0, 1, 1, 1) datePicker1.selectionChanged.connect(lambda: self.__setLableDate__( self.label1, datePicker1.selectedDate())) spacerItem2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem2, 0, 3, 1, 1) self.label2 = QLabel() self.label2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.label2.setObjectName("") gridLayout.addWidget(self.label2, 0, 4, 1, 1) datePicker2 = DatePicker() gridLayout.addWidget(datePicker2, 0, 5, 1, 1) datePicker2.selectionChanged.connect(lambda: self.__setLableDate__( self.label2, datePicker2.selectedDate())) pushButton2 = QPushButton() pushButton2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) pushButton2.setText("") #gridLayout.addWidget(pushButton2, 0, 5, 1, 1) spacerItem3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem3, 0, 6, 1, 1) groupBox = QGroupBox() groupBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) verticalLayout = QVBoxLayout(groupBox) radioButton1 = QRadioButton(groupBox) radioButton1.setText("Subscribers") verticalLayout.addWidget(radioButton1) radioButton2 = QRadioButton(groupBox) radioButton1.setText("PatientsServices") verticalLayout.addWidget(radioButton2) export_btn = QPushButton() export_btn.setText("export") gridLayout.addWidget(export_btn) export_btn.clicked.connect(lambda: self.onExport_btn()) gridLayout.addWidget(groupBox, 1, 1, 1, 2, Qt.AlignTop) self.comboBox = QComboBox() self.h_values = [ "all", "مستشفي الصفوة الدولي", "مستشفي السلام الدولي", "مختبر مصراتة المركزي", "مستشفي الحكمة الحديث", "مستشفى المدينه السكنيه" ] self.comboBox.addItems(self.h_values) self.comboBox.activated.connect(self.oncomboBoxChanged) gridLayout.addWidget(self.comboBox, 1, 4, 1, 2, Qt.AlignTop) btn = QPushButton("Click me") btn.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) spoiler = Spoiler(Spoiler.Orientation.VERTICAL) spoiler.setContentLayout(gridLayout) mainLayout.addWidget(btn) #self.searchGridLayout.addWidget(spoiler) # mainLayout.setAlignment(Qt.AlignRight) MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(716, 392) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setTabsClosable(False) self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.InsertTab = QtWidgets.QWidget() self.InsertTab.setObjectName(_fromUtf8("InsertTab")) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.InsertTab) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) spacerItem = QtWidgets.QSpacerItem(20, 109, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem) self.insertGridLayout = QtWidgets.QGridLayout() self.insertGridLayout.setObjectName(_fromUtf8("insertGridLayout")) self.run_btn = QtWidgets.QPushButton(self.InsertTab) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.run_btn.sizePolicy().hasHeightForWidth()) self.run_btn.setSizePolicy(sizePolicy) self.run_btn.setMouseTracking(False) self.run_btn.setAutoFillBackground(False) self.run_btn.setStyleSheet(_fromUtf8("border: none")) self.run_btn.setText(_fromUtf8("")) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(".\\icons\\run.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.run_btn.setIcon(icon) self.run_btn.setIconSize(QtCore.QSize(64, 64)) self.run_btn.setObjectName(_fromUtf8("runBtn")) self.insertGridLayout.addWidget(self.run_btn, 0, 3, 2, 1) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.combo = QComboBox() self.values = [ "مستشفي الصفوة الدولي", "مستشفي السلام الدولي", "مختبر مصراتة المركزي", "مستشفي الحكمة الحديث", "مستشفى المدينه السكنيه" ] self.combo.addItems(self.values) if self.combo.currentIndex() == 1: print("kln") self.combo.activated.connect(self.oncomboChanged) self.insertGridLayout.addWidget(self.combo, 1, 4, 1, 2, Qt.AlignTop) self.insertGridLayout.addItem(spacerItem1, 0, 5, 1, 1) self.excel_label = QtWidgets.QLabel(self.InsertTab) self.excel_label.setText(_fromUtf8("")) self.excel_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.excel_label.setObjectName(_fromUtf8("excelLabel")) self.insertGridLayout.addWidget(self.excel_label, 1, 0, 1, 3) self.sqlBtn = QtWidgets.QPushButton(self.InsertTab) self.sqlBtn.setMinimumSize(QtCore.QSize(200, 0)) self.sqlBtn.setObjectName(_fromUtf8("sqlBtn")) self.insertGridLayout.addWidget(self.sqlBtn, 0, 4, 1, 1) self.excelBtn = QtWidgets.QPushButton(self.InsertTab) self.excelBtn.setMinimumSize(QtCore.QSize(200, 0)) self.excelBtn.setObjectName(_fromUtf8("excelBtn")) self.insertGridLayout.addWidget(self.excelBtn, 0, 2, 1, 1) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.insertGridLayout.addItem(spacerItem2, 0, 1, 1, 1) self.sqlLabel = QtWidgets.QLabel(self.InsertTab) self.sqlLabel.setText(_fromUtf8("")) self.sqlLabel.setObjectName(_fromUtf8("sqlLabel")) self.insertGridLayout.addWidget(self.sqlLabel, 1, 4, 1, 2) self.verticalLayout_2.addLayout(self.insertGridLayout) spacerItem3 = QtWidgets.QSpacerItem(20, 108, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem3) self.tabWidget.addTab(self.InsertTab, _fromUtf8("")) # self.filterBtn = QtWidgets.QPushButton(self.searchTab) # self.filterBtn.setStyleSheet(_fromUtf8("border: none")) # icon2 = QtGui.QIcon() # icon2.addPixmap(QtGui.QPixmap(_fromUtf8("icons/filter_icon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) # self.filterBtn.setIcon(icon2) # self.filterBtn.setObjectName(_fromUtf8("filterBtn")) # self.searchGridLayout.addWidget(self.filterBtn, 0, 3, 1, 1) # self.searchGridLayout.addWidget(spoiler) self.export_btn = QPushButton() self.export_btn.setText("export") #self.gridLayout.addWidget(export_btn) self.export_btn.clicked.connect( lambda: self.onExport_btn_subscribers()) #self.exprt_btn = QtWidgets.QPushButton() #self.exprt_btn.setText("export") #self.exprt_btn.setObjectName(_fromUtf8("exprt_btn")) #self.searchGridLayout1.addWidget(self.exprt_btn, 0, 0, 2, 1) self.verticalLayout.addWidget(self.tabWidget) MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName(_fromUtf8("statusbar")) MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(1) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.excelBtn.clicked.connect(MainWindow, QtCore.SLOT("on_excel_btn_clicked()")) self.sqlBtn.clicked.connect(MainWindow, QtCore.SLOT("on_sql_btn_clicked()")) self.run_btn.clicked.connect(MainWindow, QtCore.SLOT("on_run_btn_clicked()")) self.excel_file_name = None self.sql_file_name = "C:\\Users\\PC WORLD\\Downloads\\test.sqlite" self.sqlLabel.setText(self.sql_file_name) conn = sqlite3.connect("C:\\Users\\PC WORLD\\Downloads\\test.sqlite") result = conn.execute("SELECT * FROM Subscribers") conn.close()
def _add_grouped(self, form, title, widget): box = QGroupBox(title) vbox = QVBoxLayout() vbox.addWidget(widget) box.setLayout(vbox) form.addRow(box)
def gui_init(self): control_group_layout = QVBoxLayout() control_group_widget = QWidget() control_group_widget.setLayout(control_group_layout) self.layout.addWidget(control_group_widget) rows = int(len(self._stepMotorAxies) / 2) columns = rows + int(len(self._stepMotorAxies) % 2) '''Добавляем шаговики''' steps_group = QGroupBox("{}".format(self.app.tr("Steppers"))) steps_group.setContentsMargins(0, 0, 0, 0) steps_group.setStyleSheet("QGroupBox::title {" "text-align: left;" "font: bold 14px;" "})") steps_group_layout = QGridLayout() steps_group.setLayout(steps_group_layout) row = 0 column = 0 for i, step_axis in enumerate(self._stepMotorAxies): row = int(i / (rows)) column = i - columns * row step_block = stepAxisWidget(step_axis, 1000, self.app, self) if self.__motorActions: step_block.changed.connect(self.__motorActions) steps_group_layout.addWidget(step_block, row, column, Qt.AlignCenter) # spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) # steps_group_layout.addItem(spacer) control_group_layout.addWidget(steps_group) servo_group = QGroupBox("{}".format(self.app.tr("Servos"))) servo_group.setContentsMargins(0, 0, 0, 0) servo_group.setStyleSheet("QGroupBox::title {" "text-align: left;" "font: bold 14px;" "})") servo_group_layout = QGridLayout() servo_group.setLayout(servo_group_layout) row = 0 column = 0 for i, servo_axis in enumerate(self._servoMotorAxies): row = int(i / (rows)) column = i - columns * row servo_block = servoAxisWidget(servo_axis, 1000, self.app, 180, self) if self.__motorActions: servo_block.changed.connect(self.__motorActions) servo_group_layout.addWidget(servo_block, row, column, Qt.AlignCenter) control_group_layout.addWidget(servo_group) led_group = QGroupBox("{}".format(self.app.tr("LED"))) led_group.setStyleSheet("QGroupBox::title {" "text-align: left;" "font: bold 14px;" "})") led_group_layout = QHBoxLayout() led_group.setLayout(led_group_layout) for pin, labels in self._controlPins.items(): switch = LedSwitch( self, target=pin) if len(labels) == 0 else LedSwitch( self, target=pin, leftText=labels[0], rightText=labels[1]) if self.__pinAcions: switch.switched.connect(self.__pinAcions) led_group_layout.addWidget(switch) control_group_layout.addWidget(led_group) spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) control_group_layout.addItem(spacer) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.layout.addItem(spacer)
class QBuyGroupForGroundObjectDialog(QDialog): changed = QtCore.Signal() def __init__(self, parent, ground_object: TheaterGroundObject, cp: ControlPoint, game: Game, current_group_value: int): super(QBuyGroupForGroundObjectDialog, self).__init__(parent) self.setMinimumWidth(350) self.ground_object = ground_object self.cp = cp self.game = game self.current_group_value = current_group_value self.setWindowTitle("Buy units @ " + self.ground_object.obj_name) self.setWindowIcon(EVENT_ICONS["capture"]) self.buySamButton = QPushButton("Buy") self.buyArmorButton = QPushButton("Buy") self.buySamLayout = QGridLayout() self.buyArmorLayout = QGridLayout() self.amount = QSpinBox() self.buyArmorCombo = QComboBox() self.samCombo = QComboBox() self.buySamBox = QGroupBox("Buy SAM site :") self.buyArmorBox = QGroupBox("Buy defensive position :") self.init_ui() def init_ui(self): faction = self.game.player_name # Sams possible_sams = get_faction_possible_sams_generator(faction) for sam in possible_sams: self.samCombo.addItem(sam.name + " [$" + str(sam.price) + "M]", userData=sam) self.samCombo.currentIndexChanged.connect(self.samComboChanged) self.buySamLayout.addWidget(QLabel("Site Type :"), 0, 0, Qt.AlignLeft) self.buySamLayout.addWidget(self.samCombo, 0, 1, alignment=Qt.AlignRight) self.buySamLayout.addWidget(self.buySamButton, 1, 1, alignment=Qt.AlignRight) stretch = QVBoxLayout() stretch.addStretch() self.buySamLayout.addLayout(stretch, 2, 0) self.buySamButton.clicked.connect(self.buySam) # Armored units armored_units = db.find_unittype( PinpointStrike, faction) # Todo : refactor this legacy nonsense for unit in set(armored_units): self.buyArmorCombo.addItem(db.unit_type_name_2(unit) + " [$" + str(db.PRICES[unit]) + "M]", userData=unit) self.buyArmorCombo.currentIndexChanged.connect(self.armorComboChanged) self.amount.setMinimum(2) self.amount.setMaximum(8) self.amount.setValue(2) self.amount.valueChanged.connect(self.amountComboChanged) self.buyArmorLayout.addWidget(QLabel("Unit type :"), 0, 0, Qt.AlignLeft) self.buyArmorLayout.addWidget(self.buyArmorCombo, 0, 1, alignment=Qt.AlignRight) self.buyArmorLayout.addWidget(QLabel("Group size :"), 1, 0, alignment=Qt.AlignLeft) self.buyArmorLayout.addWidget(self.amount, 1, 1, alignment=Qt.AlignRight) self.buyArmorLayout.addWidget(self.buyArmorButton, 2, 1, alignment=Qt.AlignRight) stretch2 = QVBoxLayout() stretch2.addStretch() self.buyArmorLayout.addLayout(stretch2, 3, 0) self.buyArmorButton.clicked.connect(self.buyArmor) # Do layout self.buySamBox.setLayout(self.buySamLayout) self.buyArmorBox.setLayout(self.buyArmorLayout) self.mainLayout = QHBoxLayout() self.mainLayout.addWidget(self.buySamBox) if self.ground_object.airbase_group: self.mainLayout.addWidget(self.buyArmorBox) self.setLayout(self.mainLayout) try: self.samComboChanged(0) self.armorComboChanged(0) except: pass def samComboChanged(self, index): self.buySamButton.setText("Buy [$" + str(self.samCombo.itemData(index).price) + "M] [-$" + str(self.current_group_value) + "M]") def armorComboChanged(self, index): self.buyArmorButton.setText( "Buy [$" + str(db.PRICES[self.buyArmorCombo.itemData(index)] * self.amount.value()) + "M][-$" + str(self.current_group_value) + "M]") def amountComboChanged(self): self.buyArmorButton.setText("Buy [$" + str(db.PRICES[ self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())] * self.amount.value()) + "M][-$" + str(self.current_group_value) + "M]") def buyArmor(self): print("Buy Armor ") utype = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex()) print(utype) price = db.PRICES[utype] * self.amount.value( ) - self.current_group_value if price > self.game.budget: self.error_money() self.close() return else: self.game.budget -= price # Generate Armor group = generate_armor_group_of_type_and_size(self.game, self.ground_object, utype, int(self.amount.value())) self.ground_object.groups = [group] GameUpdateSignal.get_instance().updateBudget(self.game) self.changed.emit() self.close() def buySam(self): sam_generator = self.samCombo.itemData(self.samCombo.currentIndex()) price = sam_generator.price - self.current_group_value if price > self.game.budget: self.error_money() return else: self.game.budget -= price # Generate SAM generator = sam_generator(self.game, self.ground_object) generator.generate() generated_group = generator.get_generated_group() self.ground_object.groups = [generated_group] GameUpdateSignal.get_instance().updateBudget(self.game) self.changed.emit() self.close() def error_money(self): msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText("Not enough money to buy these units !") msg.setWindowTitle("Not enough money") msg.setStandardButtons(QMessageBox.Ok) msg.setWindowFlags(Qt.WindowStaysOnTopHint) msg.exec_() self.close()
class Dialog(QDialog): def __init__(self): super(Dialog, self).__init__() self.rotableWidgets = [] self.createRotableGroupBox() self.createOptionsGroupBox() self.createButtonBox() mainLayout = QGridLayout() mainLayout.addWidget(self.rotableGroupBox, 0, 0) mainLayout.addWidget(self.optionsGroupBox, 1, 0) mainLayout.addWidget(self.buttonBox, 2, 0) mainLayout.setSizeConstraint(QLayout.SetMinimumSize) self.mainLayout = mainLayout self.setLayout(self.mainLayout) self.setWindowTitle("Dynamic Layouts") def rotateWidgets(self): count = len(self.rotableWidgets) if count % 2 == 1: raise AssertionError("Number of widgets must be even") for widget in self.rotableWidgets: self.rotableLayout.removeWidget(widget) self.rotableWidgets.append(self.rotableWidgets.pop(0)) for i in range(count//2): self.rotableLayout.addWidget( self.rotableWidgets[count - i - 1], 0, i) self.rotableLayout.addWidget(self.rotableWidgets[i], 1, i) def buttonsOrientationChanged(self, index): self.mainLayout.setSizeConstraint(QLayout.SetNoConstraint) self.setMinimumSize(0, 0) orientation = Qt.Orientation( int(self.buttonsOrientationComboBox.itemData(index))) if orientation == self.buttonBox.orientation(): return self.mainLayout.removeWidget(self.buttonBox) spacing = self.mainLayout.spacing() oldSizeHint = self.buttonBox.sizeHint() + QSize(spacing, spacing) self.buttonBox.setOrientation(orientation) newSizeHint = self.buttonBox.sizeHint() + QSize(spacing, spacing) if orientation == Qt.Horizontal: self.mainLayout.addWidget(self.buttonBox, 2, 0) self.resize( self.size() + QSize(-oldSizeHint.width(), newSizeHint.height())) else: self.mainLayout.addWidget(self.buttonBox, 0, 3, 2, 1) self.resize( self.size() + QSize(newSizeHint.width(), -oldSizeHint.height())) self.mainLayout.setSizeConstraint(QLayout.SetDefaultConstraint) def show_help(self): QMessageBox.information(self, "Dynamic Layouts Help","This example shows how to change layouts dynamically.") def createRotableGroupBox(self): self.rotableGroupBox = QGroupBox("Rotable Widgets") self.rotableWidgets.append(QSpinBox()) self.rotableWidgets.append(QSlider()) self.rotableWidgets.append(QDial()) self.rotableWidgets.append(QProgressBar()) count = len(self.rotableWidgets) for i in range(count): self.rotableWidgets[i].valueChanged[int].connect(self.rotableWidgets[(i+1) % count].setValue) self.rotableLayout = QGridLayout() self.rotableGroupBox.setLayout(self.rotableLayout) self.rotateWidgets() def createOptionsGroupBox(self): self.optionsGroupBox = QGroupBox("Options") buttonsOrientationLabel = QLabel("Orientation of buttons:") buttonsOrientationComboBox = QComboBox() buttonsOrientationComboBox.addItem("Horizontal", Qt.Horizontal) buttonsOrientationComboBox.addItem("Vertical", Qt.Vertical) buttonsOrientationComboBox.currentIndexChanged[int].connect(self.buttonsOrientationChanged) self.buttonsOrientationComboBox = buttonsOrientationComboBox optionsLayout = QGridLayout() optionsLayout.addWidget(buttonsOrientationLabel, 0, 0) optionsLayout.addWidget(self.buttonsOrientationComboBox, 0, 1) optionsLayout.setColumnStretch(2, 1) self.optionsGroupBox.setLayout(optionsLayout) def createButtonBox(self): self.buttonBox = QDialogButtonBox() closeButton = self.buttonBox.addButton(QDialogButtonBox.Close) helpButton = self.buttonBox.addButton(QDialogButtonBox.Help) rotateWidgetsButton = self.buttonBox.addButton("Rotate &Widgets", QDialogButtonBox.ActionRole) rotateWidgetsButton.clicked.connect(self.rotateWidgets) closeButton.clicked.connect(self.close) helpButton.clicked.connect(self.show_help)
def _create_ammo_pickup_boxes(self, size_policy, item_database: ItemDatabase): """ Creates the GroupBox with SpinBoxes for selecting the pickup count of all the ammo :param item_database: :return: """ self._ammo_maximum_spinboxes = collections.defaultdict(list) self._ammo_pickup_widgets = {} resource_database = default_database.resource_database_for(self.game) broad_to_category = { ItemCategory.BEAM_RELATED: ItemCategory.BEAM, ItemCategory.MORPH_BALL_RELATED: ItemCategory.MORPH_BALL, ItemCategory.MISSILE_RELATED: ItemCategory.MISSILE, } for ammo in item_database.ammo.values(): category_box, category_layout, _ = self._boxes_for_category[ broad_to_category[ammo.broad_category]] pickup_box = QGroupBox(category_box) pickup_box.setSizePolicy(size_policy) pickup_box.setTitle(ammo.name + "s") layout = QGridLayout(pickup_box) layout.setObjectName(f"{ammo.name} Box Layout") current_row = 0 for ammo_item in ammo.items: item = resource_database.get_by_type_and_index( ResourceType.ITEM, ammo_item) target_count_label = QLabel(pickup_box) target_count_label.setText(f"{item.long_name} Target" if len( ammo.items) > 1 else "Target count") maximum_spinbox = QSpinBox(pickup_box) maximum_spinbox.setMaximum(ammo.maximum) maximum_spinbox.valueChanged.connect( partial(self._on_update_ammo_maximum_spinbox, ammo_item)) self._ammo_maximum_spinboxes[ammo_item].append(maximum_spinbox) layout.addWidget(target_count_label, current_row, 0) layout.addWidget(maximum_spinbox, current_row, 1) current_row += 1 count_label = QLabel(pickup_box) count_label.setText("Pickup Count") count_label.setToolTip( "How many instances of this expansion should be placed.") pickup_spinbox = QSpinBox(pickup_box) pickup_spinbox.setMaximum(AmmoState.maximum_pickup_count()) pickup_spinbox.valueChanged.connect( partial(self._on_update_ammo_pickup_spinbox, ammo)) layout.addWidget(count_label, current_row, 0) layout.addWidget(pickup_spinbox, current_row, 1) current_row += 1 if ammo.temporaries: require_major_item_check = QCheckBox(pickup_box) require_major_item_check.setText( "Requires the major item to work?") require_major_item_check.stateChanged.connect( partial(self._on_update_ammo_require_major_item, ammo)) layout.addWidget(require_major_item_check, current_row, 0, 1, 2) current_row += 1 else: require_major_item_check = None expected_count = QLabel(pickup_box) expected_count.setWordWrap(True) expected_count.setText(_EXPECTED_COUNT_TEXT_TEMPLATE) expected_count.setToolTip( "Some expansions may provide 1 extra, even with no variance, if the total count " "is not divisible by the pickup count.") layout.addWidget(expected_count, current_row, 0, 1, 2) current_row += 1 self._ammo_pickup_widgets[ammo] = AmmoPickupWidgets( pickup_spinbox, expected_count, pickup_box, require_major_item_check) category_layout.addWidget(pickup_box)
def init_ui(self): self.setAttribute(Qt.WA_StyledBackground, True) self.main_layout = QGridLayout(self) # self.main_layout.setContentsMargins(0, 0, 0, 0) # control group self.control_group = QGroupBox(self.tr("Control")) self.control_layout = QGridLayout(self.control_group) self.resolver_label = QLabel(self.tr("Resolver")) self.resolver_combo_box = QComboBox() self.resolver_combo_box.addItems(["classic", "neural"]) self.control_layout.addWidget(self.resolver_label, 0, 0) self.control_layout.addWidget(self.resolver_combo_box, 0, 1) self.load_dataset_button = QPushButton(qta.icon("fa.database"), self.tr("Load Dataset")) self.load_dataset_button.clicked.connect(self.on_load_dataset_clicked) self.configure_fitting_button = QPushButton( qta.icon("fa.gears"), self.tr("Configure Fitting Algorithm")) self.configure_fitting_button.clicked.connect( self.on_configure_fitting_clicked) self.control_layout.addWidget(self.load_dataset_button, 1, 0) self.control_layout.addWidget(self.configure_fitting_button, 1, 1) self.distribution_label = QLabel(self.tr("Distribution Type")) self.distribution_combo_box = QComboBox() self.distribution_combo_box.addItems( [name for _, name in self.distribution_types]) self.component_number_label = QLabel(self.tr("N<sub>components</sub>")) self.n_components_input = QSpinBox() self.n_components_input.setRange(1, 10) self.n_components_input.setValue(3) self.control_layout.addWidget(self.distribution_label, 2, 0) self.control_layout.addWidget(self.distribution_combo_box, 2, 1) self.control_layout.addWidget(self.component_number_label, 3, 0) self.control_layout.addWidget(self.n_components_input, 3, 1) self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>")) self.n_samples_display = QLabel(self.tr("Unknown")) self.control_layout.addWidget(self.n_samples_label, 4, 0) self.control_layout.addWidget(self.n_samples_display, 4, 1) self.sample_index_label = QLabel(self.tr("Sample Index")) self.sample_index_input = QSpinBox() self.sample_index_input.valueChanged.connect( self.on_sample_index_changed) self.sample_index_input.setEnabled(False) self.control_layout.addWidget(self.sample_index_label, 5, 0) self.control_layout.addWidget(self.sample_index_input, 5, 1) self.sample_name_label = QLabel(self.tr("Sample Name")) self.sample_name_display = QLabel(self.tr("Unknown")) self.control_layout.addWidget(self.sample_name_label, 6, 0) self.control_layout.addWidget(self.sample_name_display, 6, 1) self.manual_test_button = QPushButton(qta.icon("fa.sliders"), self.tr("Manual Test")) self.manual_test_button.setEnabled(False) self.manual_test_button.clicked.connect(self.on_manual_test_clicked) self.load_reference_button = QPushButton(qta.icon("mdi.map-check"), self.tr("Load Reference")) self.load_reference_button.clicked.connect( lambda: self.reference_view.load_dump(mark_ref=True)) self.control_layout.addWidget(self.manual_test_button, 7, 0) self.control_layout.addWidget(self.load_reference_button, 7, 1) self.single_test_button = QPushButton(qta.icon("fa.play-circle"), self.tr("Single Test")) self.single_test_button.setEnabled(False) self.single_test_button.clicked.connect(self.on_single_test_clicked) self.continuous_test_button = QPushButton( qta.icon("mdi.playlist-play"), self.tr("Continuous Test")) self.continuous_test_button.setEnabled(False) self.continuous_test_button.clicked.connect( self.on_continuous_test_clicked) self.control_layout.addWidget(self.single_test_button, 8, 0) self.control_layout.addWidget(self.continuous_test_button, 8, 1) self.test_previous_button = QPushButton( qta.icon("mdi.skip-previous-circle"), self.tr("Test Previous")) self.test_previous_button.setEnabled(False) self.test_previous_button.clicked.connect( self.on_test_previous_clicked) self.test_next_button = QPushButton(qta.icon("mdi.skip-next-circle"), self.tr("Test Next")) self.test_next_button.setEnabled(False) self.test_next_button.clicked.connect(self.on_test_next_clicked) self.control_layout.addWidget(self.test_previous_button, 9, 0) self.control_layout.addWidget(self.test_next_button, 9, 1) # chart group self.chart_group = QGroupBox(self.tr("Chart")) self.chart_layout = QGridLayout(self.chart_group) self.result_chart = MixedDistributionChart(show_mode=True, toolbar=False) self.chart_layout.addWidget(self.result_chart, 0, 0) # table group self.table_group = QGroupBox(self.tr("Table")) self.reference_view = ReferenceResultViewer() self.result_view = FittingResultViewer(self.reference_view) self.result_view.result_marked.connect( lambda result: self.reference_view.add_references([result])) self.table_tab = QTabWidget() self.table_tab.addTab(self.result_view, qta.icon("fa.cubes"), self.tr("Result")) self.table_tab.addTab(self.reference_view, qta.icon("fa5s.key"), self.tr("Reference")) self.result_layout = QGridLayout(self.table_group) self.result_layout.addWidget(self.table_tab, 0, 0) # pack all group self.splitter1 = QSplitter(Qt.Orientation.Vertical) self.splitter1.addWidget(self.control_group) self.splitter1.addWidget(self.chart_group) self.splitter2 = QSplitter(Qt.Orientation.Horizontal) self.splitter2.addWidget(self.splitter1) self.splitter2.addWidget(self.table_group) self.main_layout.addWidget(self.splitter2, 0, 0)
def add_purchase_row(self, unit_type, layout, row): exist = QGroupBox() exist.setProperty("style", "buy-box") exist.setMaximumHeight(36) exist.setMinimumHeight(36) existLayout = QHBoxLayout() exist.setLayout(existLayout) existing_units = self.cp.base.total_units_of_type(unit_type) scheduled_units = self.deliveryEvent.units.get(unit_type, 0) unitName = QLabel("<b>" + db.unit_type_name_2(unit_type) + "</b>") unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) existing_units = QLabel(str(existing_units)) existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) amount_bought = QLabel("<b>{}</b>".format(str(scheduled_units))) amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.existing_units_labels[unit_type] = existing_units self.bought_amount_labels[unit_type] = amount_bought price = QLabel("<b>$ {:02d}</b> m".format(db.PRICES[unit_type])) price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) buysell = QGroupBox() buysell.setProperty("style", "buy-box") buysell.setMaximumHeight(36) buysell.setMinimumHeight(36) buysellayout = QHBoxLayout() buysell.setLayout(buysellayout) buy = QPushButton("+") buy.setProperty("style", "btn-buy") buy.setMinimumSize(16, 16) buy.setMaximumSize(16, 16) buy.clicked.connect(lambda: self.buy(unit_type)) buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell = QPushButton("-") sell.setProperty("style", "btn-sell") sell.setMinimumSize(16, 16) sell.setMaximumSize(16, 16) sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell.clicked.connect(lambda: self.sell(unit_type)) existLayout.addWidget(unitName) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(existing_units) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(price) buysellayout.addWidget(sell) buysellayout.addWidget(amount_bought) buysellayout.addWidget(buy) layout.addWidget(exist, row, 1) layout.addWidget(buysell, row, 2) return row + 1
class QWaitingForMissionResultWindow(QDialog): def __init__(self, gameEvent: Event, game: Game, unit_map: UnitMap) -> None: super(QWaitingForMissionResultWindow, self).__init__() self.setModal(True) self.gameEvent = gameEvent self.game = game self.unit_map = unit_map self.setWindowTitle("Waiting for mission completion.") self.setWindowIcon(QIcon("./resources/icon.png")) self.setMinimumHeight(570) self.initUi() DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect( self.updateLayout) self.wait_thread = wait_for_debriefing( lambda debriefing: self.on_debriefing_update(debriefing), self.game, self.unit_map) def initUi(self): self.layout = QGridLayout() header = QLabel(self) header.setGeometry(0, 0, 655, 106) pixmap = QPixmap("./resources/ui/conflict.png") header.setPixmap(pixmap) self.layout.addWidget(header, 0, 0) self.gridLayout = QGridLayout() jinja = Environment( loader=FileSystemLoader("resources/ui/templates"), autoescape=select_autoescape( disabled_extensions=("", ), default_for_string=True, default=True, ), trim_blocks=True, lstrip_blocks=True, ) self.instructions_text = QTextBrowser() self.instructions_text.setHtml( jinja.get_template("mission_start_EN.j2").render()) self.instructions_text.setOpenExternalLinks(True) self.gridLayout.addWidget(self.instructions_text, 1, 0) progress = QLabel("") progress.setAlignment(QtCore.Qt.AlignCenter) progress_bar = QMovie("./resources/ui/loader.gif") progress.setMovie(progress_bar) self.actions = QGroupBox("Actions :") self.actions_layout = QHBoxLayout() self.actions.setLayout(self.actions_layout) self.manually_submit = QPushButton("Manually Submit [Advanced users]") self.manually_submit.clicked.connect(self.submit_manually) self.actions_layout.addWidget(self.manually_submit) self.cancel = QPushButton("Abort mission") self.cancel.clicked.connect(self.close) self.actions_layout.addWidget(self.cancel) self.gridLayout.addWidget(self.actions, 2, 0) self.actions2 = QGroupBox("Actions :") self.actions2_layout = QHBoxLayout() self.actions2.setLayout(self.actions2_layout) self.manually_submit2 = QPushButton("Manually Submit [Advanced users]") self.manually_submit2.clicked.connect(self.submit_manually) self.actions2_layout.addWidget(self.manually_submit2) self.cancel2 = QPushButton("Abort mission") self.cancel2.clicked.connect(self.close) self.actions2_layout.addWidget(self.cancel2) self.proceed = QPushButton("Accept results") self.proceed.setProperty("style", "btn-success") self.proceed.clicked.connect(self.process_debriefing) self.actions2_layout.addWidget(self.proceed) progress_bar.start() self.layout.addLayout(self.gridLayout, 1, 0) self.setLayout(self.layout) def updateLayout(self, debriefing: Debriefing) -> None: updateBox = QGroupBox("Mission status") updateLayout = QGridLayout() updateBox.setLayout(updateLayout) self.debriefing = debriefing updateLayout.addWidget(QLabel("<b>Aircraft destroyed</b>"), 0, 0) updateLayout.addWidget( QLabel(str(len(list(debriefing.air_losses.losses)))), 0, 1) updateLayout.addWidget(QLabel("<b>Front line units destroyed</b>"), 1, 0) updateLayout.addWidget( QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1) updateLayout.addWidget(QLabel("<b>Other ground units destroyed</b>"), 2, 0) updateLayout.addWidget( QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1) updateLayout.addWidget(QLabel("<b>Buildings destroyed</b>"), 3, 0) updateLayout.addWidget( QLabel(str(len(list(debriefing.building_losses)))), 3, 1) updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 4, 0) updateLayout.addWidget( QLabel(str(len(debriefing.base_capture_events))), 4, 1) # Clear previous content of the window for i in reversed(range(self.gridLayout.count())): try: self.gridLayout.itemAt(i).widget().setParent(None) except: logging.exception("Failed to clear window") # Set new window content self.gridLayout.addWidget(updateBox, 0, 0) if not debriefing.state_data.mission_ended: self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0) self.gridLayout.addWidget(self.actions, 2, 0) else: self.gridLayout.addWidget(QLabel("<b>Mission is over</b>"), 1, 0) self.gridLayout.addWidget(self.actions2, 2, 0) def on_debriefing_update(self, debriefing: Debriefing) -> None: try: logging.info("On Debriefing update") logging.debug(debriefing) DebriefingFileWrittenSignal.get_instance().sendDebriefing( debriefing) except Exception: logging.exception("Got an error while sending debriefing") self.wait_thread = wait_for_debriefing( lambda d: self.on_debriefing_update(d), self.game, self.unit_map) def process_debriefing(self): start = timeit.default_timer() self.game.finish_event(event=self.gameEvent, debriefing=self.debriefing) self.game.pass_turn() GameUpdateSignal.get_instance().sendDebriefing(self.debriefing) GameUpdateSignal.get_instance().updateGame(self.game) end = timeit.default_timer() logging.info("Turn processing took %s", timedelta(seconds=end - start)) self.close() def debriefing_directory_location(self) -> str: return os.path.join(base_path(), "liberation_debriefings") def closeEvent(self, evt): super(QWaitingForMissionResultWindow, self).closeEvent(evt) if self.wait_thread is not None: self.wait_thread.stop() def submit_manually(self): file = QFileDialog.getOpenFileName(self, "Select game file to open", filter="json(*.json)", dir=".") print(file) try: with open(file[0], "r") as json_file: json_data = json.load(json_file) json_data["mission_ended"] = True debriefing = Debriefing(json_data, self.game, self.unit_map) self.on_debriefing_update(debriefing) except Exception as e: logging.error(e) msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText("Invalid file : " + file[0]) msg.setWindowTitle("Invalid file.") msg.setStandardButtons(QMessageBox.Ok) msg.setWindowFlags(Qt.WindowStaysOnTopHint) msg.exec_() return
def initDifficultyLayout(self): self.difficultyPage = QWidget() self.difficultyLayout = QVBoxLayout() self.difficultyLayout.setAlignment(Qt.AlignTop) self.difficultyPage.setLayout(self.difficultyLayout) # DCS AI difficulty settings self.aiDifficultySettings = QGroupBox("AI Difficulty") self.aiDifficultyLayout = QGridLayout() self.playerCoalitionSkill = QComboBox() self.enemyCoalitionSkill = QComboBox() self.enemyAASkill = QComboBox() for skill in CONST.SKILL_OPTIONS: self.playerCoalitionSkill.addItem(skill) self.enemyCoalitionSkill.addItem(skill) self.enemyAASkill.addItem(skill) self.playerCoalitionSkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.player_skill)) self.enemyCoalitionSkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.enemy_skill)) self.enemyAASkill.setCurrentIndex( CONST.SKILL_OPTIONS.index(self.game.settings.enemy_vehicle_skill)) self.player_income = TenthsSpinSlider( "Player income multiplier", 1, 50, int(self.game.settings.player_income_multiplier * 10), ) self.player_income.spinner.valueChanged.connect(self.applySettings) self.enemy_income = TenthsSpinSlider( "Enemy income multiplier", 1, 50, int(self.game.settings.enemy_income_multiplier * 10), ) self.enemy_income.spinner.valueChanged.connect(self.applySettings) self.playerCoalitionSkill.currentIndexChanged.connect( self.applySettings) self.enemyCoalitionSkill.currentIndexChanged.connect( self.applySettings) self.enemyAASkill.currentIndexChanged.connect(self.applySettings) # Mission generation settings related to difficulty self.missionSettings = QGroupBox("Mission Difficulty") self.missionLayout = QGridLayout() self.manpads = QCheckBox() self.manpads.setChecked(self.game.settings.manpads) self.manpads.toggled.connect(self.applySettings) self.noNightMission = QCheckBox() self.noNightMission.setChecked(self.game.settings.night_disabled) self.noNightMission.toggled.connect(self.applySettings) # DCS Mission options self.missionRestrictionsSettings = QGroupBox("Mission Restrictions") self.missionRestrictionsLayout = QGridLayout() self.difficultyLabel = QComboBox() [self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS] self.difficultyLabel.setCurrentIndex( CONST.LABELS_OPTIONS.index(self.game.settings.labels)) self.difficultyLabel.currentIndexChanged.connect(self.applySettings) self.mapVisibiitySelection = QComboBox() self.mapVisibiitySelection.addItem("All", ForcedOptions.Views.All) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All: self.mapVisibiitySelection.setCurrentIndex(0) self.mapVisibiitySelection.addItem("Fog of War", ForcedOptions.Views.Allies) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies: self.mapVisibiitySelection.setCurrentIndex(1) self.mapVisibiitySelection.addItem("Allies Only", ForcedOptions.Views.OnlyAllies) if (self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies): self.mapVisibiitySelection.setCurrentIndex(2) self.mapVisibiitySelection.addItem("Own Aircraft Only", ForcedOptions.Views.MyAircraft) if (self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft): self.mapVisibiitySelection.setCurrentIndex(3) self.mapVisibiitySelection.addItem("Map Only", ForcedOptions.Views.OnlyMap) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap: self.mapVisibiitySelection.setCurrentIndex(4) self.mapVisibiitySelection.currentIndexChanged.connect( self.applySettings) self.ext_views = QCheckBox() self.ext_views.setChecked(self.game.settings.external_views_allowed) self.ext_views.toggled.connect(self.applySettings) self.aiDifficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0) self.aiDifficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight) self.aiDifficultyLayout.addWidget(QLabel("Enemy coalition skill"), 1, 0) self.aiDifficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight) self.aiDifficultyLayout.addWidget( QLabel("Enemy AA and vehicles skill"), 2, 0) self.aiDifficultyLayout.addWidget(self.enemyAASkill, 2, 1, Qt.AlignRight) self.aiDifficultyLayout.addLayout(self.player_income, 3, 0) self.aiDifficultyLayout.addLayout(self.enemy_income, 4, 0) self.aiDifficultySettings.setLayout(self.aiDifficultyLayout) self.difficultyLayout.addWidget(self.aiDifficultySettings) self.missionLayout.addWidget(QLabel("Manpads on frontlines"), 0, 0) self.missionLayout.addWidget(self.manpads, 0, 1, Qt.AlignRight) self.missionLayout.addWidget(QLabel("No night missions"), 1, 0) self.missionLayout.addWidget(self.noNightMission, 1, 1, Qt.AlignRight) self.missionSettings.setLayout(self.missionLayout) self.difficultyLayout.addWidget(self.missionSettings) self.missionRestrictionsLayout.addWidget(QLabel("In Game Labels"), 0, 0) self.missionRestrictionsLayout.addWidget(self.difficultyLabel, 0, 1, Qt.AlignRight) self.missionRestrictionsLayout.addWidget( QLabel("Map visibility options"), 1, 0) self.missionRestrictionsLayout.addWidget(self.mapVisibiitySelection, 1, 1, Qt.AlignRight) self.missionRestrictionsLayout.addWidget( QLabel("Allow external views"), 2, 0) self.missionRestrictionsLayout.addWidget(self.ext_views, 2, 1, Qt.AlignRight) self.missionRestrictionsSettings.setLayout( self.missionRestrictionsLayout) self.difficultyLayout.addWidget(self.missionRestrictionsSettings)
def initUi(self): self.layout = QGridLayout() header = QLabel(self) header.setGeometry(0, 0, 655, 106) pixmap = QPixmap("./resources/ui/conflict.png") header.setPixmap(pixmap) self.layout.addWidget(header, 0, 0) self.gridLayout = QGridLayout() jinja = Environment( loader=FileSystemLoader("resources/ui/templates"), autoescape=select_autoescape( disabled_extensions=("", ), default_for_string=True, default=True, ), trim_blocks=True, lstrip_blocks=True, ) self.instructions_text = QTextBrowser() self.instructions_text.setHtml( jinja.get_template("mission_start_EN.j2").render()) self.instructions_text.setOpenExternalLinks(True) self.gridLayout.addWidget(self.instructions_text, 1, 0) progress = QLabel("") progress.setAlignment(QtCore.Qt.AlignCenter) progress_bar = QMovie("./resources/ui/loader.gif") progress.setMovie(progress_bar) self.actions = QGroupBox("Actions :") self.actions_layout = QHBoxLayout() self.actions.setLayout(self.actions_layout) self.manually_submit = QPushButton("Manually Submit [Advanced users]") self.manually_submit.clicked.connect(self.submit_manually) self.actions_layout.addWidget(self.manually_submit) self.cancel = QPushButton("Abort mission") self.cancel.clicked.connect(self.close) self.actions_layout.addWidget(self.cancel) self.gridLayout.addWidget(self.actions, 2, 0) self.actions2 = QGroupBox("Actions :") self.actions2_layout = QHBoxLayout() self.actions2.setLayout(self.actions2_layout) self.manually_submit2 = QPushButton("Manually Submit [Advanced users]") self.manually_submit2.clicked.connect(self.submit_manually) self.actions2_layout.addWidget(self.manually_submit2) self.cancel2 = QPushButton("Abort mission") self.cancel2.clicked.connect(self.close) self.actions2_layout.addWidget(self.cancel2) self.proceed = QPushButton("Accept results") self.proceed.setProperty("style", "btn-success") self.proceed.clicked.connect(self.process_debriefing) self.actions2_layout.addWidget(self.proceed) progress_bar.start() self.layout.addLayout(self.gridLayout, 1, 0) self.setLayout(self.layout)
def initGeneratorLayout(self): self.generatorPage = QWidget() self.generatorLayout = QVBoxLayout() self.generatorLayout.setAlignment(Qt.AlignTop) self.generatorPage.setLayout(self.generatorLayout) self.gameplay = QGroupBox("Gameplay") self.gameplayLayout = QGridLayout() self.gameplayLayout.setAlignment(Qt.AlignTop) self.gameplay.setLayout(self.gameplayLayout) self.supercarrier = QCheckBox() self.supercarrier.setChecked(self.game.settings.supercarrier) self.supercarrier.toggled.connect(self.applySettings) self.generate_marks = QCheckBox() self.generate_marks.setChecked(self.game.settings.generate_marks) self.generate_marks.toggled.connect(self.applySettings) self.never_delay_players = QCheckBox() self.never_delay_players.setChecked( self.game.settings.never_delay_player_flights) self.never_delay_players.toggled.connect(self.applySettings) self.never_delay_players.setToolTip( "When checked, player flights with a delayed start time will be " "spawned immediately. AI wingmen may begin startup immediately.") self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0) self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0) self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Never delay player flights"), 2, 0) self.gameplayLayout.addWidget(self.never_delay_players, 2, 1, Qt.AlignRight) start_type_label = QLabel( "Default start type for AI aircraft:<br /><strong>Warning: " + "Any option other than Cold breaks OCA/Aircraft missions.</strong>" ) start_type_label.setToolTip(START_TYPE_TOOLTIP) start_type = StartTypeComboBox(self.game.settings) start_type.setCurrentText(self.game.settings.default_start_type) self.gameplayLayout.addWidget(start_type_label, 3, 0) self.gameplayLayout.addWidget(start_type, 3, 1) self.performance = QGroupBox("Performance") self.performanceLayout = QGridLayout() self.performanceLayout.setAlignment(Qt.AlignTop) self.performance.setLayout(self.performanceLayout) self.smoke = QCheckBox() self.smoke.setChecked(self.game.settings.perf_smoke_gen) self.smoke.toggled.connect(self.applySettings) self.red_alert = QCheckBox() self.red_alert.setChecked(self.game.settings.perf_red_alert_state) self.red_alert.toggled.connect(self.applySettings) self.arti = QCheckBox() self.arti.setChecked(self.game.settings.perf_artillery) self.arti.toggled.connect(self.applySettings) self.moving_units = QCheckBox() self.moving_units.setChecked(self.game.settings.perf_moving_units) self.moving_units.toggled.connect(self.applySettings) self.infantry = QCheckBox() self.infantry.setChecked(self.game.settings.perf_infantry) self.infantry.toggled.connect(self.applySettings) self.destroyed_units = QCheckBox() self.destroyed_units.setChecked( self.game.settings.perf_destroyed_units) self.destroyed_units.toggled.connect(self.applySettings) self.culling = QCheckBox() self.culling.setChecked(self.game.settings.perf_culling) self.culling.toggled.connect(self.applySettings) self.culling_distance = QSpinBox() self.culling_distance.setMinimum(10) self.culling_distance.setMaximum(10000) self.culling_distance.setValue( self.game.settings.perf_culling_distance) self.culling_distance.valueChanged.connect(self.applySettings) self.culling_do_not_cull_carrier = QCheckBox() self.culling_do_not_cull_carrier.setChecked( self.game.settings.perf_do_not_cull_carrier) self.culling_do_not_cull_carrier.toggled.connect(self.applySettings) self.performanceLayout.addWidget( QLabel("Smoke visual effect on frontline"), 0, 0) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("SAM starts in RED alert mode"), 1, 0) self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0) self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0) self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Generate infantry squads along vehicles"), 4, 0) self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Include destroyed units carcass"), 6, 0) self.performanceLayout.addWidget(self.destroyed_units, 6, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QHorizontalSeparationLine(), 7, 0, 1, 2) self.performanceLayout.addWidget( QLabel("Culling of distant units enabled"), 8, 0) self.performanceLayout.addWidget(self.culling, 8, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Culling distance (km)"), 9, 0) self.performanceLayout.addWidget(self.culling_distance, 9, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget( QLabel("Do not cull carrier's surroundings"), 10, 0) self.performanceLayout.addWidget(self.culling_do_not_cull_carrier, 10, 1, alignment=Qt.AlignRight) self.generatorLayout.addWidget(self.gameplay) self.generatorLayout.addWidget( QLabel( "Disabling settings below may improve performance, but will impact the overall quality of the experience." )) self.generatorLayout.addWidget(self.performance)
def createTurnTableControllerTabWidget(self): ipAddressLabel = QLabel("IP Address: ") self.ipAddressLineEdit = createLineEdit( self.configManager.turnTableIPAddress) self.watchdogPortLineEdit = createLineEdit( self.configManager.watchdogPort) self.shaftEncoderPortLineEdit = createLineEdit( self.configManager.shaftEncoderPort) self.motorControllerPortLineEdit = createLineEdit( self.configManager.motorControllerPort) portSettingsLayout = QFormLayout() portSettingsLayout.addRow(QLabel("Watchdog Port: "), self.watchdogPortLineEdit) portSettingsLayout.addRow(QLabel("Shaft Encoder Port: "), self.shaftEncoderPortLineEdit) portSettingsLayout.addRow(QLabel("Motor Controller Port: "), self.motorControllerPortLineEdit) portsGroupBox = QGroupBox("Ports") portsGroupBox.setLayout(portSettingsLayout) ipAddressLayout = QHBoxLayout() ipAddressLayout.addWidget(ipAddressLabel) ipAddressLayout.addWidget(self.ipAddressLineEdit) settingsLayout = QVBoxLayout() settingsLayout.addLayout(ipAddressLayout) settingsLayout.addWidget(portsGroupBox) connectionSettingsGroupBox = QGroupBox("Connection Settings") connectionSettingsGroupBox.setLayout(settingsLayout) self.proportionalGainLineEdit = createLineEdit( self.configManager.controlProportionalGain) self.integralGainLineEdit = createLineEdit( self.configManager.controlIntegralGain) self.derivativeGainLineEdit = createLineEdit( self.configManager.controlDerivativeGain) self.minimumControlSignalLineEdit = createLineEdit( self.configManager.minimumControlSignalValue) pidControllerSettingsLayout = QFormLayout() pidControllerSettingsLayout.addRow(QLabel("Proportional Gain: "), self.proportionalGainLineEdit) pidControllerSettingsLayout.addRow(QLabel("Integral Gain: "), self.integralGainLineEdit) pidControllerSettingsLayout.addRow(QLabel("Derivative Gain: "), self.derivativeGainLineEdit) pidControllerSettingsLayout.addRow(QLabel("Minimum Control Signal: "), self.minimumControlSignalLineEdit) pidControllerSettingsGroupBox = QGroupBox("PID Controller") pidControllerSettingsGroupBox.setLayout(pidControllerSettingsLayout) self.minimumGotoPositionLineEdit = createLineEdit( self.configManager.minimumGotoPosition) self.maximumGotoPositionLineEdit = createLineEdit( self.configManager.maximumGotoPosition) self.minimumStepSizeLineEdit = createLineEdit( self.configManager.minimumStepSize) self.maximumStepSizeLineEdit = createLineEdit( self.configManager.maximumStepSize) positionSettingsLayout = QFormLayout() positionSettingsLayout.addRow(QLabel("Minimum Goto Position: "), self.minimumGotoPositionLineEdit) positionSettingsLayout.addRow(QLabel("Maximum Goto Position: "), self.maximumGotoPositionLineEdit) positionSettingsLayout.addRow(QLabel("Minimum Step Size: "), self.minimumStepSizeLineEdit) positionSettingsLayout.addRow(QLabel("Maximum Step Size: "), self.maximumStepSizeLineEdit) positionSettingsGroupBox = QGroupBox("Position Settings") positionSettingsGroupBox.setLayout(positionSettingsLayout) settingsLayout = QVBoxLayout() settingsLayout.addWidget(connectionSettingsGroupBox) settingsLayout.addWidget(pidControllerSettingsGroupBox) settingsLayout.addWidget(positionSettingsGroupBox) settingsTabWidget = QWidget() settingsTabWidget.setLayout(settingsLayout) return settingsTabWidget
def __init__(self, mode, parentQWidget = None): QVBoxLayout.__init__(self) self.sig.connect(self.addThreadList) self.mode = mode self.sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.groupBoxSearch = QGroupBox() self.groupBoxSearch.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 4px; };") vboxSearch = QVBoxLayout() self.searchTitle = QLabel("Search Messages") vboxSearch.addWidget(self.searchTitle) self.searchHLayout = QHBoxLayout() self.editTextSearch = QTextEdit('') self.editTextSearch.setFixedSize(200,30) self.buttonSearch = QPushButton('Search') self.buttonSearch.setFixedSize(100,30) self.buttonSearch.clicked.connect(self.searchMsg) vboxSearch.addWidget(self.editTextSearch) self.searchHLayout.addWidget(self.buttonSearch) self.searchCursor = QLabel() self.searchHLayout.addWidget(self.searchCursor) vboxSearch.addLayout(self.searchHLayout) self.browseHLayout = QHBoxLayout() self.buttonLookUp = QPushButton('\u21e7') #Arrow up self.buttonLookUp.setFixedWidth(100) self.buttonLookUp.clicked.connect(self.moveToPrev) self.buttonLookDown = QPushButton('\u21e9') #Arrow down self.buttonLookDown.setFixedWidth(100) self.buttonLookDown.clicked.connect(self.moveToNext) self.browseHLayout.addWidget(self.buttonLookUp) self.browseHLayout.addWidget(self.buttonLookDown) vboxSearch.addLayout(self.browseHLayout) self.groupBoxSearch.setLayout(vboxSearch) self.addWidget(self.groupBoxSearch) self.groupBoxSearch.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.buttonHiddenLifelines = QPushButton('Show hidden life-lines') self.buttonHiddenLifelines.setFixedWidth(200) self.buttonHiddenLifelines.clicked.connect(self.showHiddenLifelines) self.addWidget(self.buttonHiddenLifelines) self.buttonHiddenMessages = QPushButton('Show hidden Messages') self.buttonHiddenMessages.setFixedWidth(200) self.buttonHiddenMessages.clicked.connect(self.showHiddenMessages) self.addWidget(self.buttonHiddenMessages) if const.mode_interactive == mode: self.buttonCapture = QPushButton('Capture') self.buttonCapture.setFixedWidth(200) self.buttonCapture.clicked.connect(self.notifyCapture) self.addWidget(self.buttonCapture) self.msgRcv = [] self.msgInfo = QLabel("Message Info.") self.groupBoxMessageInfo = QGroupBox() self.groupBoxMessageInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") vbox = QVBoxLayout() vbox.addWidget(self.msgInfo) self.tableTime = QTableWidget(3,2) self.tableTime.setHorizontalHeaderLabels(['-','time']) self.tableTime.setColumnWidth(0,80) self.tableTime.setColumnWidth(1,150) vwidth = self.tableTime.verticalHeader().length() hwidth = self.tableTime.horizontalHeader().height() fwidth = self.tableTime.frameWidth() * 2 self.tableTime.setFixedHeight(vwidth + hwidth + fwidth) self.tableTime.horizontalHeader().setStretchLastSection(True) self.tableTime.setItem(0,0,QTableWidgetItem('begin')) self.tableTime.setItem(0,1,QTableWidgetItem(' - ')) self.tableTime.setItem(1,0,QTableWidgetItem('end')) self.tableTime.setItem(1,1,QTableWidgetItem(' - ')) self.tableTime.setItem(2,0,QTableWidgetItem('duration')) self.tableTime.setItem(2,1,QTableWidgetItem(' - ')) vbox.addWidget(self.tableTime) self.titleArg = QLabel('Argument List') vbox.addWidget(self.titleArg) max_arg_num = 10 self.tableArgs = QTableWidget(max_arg_num,2) self.tableArgs.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_arg_num): self.tableArgs.setItem(idx,0,QTableWidgetItem()) self.tableArgs.setItem(idx,1,QTableWidgetItem()) self.tableArgs.horizontalHeader().setStretchLastSection(True) vbox.addWidget(self.tableArgs) self.titleArg = QLabel('Return Value List') vbox.addWidget(self.titleArg) max_ret_num = 4 self.tableRet = QTableWidget(max_ret_num,2) self.tableRet.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_ret_num): self.tableRet.setItem(idx,0,QTableWidgetItem()) self.tableRet.setItem(idx,1,QTableWidgetItem()) self.tableRet.horizontalHeader().setStretchLastSection(True) vwidth = self.tableRet.verticalHeader().length() hwidth = self.tableRet.horizontalHeader().height() fwidth = self.tableRet.frameWidth() * 2 self.tableRet.setFixedHeight(vwidth + hwidth + fwidth) vbox.addWidget(self.tableRet) self.buttonSrcView = QPushButton('view code') self.buttonSrcView.setFixedWidth(200) self.buttonSrcView.clicked.connect(self.openSourceViewer) self.buttonHide = QPushButton('Hide') self.buttonHide.setFixedWidth(200) self.buttonHide.clicked.connect(self.notifyHide) self.buttonHideAllMsg = QPushButton('Hide All') self.buttonHideAllMsg.setFixedWidth(200) self.buttonHideAllMsg.clicked.connect(self.hideAllMsgNamedAsSelected) self.groupBoxMessageInfo.setLayout(vbox) self.checkHideCircular = QCheckBox('Hide Circular Messages') self.checkHideCircular.setCheckState(QtCore.Qt.Unchecked) self.checkHideCircular.stateChanged.connect(self.changeHideCircularMessage) self.addWidget(self.checkHideCircular) self.addWidget(self.groupBoxMessageInfo) self.groupBoxMessageInfo.setSizePolicy(self.sizePolicy)
class View(QMainWindow): def __init__(self, model): super().__init__() self.setWindowTitle("OpenHRV") self.setWindowIcon(QIcon(":/logo.png")) self.setGeometry(50, 50, 1750, 850) self.model = model self.signals = ViewSignals() self.scanner = SensorScanner() self.scanner_thread = QThread(self) self.scanner.moveToThread(self.scanner_thread) self.scanner.mac_update.connect(self.model.set_mac_addresses) self.sensor = SensorClient() self.sensor_thread = QThread(self) self.sensor.moveToThread(self.sensor_thread) self.sensor.ibi_update.connect(self.model.set_ibis_buffer) self.sensor_thread.started.connect(self.sensor.run) self.redis_publisher = RedisPublisher() self.redis_publisher_thread = QThread(self) self.redis_publisher.moveToThread(self.redis_publisher_thread) self.model.ibis_buffer_update.connect(self.redis_publisher.publish) self.model.mean_hrv_update.connect(self.redis_publisher.publish) self.model.mac_addresses_update.connect(self.redis_publisher.publish) self.model.pacer_rate_update.connect(self.redis_publisher.publish) self.model.hrv_target_update.connect(self.redis_publisher.publish) self.model.biofeedback_update.connect(self.redis_publisher.publish) self.signals.annotation.connect(self.redis_publisher.publish) self.redis_publisher_thread.started.connect( self.redis_publisher.monitor.start) self.redis_logger = RedisLogger() self.redis_logger_thread = QThread(self) self.redis_logger.moveToThread(self.redis_logger_thread) self.redis_logger_thread.finished.connect( self.redis_logger.save_recording) self.redis_logger.recording_status.connect(self.show_recording_status) self.ibis_plot = pg.PlotWidget() self.ibis_plot.setBackground("w") self.ibis_plot.setLabel("left", "Inter-Beat-Interval (msec)", **{"font-size": "25px"}) self.ibis_plot.setLabel("bottom", "Seconds", **{"font-size": "25px"}) self.ibis_plot.showGrid(y=True) self.ibis_plot.setYRange(300, 1500, padding=0) self.ibis_plot.setMouseEnabled(x=False, y=False) self.ibis_signal = pg.PlotCurveItem() pen = pg.mkPen(color=(0, 191, 255), width=7.5) self.ibis_signal.setPen(pen) self.ibis_signal.setData(self.model.ibis_seconds, self.model.ibis_buffer) self.ibis_plot.addItem(self.ibis_signal) self.mean_hrv_plot = pg.PlotWidget() self.mean_hrv_plot.setBackground("w") self.mean_hrv_plot.setLabel("left", "HRV (msec)", **{"font-size": "25px"}) self.mean_hrv_plot.setLabel("bottom", "Seconds", **{"font-size": "25px"}) self.mean_hrv_plot.showGrid(y=True) self.mean_hrv_plot.setYRange(0, 600, padding=0) self.mean_hrv_plot.setMouseEnabled(x=False, y=False) colorgrad = QLinearGradient(0, 0, 0, 1) # horizontal gradient colorgrad.setCoordinateMode(QGradient.ObjectMode) colorgrad.setColorAt(0, pg.mkColor("g")) colorgrad.setColorAt(.5, pg.mkColor("y")) colorgrad.setColorAt(1, pg.mkColor("r")) brush = QBrush(colorgrad) self.mean_hrv_plot.getViewBox().setBackgroundColor(brush) self.mean_hrv_signal = pg.PlotCurveItem() pen = pg.mkPen(color="w", width=7.5) self.mean_hrv_signal.setPen(pen) self.mean_hrv_signal.setData(self.model.mean_hrv_seconds, self.model.mean_hrv_buffer) self.mean_hrv_plot.addItem(self.mean_hrv_signal) self.pacer_plot = pg.PlotWidget() self.pacer_plot.setBackground("w") self.pacer_plot.setAspectLocked(lock=True, ratio=1) self.pacer_plot.setMouseEnabled(x=False, y=False) self.pacer_plot.disableAutoRange() self.pacer_plot.setXRange(-1, 1, padding=0) self.pacer_plot.setYRange(-1, 1, padding=0) self.pacer_plot.hideAxis("left") self.pacer_plot.hideAxis("bottom") self.pacer_disc = pg.PlotCurveItem() brush = pg.mkBrush(color=(135, 206, 250)) self.pacer_disc.setBrush(brush) self.pacer_disc.setFillLevel(1) self.pacer_plot.addItem(self.pacer_disc) self.pacer_rate = QSlider(Qt.Horizontal) self.pacer_rate.setTracking(False) self.pacer_rate.setRange( 0, 6) # transformed to bpm [4, 7], step .5 by model self.pacer_rate.valueChanged.connect(self.model.set_breathing_rate) self.pacer_rate.setSliderPosition(4) # corresponds to 6 bpm self.pacer_label = QLabel(f"Rate: {self.model.breathing_rate}") self.pacer_toggle = QCheckBox("Show pacer", self) self.pacer_toggle.setChecked(True) self.pacer_toggle.stateChanged.connect(self.toggle_pacer) self.hrv_target_label = QLabel(f"Target: {self.model.hrv_target}") self.hrv_target = QSlider(Qt.Horizontal) self.hrv_target.setRange(50, 600) self.hrv_target.setSingleStep(10) self.hrv_target.valueChanged.connect(self.model.set_hrv_target) self.hrv_target.setSliderPosition(self.model.hrv_target) self.mean_hrv_plot.setYRange(0, self.model.hrv_target, padding=0) self.scan_button = QPushButton("Scan") self.scan_button.clicked.connect(self.scanner.scan) self.mac_menu = QComboBox() self.connect_button = QPushButton("Connect") self.connect_button.clicked.connect(self.connect_sensor) self.start_recording_button = QPushButton("Start") self.start_recording_button.clicked.connect( self.redis_logger.start_recording) self.save_recording_button = QPushButton("Save") self.save_recording_button.clicked.connect( self.redis_logger.save_recording) self.annotation = QComboBox() self.annotation.setEditable(True) self.annotation.setDuplicatesEnabled(False) self.annotation.addItems([ "start_baseline", "end_baseline", "start_bf", "end_bf", "start_nobf", "end_nobf" ]) self.annotation.setMaxCount( 10) # user can configure up to 4 additional custom annotations self.annotation_button = QPushButton("Annotate") self.annotation_button.clicked.connect(self.emit_annotation) self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.recording_status_label = QLabel("Status:") self.recording_statusbar = QProgressBar() self.recording_statusbar.setRange(0, 1) self.vlayout0 = QVBoxLayout(self.central_widget) self.hlayout0 = QHBoxLayout() self.hlayout0.addWidget(self.ibis_plot, stretch=80) self.hlayout0.addWidget(self.pacer_plot, stretch=20) self.vlayout0.addLayout(self.hlayout0) self.vlayout0.addWidget(self.mean_hrv_plot) self.hlayout1 = QHBoxLayout() self.device_config = QFormLayout() self.device_config.addRow(self.scan_button, self.mac_menu) self.device_config.addRow(self.connect_button) self.device_panel = QGroupBox("ECG Devices") self.device_panel.setLayout(self.device_config) self.hlayout1.addWidget(self.device_panel, stretch=25) self.hrv_config = QFormLayout() self.hrv_config.addRow(self.hrv_target_label, self.hrv_target) self.hrv_panel = QGroupBox("HRV Settings") self.hrv_panel.setLayout(self.hrv_config) self.hlayout1.addWidget(self.hrv_panel, stretch=25) self.pacer_config = QFormLayout() self.pacer_config.addRow(self.pacer_label, self.pacer_rate) self.pacer_config.addRow(self.pacer_toggle) self.pacer_panel = QGroupBox("Breathing Pacer") self.pacer_panel.setLayout(self.pacer_config) self.hlayout1.addWidget(self.pacer_panel, stretch=25) self.recording_config = QGridLayout() self.recording_config.addWidget(self.start_recording_button, 0, 0) self.recording_config.addWidget(self.save_recording_button, 0, 1) self.recording_config.addWidget(self.recording_statusbar, 0, 2) self.recording_config.addWidget(self.annotation, 1, 0, 1, 2) # row, column, rowspan, columnspan self.recording_config.addWidget(self.annotation_button, 1, 2) self.recording_panel = QGroupBox("Recording") self.recording_panel.setLayout(self.recording_config) self.hlayout1.addWidget(self.recording_panel, stretch=25) self.vlayout0.addLayout(self.hlayout1) self.model.ibis_buffer_update.connect(self.plot_ibis) self.model.mean_hrv_update.connect(self.plot_hrv) self.model.mac_addresses_update.connect(self.list_macs) self.model.pacer_disk_update.connect(self.plot_pacer_disk) self.model.pacer_rate_update.connect(self.update_pacer_label) self.model.hrv_target_update.connect(self.update_hrv_target) self.scanner_thread.start() self.sensor_thread.start() self.redis_publisher_thread.start() self.redis_logger_thread.start() def closeEvent(self, event): """Properly shut down all threads.""" print("Closing threads...") self.scanner_thread.quit() self.scanner_thread.wait() self.sensor_thread.quit( ) # since quit() only works if the thread has a running event loop... asyncio.run_coroutine_threadsafe( self.sensor.stop(), self.sensor.loop ) # ...the event loop must only be stopped AFTER quit() has been called! self.sensor_thread.wait() self.redis_publisher_thread.quit() self.redis_publisher_thread.wait() self.redis_logger_thread.quit() self.redis_logger_thread.wait() def connect_sensor(self): mac = self.mac_menu.currentText() if not valid_mac(mac): print("Invalid MAC.") return asyncio.run_coroutine_threadsafe(self.sensor.connect_client(mac), self.sensor.loop) def plot_ibis(self, ibis): self.ibis_signal.setData(self.model.ibis_seconds, ibis[1]) def plot_hrv(self, hrv): self.mean_hrv_signal.setData(self.model.mean_hrv_seconds, hrv[1]) def list_macs(self, macs): self.mac_menu.clear() self.mac_menu.addItems(macs[1]) def plot_pacer_disk(self, coordinates): self.pacer_disc.setData(*coordinates[1]) def update_pacer_label(self, rate): self.pacer_label.setText(f"Rate: {rate[1]}") def update_hrv_target(self, target): self.mean_hrv_plot.setYRange(0, target[1], padding=0) self.hrv_target_label.setText(f"Target: {target[1]}") def toggle_pacer(self): visible = self.pacer_plot.isVisible() self.pacer_plot.setVisible(not visible) def show_recording_status(self, status): self.recording_statusbar.setRange( 0, status) # indicates busy state if progress is 0 def emit_annotation(self): self.signals.annotation.emit( ("eventmarker", self.annotation.currentText()))
class ToolBox(QVBoxLayout): sig = QtCore.Signal(object) listThread = None groupBoxThreadInfo = None threadvbox = None mode = None def __init__(self, mode, parentQWidget = None): QVBoxLayout.__init__(self) self.sig.connect(self.addThreadList) self.mode = mode self.sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.groupBoxSearch = QGroupBox() self.groupBoxSearch.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 4px; };") vboxSearch = QVBoxLayout() self.searchTitle = QLabel("Search Messages") vboxSearch.addWidget(self.searchTitle) self.searchHLayout = QHBoxLayout() self.editTextSearch = QTextEdit('') self.editTextSearch.setFixedSize(200,30) self.buttonSearch = QPushButton('Search') self.buttonSearch.setFixedSize(100,30) self.buttonSearch.clicked.connect(self.searchMsg) vboxSearch.addWidget(self.editTextSearch) self.searchHLayout.addWidget(self.buttonSearch) self.searchCursor = QLabel() self.searchHLayout.addWidget(self.searchCursor) vboxSearch.addLayout(self.searchHLayout) self.browseHLayout = QHBoxLayout() self.buttonLookUp = QPushButton('\u21e7') #Arrow up self.buttonLookUp.setFixedWidth(100) self.buttonLookUp.clicked.connect(self.moveToPrev) self.buttonLookDown = QPushButton('\u21e9') #Arrow down self.buttonLookDown.setFixedWidth(100) self.buttonLookDown.clicked.connect(self.moveToNext) self.browseHLayout.addWidget(self.buttonLookUp) self.browseHLayout.addWidget(self.buttonLookDown) vboxSearch.addLayout(self.browseHLayout) self.groupBoxSearch.setLayout(vboxSearch) self.addWidget(self.groupBoxSearch) self.groupBoxSearch.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.buttonHiddenLifelines = QPushButton('Show hidden life-lines') self.buttonHiddenLifelines.setFixedWidth(200) self.buttonHiddenLifelines.clicked.connect(self.showHiddenLifelines) self.addWidget(self.buttonHiddenLifelines) self.buttonHiddenMessages = QPushButton('Show hidden Messages') self.buttonHiddenMessages.setFixedWidth(200) self.buttonHiddenMessages.clicked.connect(self.showHiddenMessages) self.addWidget(self.buttonHiddenMessages) if const.mode_interactive == mode: self.buttonCapture = QPushButton('Capture') self.buttonCapture.setFixedWidth(200) self.buttonCapture.clicked.connect(self.notifyCapture) self.addWidget(self.buttonCapture) self.msgRcv = [] self.msgInfo = QLabel("Message Info.") self.groupBoxMessageInfo = QGroupBox() self.groupBoxMessageInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") vbox = QVBoxLayout() vbox.addWidget(self.msgInfo) self.tableTime = QTableWidget(3,2) self.tableTime.setHorizontalHeaderLabels(['-','time']) self.tableTime.setColumnWidth(0,80) self.tableTime.setColumnWidth(1,150) vwidth = self.tableTime.verticalHeader().length() hwidth = self.tableTime.horizontalHeader().height() fwidth = self.tableTime.frameWidth() * 2 self.tableTime.setFixedHeight(vwidth + hwidth + fwidth) self.tableTime.horizontalHeader().setStretchLastSection(True) self.tableTime.setItem(0,0,QTableWidgetItem('begin')) self.tableTime.setItem(0,1,QTableWidgetItem(' - ')) self.tableTime.setItem(1,0,QTableWidgetItem('end')) self.tableTime.setItem(1,1,QTableWidgetItem(' - ')) self.tableTime.setItem(2,0,QTableWidgetItem('duration')) self.tableTime.setItem(2,1,QTableWidgetItem(' - ')) vbox.addWidget(self.tableTime) self.titleArg = QLabel('Argument List') vbox.addWidget(self.titleArg) max_arg_num = 10 self.tableArgs = QTableWidget(max_arg_num,2) self.tableArgs.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_arg_num): self.tableArgs.setItem(idx,0,QTableWidgetItem()) self.tableArgs.setItem(idx,1,QTableWidgetItem()) self.tableArgs.horizontalHeader().setStretchLastSection(True) vbox.addWidget(self.tableArgs) self.titleArg = QLabel('Return Value List') vbox.addWidget(self.titleArg) max_ret_num = 4 self.tableRet = QTableWidget(max_ret_num,2) self.tableRet.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_ret_num): self.tableRet.setItem(idx,0,QTableWidgetItem()) self.tableRet.setItem(idx,1,QTableWidgetItem()) self.tableRet.horizontalHeader().setStretchLastSection(True) vwidth = self.tableRet.verticalHeader().length() hwidth = self.tableRet.horizontalHeader().height() fwidth = self.tableRet.frameWidth() * 2 self.tableRet.setFixedHeight(vwidth + hwidth + fwidth) vbox.addWidget(self.tableRet) self.buttonSrcView = QPushButton('view code') self.buttonSrcView.setFixedWidth(200) self.buttonSrcView.clicked.connect(self.openSourceViewer) self.buttonHide = QPushButton('Hide') self.buttonHide.setFixedWidth(200) self.buttonHide.clicked.connect(self.notifyHide) self.buttonHideAllMsg = QPushButton('Hide All') self.buttonHideAllMsg.setFixedWidth(200) self.buttonHideAllMsg.clicked.connect(self.hideAllMsgNamedAsSelected) self.groupBoxMessageInfo.setLayout(vbox) self.checkHideCircular = QCheckBox('Hide Circular Messages') self.checkHideCircular.setCheckState(QtCore.Qt.Unchecked) self.checkHideCircular.stateChanged.connect(self.changeHideCircularMessage) self.addWidget(self.checkHideCircular) self.addWidget(self.groupBoxMessageInfo) self.groupBoxMessageInfo.setSizePolicy(self.sizePolicy) def reset(self): for idx in reversed(range(0,self.listThread.count())): self.listThread.takeItem(idx) def setMsgInfoMessage(self,msg): self.strMessage = msg def changeHideCircularMessage(self,state): if state == QtCore.Qt.Unchecked: self.diagramView.hideCircularChanged(False) elif state == QtCore.Qt.Checked: self.diagramView.hideCircularChanged(True) def setMsgInfoModule(self,module): self.strModule = module def updateSearchStatus(self,curr,number): self.searchCursor.setText("%d/%d" % (curr,number)) def connectSourceViewer(self,viewer): self.srcViewer = viewer def openSourceViewer(self): self.srcViewer.openViewer(self.strModule,self.strMessage) def setMessageInfoTime(self,begin,end,duration): self.tableTime.item(0,1).setText(begin) self.tableTime.item(1,1).setText(end) self.tableTime.item(2,1).setText(duration + ' msec') def setMessageInfoArg(self,listParam,listArg): # Clear the contents in the table max_arg_num = 10 for idx in range(0,max_arg_num): self.tableArgs.item(idx,0).setText('') self.tableArgs.item(idx,1).setText('') if listArg: for idx, text in enumerate(listArg): self.tableArgs.item(idx,1).setText(text) for idx, text in enumerate(listParam): self.tableArgs.item(idx,0).setText(text) else: for idx in range(0,self.tableArgs.rowCount()): self.tableArgs.item(idx,1).setText('') self.tableArgs.item(idx,0).setText('') def setMessageInfoRet(self,listRet): if listRet: for idx, text in enumerate(listRet): self.tableRet.item(idx,1).setText(text) else: for idx in range(0,self.tableRet.rowCount()): self.tableRet.item(idx,1).setText('') self.tableRet.item(idx,0).setText('') def notifyInteractiveStateChanged(self,state): if const.mode_interactive != self.mode: return if const.STATE_INTERACTIVE_CAPTURING == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Stop Capture') if const.STATE_INTERACTIVE_PROCESSING == state: self.buttonCapture.setEnabled(False) if const.STATE_INTERACTIVE_IDLE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') if const.STATE_INTERACTIVE_RESET == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') elif const.STATE_INTERACTIVE_ACTIVE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') def setMessageInfo(self,info): self.msgInfo.setText(info) def setAvailable(self,threads): self.sig.emit(threads) def toggleThreadDisplay(self,item): print(self.listThread.currentRow()) #if item.isSelected(): # print(item.text() + " is selected") #else: # print(item.text() + " is not selected") self.diagramView.showThread(self.listThread.currentRow(),item.isSelected()) def hideAllMsgNamedAsSelected(self): self.diagramView.hideAllMessageSelected() def addThreadList(self,threads): if not self.groupBoxThreadInfo: self.groupBoxThreadInfo = QGroupBox() self.threadInfo = QLabel("Thread Info.") self.groupBoxThreadInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") if not self.threadvbox: self.threadvbox = QVBoxLayout() if not self.listThread: self.listThread = QListWidget() self.listThread.setFixedWidth(200) self.listThread.setSelectionMode(QAbstractItemView.MultiSelection) QtCore.QObject.connect(self.listThread, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), self.toggleThreadDisplay) self.threadvbox.addWidget(self.threadInfo) self.threadvbox.addWidget(self.listThread) self.groupBoxThreadInfo.setLayout(self.threadvbox) self.addWidget(self.groupBoxThreadInfo) self.groupBoxThreadInfo.setSizePolicy(self.sizePolicy) for id in threads: item = QListWidgetItem(id) self.listThread.addItem(item) def connectController(self,controller): self.controller = controller self.connect(controller,QtCore.SIGNAL('setAvailable()'),self.setAvailable) def connectDiagramView(self,view): self.diagramView = view def disconnectMsgRcv(self,receiver): print("Implement this method !!! disconnectMsgRcv") def connectMsgRcv(self,receiver): self.msgRcv.append(receiver) def notifyHide(self): for rcv in self.msgRcv: rcv.activateHide(True) def showHiddenLifelines(self): response, selected_items = HiddenDialog.HiddenDialog.getSelectedItems(self.diagramView.getHiddenLifeLines()) if response: self.diagramView.showLifelines(selected_items) def showHiddenMessages(self): response, selected_items = HiddenMessageDialog.HiddenMessageDialog.getSelectedItems(self.diagramView.getHiddenMessages(),self.diagramView.getHiddenLifeLines()) if response: if selected_items[3] in self.diagramView.getHiddenLifeLines(): confirmation = ShowLifeLineDialog.ShowLifeLineDialog.confirmToShowLifeLine(selected_items[3]) if confirmation: self.diagramView.showLifelines([selected_items[3]]) self.diagramView.showMessages(selected_items) else: self.diagramView.showMessages(selected_items) def notifyCapture(self): for rcv in self.msgRcv: rcv.activateCapture(True) def moveToPrev(self): for rcv in self.msgRcv: rcv.moveToPrev() def moveToNext(self): for rcv in self.msgRcv: rcv.moveToNext() def searchMsg(self): str = self.editTextSearch.toPlainText() for rcv in self.msgRcv: rcv.searchMessage(str)
def __init__(self, model): super().__init__() self.setWindowTitle("OpenHRV") self.setWindowIcon(QIcon(":/logo.png")) self.setGeometry(50, 50, 1750, 850) self.model = model self.signals = ViewSignals() self.scanner = SensorScanner() self.scanner_thread = QThread(self) self.scanner.moveToThread(self.scanner_thread) self.scanner.mac_update.connect(self.model.set_mac_addresses) self.sensor = SensorClient() self.sensor_thread = QThread(self) self.sensor.moveToThread(self.sensor_thread) self.sensor.ibi_update.connect(self.model.set_ibis_buffer) self.sensor_thread.started.connect(self.sensor.run) self.redis_publisher = RedisPublisher() self.redis_publisher_thread = QThread(self) self.redis_publisher.moveToThread(self.redis_publisher_thread) self.model.ibis_buffer_update.connect(self.redis_publisher.publish) self.model.mean_hrv_update.connect(self.redis_publisher.publish) self.model.mac_addresses_update.connect(self.redis_publisher.publish) self.model.pacer_rate_update.connect(self.redis_publisher.publish) self.model.hrv_target_update.connect(self.redis_publisher.publish) self.model.biofeedback_update.connect(self.redis_publisher.publish) self.signals.annotation.connect(self.redis_publisher.publish) self.redis_publisher_thread.started.connect( self.redis_publisher.monitor.start) self.redis_logger = RedisLogger() self.redis_logger_thread = QThread(self) self.redis_logger.moveToThread(self.redis_logger_thread) self.redis_logger_thread.finished.connect( self.redis_logger.save_recording) self.redis_logger.recording_status.connect(self.show_recording_status) self.ibis_plot = pg.PlotWidget() self.ibis_plot.setBackground("w") self.ibis_plot.setLabel("left", "Inter-Beat-Interval (msec)", **{"font-size": "25px"}) self.ibis_plot.setLabel("bottom", "Seconds", **{"font-size": "25px"}) self.ibis_plot.showGrid(y=True) self.ibis_plot.setYRange(300, 1500, padding=0) self.ibis_plot.setMouseEnabled(x=False, y=False) self.ibis_signal = pg.PlotCurveItem() pen = pg.mkPen(color=(0, 191, 255), width=7.5) self.ibis_signal.setPen(pen) self.ibis_signal.setData(self.model.ibis_seconds, self.model.ibis_buffer) self.ibis_plot.addItem(self.ibis_signal) self.mean_hrv_plot = pg.PlotWidget() self.mean_hrv_plot.setBackground("w") self.mean_hrv_plot.setLabel("left", "HRV (msec)", **{"font-size": "25px"}) self.mean_hrv_plot.setLabel("bottom", "Seconds", **{"font-size": "25px"}) self.mean_hrv_plot.showGrid(y=True) self.mean_hrv_plot.setYRange(0, 600, padding=0) self.mean_hrv_plot.setMouseEnabled(x=False, y=False) colorgrad = QLinearGradient(0, 0, 0, 1) # horizontal gradient colorgrad.setCoordinateMode(QGradient.ObjectMode) colorgrad.setColorAt(0, pg.mkColor("g")) colorgrad.setColorAt(.5, pg.mkColor("y")) colorgrad.setColorAt(1, pg.mkColor("r")) brush = QBrush(colorgrad) self.mean_hrv_plot.getViewBox().setBackgroundColor(brush) self.mean_hrv_signal = pg.PlotCurveItem() pen = pg.mkPen(color="w", width=7.5) self.mean_hrv_signal.setPen(pen) self.mean_hrv_signal.setData(self.model.mean_hrv_seconds, self.model.mean_hrv_buffer) self.mean_hrv_plot.addItem(self.mean_hrv_signal) self.pacer_plot = pg.PlotWidget() self.pacer_plot.setBackground("w") self.pacer_plot.setAspectLocked(lock=True, ratio=1) self.pacer_plot.setMouseEnabled(x=False, y=False) self.pacer_plot.disableAutoRange() self.pacer_plot.setXRange(-1, 1, padding=0) self.pacer_plot.setYRange(-1, 1, padding=0) self.pacer_plot.hideAxis("left") self.pacer_plot.hideAxis("bottom") self.pacer_disc = pg.PlotCurveItem() brush = pg.mkBrush(color=(135, 206, 250)) self.pacer_disc.setBrush(brush) self.pacer_disc.setFillLevel(1) self.pacer_plot.addItem(self.pacer_disc) self.pacer_rate = QSlider(Qt.Horizontal) self.pacer_rate.setTracking(False) self.pacer_rate.setRange( 0, 6) # transformed to bpm [4, 7], step .5 by model self.pacer_rate.valueChanged.connect(self.model.set_breathing_rate) self.pacer_rate.setSliderPosition(4) # corresponds to 6 bpm self.pacer_label = QLabel(f"Rate: {self.model.breathing_rate}") self.pacer_toggle = QCheckBox("Show pacer", self) self.pacer_toggle.setChecked(True) self.pacer_toggle.stateChanged.connect(self.toggle_pacer) self.hrv_target_label = QLabel(f"Target: {self.model.hrv_target}") self.hrv_target = QSlider(Qt.Horizontal) self.hrv_target.setRange(50, 600) self.hrv_target.setSingleStep(10) self.hrv_target.valueChanged.connect(self.model.set_hrv_target) self.hrv_target.setSliderPosition(self.model.hrv_target) self.mean_hrv_plot.setYRange(0, self.model.hrv_target, padding=0) self.scan_button = QPushButton("Scan") self.scan_button.clicked.connect(self.scanner.scan) self.mac_menu = QComboBox() self.connect_button = QPushButton("Connect") self.connect_button.clicked.connect(self.connect_sensor) self.start_recording_button = QPushButton("Start") self.start_recording_button.clicked.connect( self.redis_logger.start_recording) self.save_recording_button = QPushButton("Save") self.save_recording_button.clicked.connect( self.redis_logger.save_recording) self.annotation = QComboBox() self.annotation.setEditable(True) self.annotation.setDuplicatesEnabled(False) self.annotation.addItems([ "start_baseline", "end_baseline", "start_bf", "end_bf", "start_nobf", "end_nobf" ]) self.annotation.setMaxCount( 10) # user can configure up to 4 additional custom annotations self.annotation_button = QPushButton("Annotate") self.annotation_button.clicked.connect(self.emit_annotation) self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.recording_status_label = QLabel("Status:") self.recording_statusbar = QProgressBar() self.recording_statusbar.setRange(0, 1) self.vlayout0 = QVBoxLayout(self.central_widget) self.hlayout0 = QHBoxLayout() self.hlayout0.addWidget(self.ibis_plot, stretch=80) self.hlayout0.addWidget(self.pacer_plot, stretch=20) self.vlayout0.addLayout(self.hlayout0) self.vlayout0.addWidget(self.mean_hrv_plot) self.hlayout1 = QHBoxLayout() self.device_config = QFormLayout() self.device_config.addRow(self.scan_button, self.mac_menu) self.device_config.addRow(self.connect_button) self.device_panel = QGroupBox("ECG Devices") self.device_panel.setLayout(self.device_config) self.hlayout1.addWidget(self.device_panel, stretch=25) self.hrv_config = QFormLayout() self.hrv_config.addRow(self.hrv_target_label, self.hrv_target) self.hrv_panel = QGroupBox("HRV Settings") self.hrv_panel.setLayout(self.hrv_config) self.hlayout1.addWidget(self.hrv_panel, stretch=25) self.pacer_config = QFormLayout() self.pacer_config.addRow(self.pacer_label, self.pacer_rate) self.pacer_config.addRow(self.pacer_toggle) self.pacer_panel = QGroupBox("Breathing Pacer") self.pacer_panel.setLayout(self.pacer_config) self.hlayout1.addWidget(self.pacer_panel, stretch=25) self.recording_config = QGridLayout() self.recording_config.addWidget(self.start_recording_button, 0, 0) self.recording_config.addWidget(self.save_recording_button, 0, 1) self.recording_config.addWidget(self.recording_statusbar, 0, 2) self.recording_config.addWidget(self.annotation, 1, 0, 1, 2) # row, column, rowspan, columnspan self.recording_config.addWidget(self.annotation_button, 1, 2) self.recording_panel = QGroupBox("Recording") self.recording_panel.setLayout(self.recording_config) self.hlayout1.addWidget(self.recording_panel, stretch=25) self.vlayout0.addLayout(self.hlayout1) self.model.ibis_buffer_update.connect(self.plot_ibis) self.model.mean_hrv_update.connect(self.plot_hrv) self.model.mac_addresses_update.connect(self.list_macs) self.model.pacer_disk_update.connect(self.plot_pacer_disk) self.model.pacer_rate_update.connect(self.update_pacer_label) self.model.hrv_target_update.connect(self.update_hrv_target) self.scanner_thread.start() self.sensor_thread.start() self.redis_publisher_thread.start() self.redis_logger_thread.start()