class UpdateScreen(Screen): """Screen to display message that image update is available.""" goto_next = Signal() update_text = ( "<b>Update available</b><br>" "An update to the notebook server is available. Updating the " "notebook server will allow continued use of the most recent " "EPI2ME Labs notebooks on GitHub. Please press the Update " "button on the main screen to update.<br><br>" "Current version: {}.<br>" "Latest version: {}.") def __init__(self, parent=None): """Initialize the screen.""" super().__init__(parent=parent) self.layout = QVBoxLayout() self.update_lbl = QLabel() self.layout.addWidget(self.update_lbl) self.layout.insertStretch(-1) self.l0 = QHBoxLayout() self.layout.insertStretch(-1) self.dismiss_btn = QPushButton("OK") self.dismiss_btn.clicked.connect(self.goto_next.emit) self.l0.addWidget(self.dismiss_btn) self.layout.addLayout(self.l0) self.setLayout(self.layout)
class PropertyTree(QWidget): def __init__(self, data, parent=None): super(PropertyTree, self).__init__(parent) self.tcam = data.tcam self.data = data self.setup_ui() self.property_number = 0 self.prop_dict = {} def setup_ui(self): self.layout = QVBoxLayout() self.setLayout(self.layout) def finish_setup(self): # insert spacer only after all other elements have been added self.layout.insertStretch(-1, 1) def add_property(self, prop: Prop): wid = PropertyWidget(self.data, prop) self.layout.addWidget(wid) self.prop_dict[prop.name] = wid self.property_number = self.property_number + 1 def set_property(self, name, value): return self.tcam.set_tcam_property(name, value) def get_property_count(self): return self.property_number def update(self, prop): try: self.prop_dict[prop.name].update(prop) except KeyError as e: self.prop_dict[prop.name] = PropertyWidget(self.data, prop) self.layout.addWidget(self.prop_dict[prop.name]) self.property_number = self.property_number + 1
def initUI(self): layout = QVBoxLayout() ### TIMER LABEL ### self.timer_label = QLabel('', self) self.timer_label.setStyleSheet('font-size: 50px;') self.timer_label.setAlignment(Qt.AlignCenter) ################### ### INSTRUCTIONS LABELS ### instructions_label = QLabel( 'Press "Esc" to skip break or ' + '"F1" to add 5 minutes', self) instructions_label.setStyleSheet('font-size: 30px;') ########################### layout.insertStretch(0, stretch=3) layout.addWidget(self.timer_label) layout.insertStretch(2, stretch=1) layout.addWidget(instructions_label) layout.insertStretch(4, stretch=4) layout.setAlignment(Qt.AlignCenter) self.setLayout(layout) ''' WindowStaysOnTopHint for always on top, WindowTransparentForInput makes QWidget frameless, FramelessWindowHint makes transparent for input ''' flags = self.windowFlags() flags |= Qt.WindowStaysOnTopHint flags |= Qt.WindowTransparentForInput flags |= Qt.FramelessWindowHint if platform.system() == 'Linux': flags |= Qt.Tool self.setWindowFlags(flags) # adjust transparency (opacity): self.setWindowOpacity(0.5) # full screen: self.resize(QApplication.desktop().size()) # self.setWindowState(Qt.WindowFullScreen) self.setStyleSheet('background-color: black;' + 'color: white;')
def build_interface(self): # Hide starting widget self._w_start.hide() # Reserve a place to display input and output images pix = QPixmap(2 * self.image_size, 2 * self.image_size) pix.fill(QColor(0, 0, 0)) # Left sidebar l_left = QVBoxLayout() self._l_start.deleteLater() self._l_main.addLayout(l_left) b_iimgl = [ QPushButton('Load first image', self), QPushButton('Load second image', self) ] b_iimgrz = [ QPushButton('Sample random first z', self), QPushButton('Sample random second z', self) ] b_iimgri = [ QPushButton('Sample random first image', self), QPushButton('Sample random second image', self) ] imgl_slots = [partial(self.load_image, 0), partial(self.load_image, 1)] imgrz_slots = [ partial(self.sample_random_z, 0), partial(self.sample_random_z, 1) ] imgri_slots = [ partial(self.sample_random_image, 0), partial(self.sample_random_image, 1) ] for i in range(2): self._l_iimg[i].setPixmap(pix) self._l_oimg[i].setPixmap(pix) l_left.addWidget(b_iimgl[i]) l_left.addWidget(b_iimgrz[i]) l_left.addWidget(b_iimgri[i]) l = QHBoxLayout() l.addWidget(self._l_iimg[i]) l.addWidget(self._l_oimg[i]) b_iimgl[i].clicked.connect(imgl_slots[i]) b_iimgrz[i].clicked.connect(imgrz_slots[i]) b_iimgri[i].clicked.connect(imgri_slots[i]) l_left.addLayout(l) # Middle layout l_mid = QVBoxLayout() self._l_main.addLayout(l_mid) l = QHBoxLayout() l_mid.addLayout(l) b_run_d = QPushButton('Run decoder') b_run_d.clicked.connect(self.run_decoder) l.addWidget(b_run_d) b_run_a = QPushButton('Run animation') b_run_a.clicked.connect(self.run_animation) l.addWidget(b_run_a) l_mid.insertStretch(-1) # Build z sliders l = QHBoxLayout() l_mid.addLayout(l) for i in range(self.z_dim): if not i % 25: lv = QVBoxLayout() l.addLayout(lv) l.insertStretch(-1) h_l = QHBoxLayout() lv.addLayout(h_l) l_z1 = QLabel('Z: %d, -3' % i) l_z2 = QLabel('Z: %d, 3' % i) s_z = QSlider(Qt.Horizontal) s_z.setMinimum(-3000) s_z.setMaximum(3000) s_z.valueChanged.connect(self.get_sliders) self.z_sliders.append(s_z) h_l.addWidget(l_z1) h_l.addWidget(s_z) h_l.addWidget(l_z2) # Build y checkboxes if self.y_dim is not None: for i in range(self.y_dim): if not i % 20: lv = QVBoxLayout() l.addLayout(lv) l.insertStretch(-1) c_y = QCheckBox() c_y.setText('y %d' % i) c_y.stateChanged.connect(self.get_y) lv.addWidget(c_y) self.y_checks.append(c_y) l.insertStretch(-1) # Right sidebar l_right = QVBoxLayout() self._l_main.addLayout(l_right) self._l_anim.setPixmap(pix) l_right.addWidget(QLabel('Animation', self)) l_right.addWidget(self._l_anim) l_right.insertStretch(-1)
class MainWindow(QMainWindow): def __init__(self, client_socket, process_events_method): super().__init__() self.username = config.user["username"] self.client_socket = client_socket self.client_socket.recv_message.connect(self.recvMessage) self.process_events_method = process_events_method self.chats = {} self.send_on_enter = True self.initMenubar() self.initUI() for chat in config.chats: self.createChat(chat, config.chats[chat]["participants"]) def initMenubar(self): self.createChatAction = QAction("&Create Chat", self) #self.exitAction.setShortcut("Ctrl+Q") self.createChatAction.triggered.connect(self.createChat) self.addFriendAction = QAction("&Add Friend", self) self.addFriendAction.triggered.connect(self.addFriend) self.menubar = self.menuBar() self.chatMenu = self.menubar.addMenu("&Chat") self.chatMenu.addAction(self.createChatAction) self.friendMenu = self.menubar.addMenu("&Friend") self.friendMenu.addAction(self.addFriendAction) def initUI(self): self.content = QWidget() self.hbox = QHBoxLayout(self.content) self.setCentralWidget(self.content) self.friend_list = QListWidget() self.friend_list.itemClicked.connect(self.friendClicked) self.message_scroll = QScrollArea() self.message_scroll.setWidgetResizable(True) #TODO have a setting to disable this self.message_scroll.verticalScrollBar().rangeChanged.connect(self.scrollBottom) self.message_input = MessageInput() self.message_input.sendMessage.connect(self.sendMessage) self.message_split = QSplitter(Qt.Vertical) self.message_split.addWidget(self.message_scroll) self.message_split.addWidget(self.message_input) self.main_split = QSplitter(Qt.Horizontal) self.main_split.addWidget(self.friend_list) self.main_split.addWidget(self.message_split) self.hbox.addWidget(self.main_split) self.show() def addFriend(self, username=None): if type(username) is bool: add_friend_dialog = AddFriendDialog(self.client_socket) add_friend_dialog.exec_() if add_friend_dialog.selected_user == None: return username = add_friend_dialog.selected_user self.chats[username] = {"participants":[username], "messages":[]} #TODO we should probably sanatize these to prevent directory manipulation friend = QListWidgetItem(QIcon(config.ICON_DIR + username + ".png"), username) self.friend_list.addItem(friend) def createChat(self, chat_name=None, participants=None): if type(chat_name) is bool: create_chat_dialog = CreateChatDialog() create_chat_dialog.exec_() if not create_chat_dialog.created_chat: return chat_name = create_chat_dialog.chat_name participants = create_chat_dialog.participants self.chats[chat_name] = {"participants":participants, "messages":[]} self.friend_list.addItem(QListWidgetItem(chat_name)) def friendClicked(self, item): self.loadMessages(str(item.text())) def loadMessages(self, chat): #self.clearMessages() #TODO make the message history look pretty #TODO consider storing a message history for each chat and switch between when needed #TODO create a chat class and store the chat name as well as the participants there #TODO index message histories by chat name self.message_history = QVBoxLayout() self.message_history.setSpacing(0) self.message_history.setContentsMargins(0,0,0,0) self.message_history.insertStretch(-1, 1) self.message_history_container = QWidget() self.message_history_container.setLayout(self.message_history) self.message_scroll.setWidget(self.message_history_container) for message in self.chats[chat]["messages"]: self.drawMessage(message) def clearMessages(self): while not self.message_history.isEmpty(): self.message_history.takeAt(0) def sendMessage(self): message = self.message_input.toPlainText().strip() chat = self.friend_list.selectedItems()[0].text() self.client_socket.sendMessage(message, chat, self.chats[chat]["participants"]) print("Sending: %s to %s" % (message, chat)) self.message_input.setText("") self.recvMessage(chat, {"sender":self.username, "message":message}) def recvMessage(self, chat, message): self.chats[chat]["messages"].append(message) current_chat = self.friend_list.selectedItems()[0].text() if current_chat == chat: self.drawMessage(message) def drawMessage(self, message): #TODO add a timestamp to messages new_message = QLabel(message["sender"] + ':' + message["message"]) self.message_history.addWidget(new_message) def scrollBottom(self): self.message_scroll.verticalScrollBar().setValue(self.message_scroll.verticalScrollBar().maximum()) def disconnect(self): self.client_socket.disconnect()
def initUI(self): self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.receivedTextEdit = QTextEdit() self.receivedTextEdit.setAcceptRichText(False) monospaceFont = QFont("Monospace") monospaceFont.setStyleHint(QFont.Monospace) self.receivedTextEdit.setFont(monospaceFont) playExerciseButton = QPushButton("Play exercise text") playExerciseButton.clicked.connect(self.playExercise) stopButton = QPushButton("Stop playing") stopButton.clicked.connect(self.stopPlaying) validateButton = QPushButton("Check input / Generate next exercise") validateButton.clicked.connect(self.checkInput) self.wpmLineEdit = QLineEdit(self.settings.value("wpm")) self.wpmLineEdit.textChanged.connect(functools.partial(self.saveChangedText, self.wpmLineEdit, "wpm")) wpmLabel = QLabel("WPM") self.ewpmLineEdit = QLineEdit(self.settings.value("effectiveWpm")) self.ewpmLineEdit.textChanged.connect(functools.partial(self.saveChangedText, self.ewpmLineEdit, "effectiveWpm")) ewpmLabel = QLabel("effective WPM") self.freqLineEdit = QLineEdit(self.settings.value("frequency")) self.freqLineEdit.textChanged.connect(functools.partial(self.saveChangedText, self.freqLineEdit, "frequency")) freqLabel = QLabel("Frequency (Hz)") self.durationLineEdit = QLineEdit(self.settings.value("duration")) self.durationLineEdit.textChanged.connect(functools.partial(self.saveChangedText, self.durationLineEdit, "duration")) durationLabel = QLabel("Duration (min)") self.lessonGrid = QGridLayout() lessonCombo = QComboBox() lessonCombo.setStyleSheet("combobox-popup: 0;") lessonCombo.addItem("1 - K M") for lesson in range(2, len(KOCH_LETTERS)): lessonCombo.addItem(str(lesson) + " - " + KOCH_LETTERS[lesson]) lessonCombo.setCurrentIndex(int(self.settings.value("currentLesson"))-1) lessonCombo.currentIndexChanged.connect(self.newLessonSelected) lessonIdLabel = QLabel("Lesson:") lessonBox = QHBoxLayout() lessonBox.addWidget(lessonIdLabel) lessonBox.addWidget(lessonCombo) lessonBox.addStretch(-1) self.createLessonLetterButtons(self.lessonGrid) mainLayout = QVBoxLayout() inputAndParameters = QHBoxLayout() parameterField = QVBoxLayout() inputAndParameters.addWidget(self.receivedTextEdit, stretch=1) self.receivedTextEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding)) inputAndParameters.addLayout(parameterField, stretch=0) parameterField.addWidget(playExerciseButton) parameterField.addWidget(stopButton) parameterField.addWidget(validateButton) parameterGrid = QGridLayout() parameterGrid.addWidget(self.wpmLineEdit, 0, 0) parameterGrid.addWidget(wpmLabel, 0, 1) parameterGrid.addWidget(self.ewpmLineEdit, 1, 0) parameterGrid.addWidget(ewpmLabel, 1, 1) parameterGrid.addWidget(self.freqLineEdit, 2, 0) parameterGrid.addWidget(freqLabel, 2, 1) parameterGrid.addWidget(self.durationLineEdit, 3, 0) parameterGrid.addWidget(durationLabel, 3, 1) parameterField.addLayout(parameterGrid) parameterField.insertSpacing(-1, 15) parameterField.addLayout(lessonBox) parameterField.insertStretch(-1) mainLayout.addLayout(inputAndParameters) mainLayout.addLayout(self.lessonGrid) self.centralWidget.setLayout(mainLayout) self.setWindowTitle('PyMorsetrainer') self.show()
def initUI(self): self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.receivedTextEdit = QTextEdit() self.receivedTextEdit.setAcceptRichText(False) monospaceFont = QFont("Monospace") monospaceFont.setStyleHint(QFont.Monospace) self.receivedTextEdit.setFont(monospaceFont) playExerciseButton = QPushButton("Play exercise text") playExerciseButton.clicked.connect(self.playExercise) stopButton = QPushButton("Stop playing") stopButton.clicked.connect(self.stopPlaying) validateButton = QPushButton("Check input / Generate next exercise") validateButton.clicked.connect(self.checkInput) self.wpmLineEdit = QLineEdit(self.settings.value("wpm")) self.wpmLineEdit.textChanged.connect( functools.partial(self.saveChangedText, self.wpmLineEdit, "wpm")) wpmLabel = QLabel("WPM") self.ewpmLineEdit = QLineEdit(self.settings.value("effectiveWpm")) self.ewpmLineEdit.textChanged.connect( functools.partial(self.saveChangedText, self.ewpmLineEdit, "effectiveWpm")) ewpmLabel = QLabel("effective WPM") self.freqLineEdit = QLineEdit(self.settings.value("frequency")) self.freqLineEdit.textChanged.connect( functools.partial(self.saveChangedText, self.freqLineEdit, "frequency")) freqLabel = QLabel("Frequency (Hz)") self.durationLineEdit = QLineEdit(self.settings.value("duration")) self.durationLineEdit.textChanged.connect( functools.partial(self.saveChangedText, self.durationLineEdit, "duration")) durationLabel = QLabel("Duration (min)") self.lessonGrid = QGridLayout() lessonCombo = QComboBox() lessonCombo.setStyleSheet("combobox-popup: 0;") lessonCombo.addItem("1 - K M") for lesson in range(2, len(KOCH_LETTERS)): lessonCombo.addItem(str(lesson) + " - " + KOCH_LETTERS[lesson]) lessonCombo.setCurrentIndex( int(self.settings.value("currentLesson")) - 1) lessonCombo.currentIndexChanged.connect(self.newLessonSelected) lessonIdLabel = QLabel("Lesson:") lessonBox = QHBoxLayout() lessonBox.addWidget(lessonIdLabel) lessonBox.addWidget(lessonCombo) lessonBox.addStretch(-1) self.createLessonLetterButtons(self.lessonGrid) mainLayout = QVBoxLayout() inputAndParameters = QHBoxLayout() parameterField = QVBoxLayout() inputAndParameters.addWidget(self.receivedTextEdit, stretch=1) self.receivedTextEdit.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding)) inputAndParameters.addLayout(parameterField, stretch=0) parameterField.addWidget(playExerciseButton) parameterField.addWidget(stopButton) parameterField.addWidget(validateButton) parameterGrid = QGridLayout() parameterGrid.addWidget(self.wpmLineEdit, 0, 0) parameterGrid.addWidget(wpmLabel, 0, 1) parameterGrid.addWidget(self.ewpmLineEdit, 1, 0) parameterGrid.addWidget(ewpmLabel, 1, 1) parameterGrid.addWidget(self.freqLineEdit, 2, 0) parameterGrid.addWidget(freqLabel, 2, 1) parameterGrid.addWidget(self.durationLineEdit, 3, 0) parameterGrid.addWidget(durationLabel, 3, 1) parameterField.addLayout(parameterGrid) parameterField.insertSpacing(-1, 15) parameterField.addLayout(lessonBox) parameterField.insertStretch(-1) mainLayout.addLayout(inputAndParameters) mainLayout.addLayout(self.lessonGrid) self.centralWidget.setLayout(mainLayout) self.setWindowTitle('PyMorsetrainer') self.show()
def __init__(self): # Call supercalss constructor super().__init__() # Initialize backend self.decision_maker = DecisionMaker(DEFAULT_DB_PATH) # These parameter values get adjusted by the front panel controls self.day = 0 self.year = 0 self.time = 0 self.weather = 0 self.alone = 0 self.retrograde = 0 self.hunger_level = 0 self.thirst_level = 0 self.energy_level = 0 self.introvert_level = 0 self.stress_level = 0 # Status bar #self.statusBar().showMessage('Ready') # Menu bar #menu_bar = self.menuBar() #file_menu = menu_bar.addMenu('&File') # Set up layout vbox = QVBoxLayout() title = DecisionMakerTitle() controls = DecisionMakerControls(self) output = DecisionMakerOutput(self) vbox.addWidget(title) vbox.addWidget(controls) vbox.addWidget(output) vbox.insertStretch(1) vbox.insertStretch(3) vbox.setSpacing(10) vbox.addSpacing(10) hbox = QHBoxLayout() hbox.addLayout(vbox) hbox.insertStretch(0) central = QWidget(self) self.setCentralWidget(central) central.setLayout(hbox) # Color palette self.setAutoFillBackground(True) palette = self.palette() palette.setColor(self.backgroundRole(), Qt.black) self.setPalette(palette) # Window properties #self.setGeometry(300, 600, 300, 220) #window_geom = self.frameGeometry() #center_pos = QDesktopWidget().availableGeometry().center() #window_geom.moveCenter(center_pos) #self.move(window_geom.topLeft()) self.setWindowTitle('DecisionMaker') self.setWindowIcon(QIcon(ICON_PATH)) # Show window self.show()
class SettingsDlg(QDialog): """About dialog.""" def __init__(self, settings, parent=None): """Initialize the dialog. :param version: application version string. """ super().__init__(parent) self.logger = self.parent().logger self.settings = settings self.setWindowTitle("Settings") self.setFixedSize(600, 400) self.layout = QVBoxLayout() # Text boxes for setting values self.val_boxes = dict() self.l0 = QGridLayout() for row, setting in enumerate(self._mutable_settings): key = setting['key'] value = self.settings[key] lab = QLabel(key) wid = None if setting['type'] is str: wid = QLineEdit(text=value) if key == 'registry': wid.setEnabled(False) elif setting['type'] is bool: wid = QCheckBox() wid.setChecked(value) elif issubclass(setting['type'], Enum): wid = QComboBox() for flavour in setting['type']: wid.addItem(flavour.name, flavour.value) wid.setCurrentText(value.name) else: raise TypeError("Unhandled type in settings dialog.") wid.setToolTip(setting['desc']) self.val_boxes[key] = wid self.l0.addWidget(lab, row, 0) self.l0.addWidget(wid, row, 1) self.layout.addLayout(self.l0) # OK / Cancel self.l1 = QHBoxLayout() self.layout.insertStretch(-1) self.set_btn = QPushButton("OK") self.set_btn.clicked.connect(self.store_settings) self.l1.addWidget(self.set_btn) self.set_btn = QPushButton("Defaults") self.set_btn.clicked.connect(self.set_defaults) self.l1.addWidget(self.set_btn) self.cancel_btn = QPushButton("Cancel") self.cancel_btn.clicked.connect(self.close) self.l1.addWidget(self.cancel_btn) self.layout.addLayout(self.l1) self.setLayout(self.layout) @property def _mutable_settings(self): return (x for x in self.settings.spec if x['gui_menu']) def store_settings(self): """Save settings in edit fields to Qt settings manager.""" self.logger.info("Saving configuration.") for key, wid in self.val_boxes.items(): value = None if isinstance(wid, QLineEdit): value = wid.text() elif isinstance(wid, QCheckBox): value = wid.isChecked() elif isinstance(wid, QComboBox): value = wid.currentData() else: raise TypeError("Unhandled widget type when setting item.") self.settings[key] = value self.settings.qsettings.sync() msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText("Restart Application") msg.setInformativeText( "Please restart the Launcher application for settings " "to take effect.") msg.setWindowTitle("Restart Notice") msg.exec_() self.close() def set_defaults(self): """Set edit fields to default values. ..note:: This does not store the values to Qt. """ self.logger.info("Preparing default configuration.") for key, wid in self.val_boxes.items(): value = self.settings.spec.by_key[key]['default'] if isinstance(wid, QLineEdit): wid.setText(value) elif isinstance(wid, QCheckBox): wid.setChecked(value) elif isinstance(wid, QComboBox): wid.setCurrentText(value.name) else: raise TypeError("Unhandled widget type when setting default.") wid.repaint()
class StartScreen(Screen): """Screen to set options and start server.""" goto_home = Signal() path_help = ("The data path is the location on your computer you\n" "wish to be readable (and writeable) within the notebook\n" "environment. It will be available in the environment under\n" "`/epi2melabs`.") token_help = ( "A secret token used to connect to the notebook server. Anyone\n" "with this token will be able to connect to the server, and \n" "therefore modify files under the data location. We recommend\n" "changing this from the default value.") port_help = ( "The network port to used to communicate between web-browser\n" "and notebook server.") aux_port_help = ( "An auxialiary network port used for secondary applications\n" "e.g. for use by the Pavian metagenomics dataset explorer.") def __init__(self, parent=None): """Initialize the screen.""" super().__init__(parent=parent) self.token_policy = PasswordPolicy.from_names(length=8, uppercase=1, numbers=1) self.onlyInt = QIntValidator() self.layout = QVBoxLayout() # header self.l0 = QHBoxLayout() self.header_lbl = QLabel("Start server") self.l0.addWidget(self.header_lbl) self.layout.addLayout(self.l0) # data path, token, port self.l1 = QGridLayout() self.path_btn = QPushButton('Select folder') self.path_btn.clicked.connect(self.select_path) self.path_txt = QLineEdit(text=self.app.settings['data_mount']) self.path_txt.setToolTip(self.path_help) self.path_txt.setReadOnly(True) self.l1.addWidget(self.path_btn, 0, 0) self.l1.addWidget(self.path_txt, 0, 1) self.token_lbl = QLabel('Token:') self.token_txt = QLineEdit(text=self.app.settings['token']) self.token_txt.textChanged.connect(self.token_change) self.token_txt.setMaxLength(16) self.token_txt.setToolTip(self.token_help) self.port_lbl = QLabel('Port:') self.port_txt = QLineEdit(text=str(self.app.settings['port'])) self.port_txt.textChanged.connect(self.port_change) self.port_txt.setValidator(self.onlyInt) self.port_txt.setToolTip(self.port_help) self.aux_port_lbl = QLabel('Aux. Port:') self.aux_port_txt = QLineEdit(text=str(self.app.settings['aux_port'])) self.aux_port_txt.textChanged.connect(self.aux_port_change) self.aux_port_txt.setValidator(self.onlyInt) self.aux_port_txt.setToolTip(self.aux_port_help) self.l1.addWidget(self.token_lbl, 1, 0) self.l1.addWidget(self.token_txt, 1, 1) self.l1.addWidget(self.port_lbl, 2, 0) self.l1.addWidget(self.port_txt, 2, 1) self.l1.addWidget(self.aux_port_lbl, 3, 0) self.l1.addWidget(self.aux_port_txt, 3, 1) self.layout.addLayout(self.l1) # spacer self.layout.insertStretch(-1) # start, update, back self.l3 = QHBoxLayout() self.start_btn = QPushButton('Start') self.start_btn.clicked.connect(self.validate_and_start) self.update_btn = QPushButton('Update') self.update_btn.clicked.connect(self.pull_image) self.back_btn = QPushButton('Back') self.back_btn.clicked.connect(self.goto_home.emit) self.l3.addWidget(self.start_btn) self.l3.addWidget(self.update_btn) self.l3.addWidget(self.back_btn) self.layout.addLayout(self.l3) self.setLayout(self.layout) self.app.docker.status.changed.connect(self.on_status) self.on_status(self.app.docker.status.value) def select_path(self): """Open data path dialog and set state.""" starting_dir = self.path_txt.text() path = QFileDialog.getExistingDirectory(None, 'Open working directory', starting_dir, QFileDialog.ShowDirsOnly) if path != "": # did not press cancel self.path_txt.setText(path) self.app.settings["data_mount"] = path def token_change(self): """Set state when user changes token.""" self.app.settings["token"] = self.token_txt.text() self.app.home.set_welcome_lbl_text() def port_change(self): """Set state when user changes port.""" self.app.settings["port"] = self.port_txt.text() self.app.home.set_welcome_lbl_text() def aux_port_change(self): """Set state when user changes auxilary port.""" self.app.settings["aux_port"] = self.aux_port_txt.text() self.app.home.set_welcome_lbl_text() def validate_and_start(self): """Start the container.""" mount = self.app.settings["data_mount"] token = self.app.settings["token"] port = self.app.settings["port"] aux_port = self.app.settings["aux_port"] # validate inputs valid = all([ mount != "", os.path.isdir(mount), len(self.token_policy.test(token)) == 0, self.port_txt.hasAcceptableInput() and int(port) > 1024, self.aux_port_txt.hasAcceptableInput() and int(aux_port) > 1024, port != aux_port ]) if valid: if (self.app.docker.latest_available_tag is None or self.app.settings["fixed_tag"] == "dev"): self.pull_image(callback=self._start_container) else: self._start_container() else: self.logger.warning("Container start options were invalid.") msg = QMessageBox(self) msg.setIcon(QMessageBox.Information) msg.setText("Input error") msg.setInformativeText("The inputs given are invalid.") msg.setWindowTitle("Input error") msg.setDetailedText( "1. A valid folder must be given.\n" "2. The token must be 8 characters and include uppercase, " "lowercase and numbers.\n" "3. The ports must be >1024.\n" "4. Port and Aux. port must be distinct.") msg.exec_() def _start_container(self): """Start container.""" mount = self.app.settings["data_mount"] token = self.app.settings["token"] port = self.app.settings["port"] aux_port = self.app.settings["aux_port"] for btn in (self.start_btn, self.update_btn): btn.setEnabled(False) self.app.docker.start_container(mount, token, port, aux_port) self.repaint() if self.app.docker.status.value[1] != "running": self.logger.error("Failed to start container.") else: self.logger.info("Container started, writing config to mount.") config = configparser.ConfigParser() config['Host'] = { 'hostname': socket.gethostname(), 'operating_system': platform.platform() } config['Container'] = { 'mount': mount, 'port': port, 'aux_port': aux_port, 'image_tag': self.app.docker.latest_available_tag, 'latest_tag': str(self.app.docker.latest_tag), 'id': self.app.docker.container.id } config['Pings'] = {'enabled': self.app.settings["send_pings"]} fname = os.path.join(mount, os.path.basename(ping.CONTAINER_META)) with open(fname, 'w') as config_file: config.write(config_file) self.logger.info("Container started and primed.") def pull_image(self, *args, callback=None): """Pull new image in a thread. :param callback: function to run when pull as completed. """ self.logger.info("Starting thread to pull image.") self.worker = Worker(self.app.docker.pull_image) self.worker.setAutoDelete(True) self.app.closing.connect(self.worker.stop) self.worker.signals.finished.connect( lambda: self.update_btn.setEnabled(self.app.docker.update_available )) if callback is not None: self.worker.signals.finished.connect(callback) self.progress_dlg = DownloadDialog( progress=self.worker.signals.progress, parent=self) self.progress_dlg.finished.connect(self.worker.stop) self.worker.signals.finished.connect(self.progress_dlg.close) self.app.pool.start(self.worker) self.progress_dlg.show() @Slot(float) def on_download(self, value): """Set state when download progress changes.""" self.header_lbl.setText( "Start server: (downloading - {:.1f}%)".format(value)) @Slot(object) def on_status(self, status): """Set state when container status changes.""" old, new = status msg = "" start_text = "Start" if new == "inactive": pass elif new in ("created", "exited"): msg = " (last attempt failed)" start_text = "Restart" elif new == "running": start_text = "Restart" self.app.show_home() self.start_btn.setText(start_text) self.start_btn.setEnabled(new != "unknown") self.header_lbl.setText('Start server: {}'.format(msg)) self.update_btn.setEnabled(self.app.docker.update_available and new != "unknown") self.repaint()