Пример #1
0
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)
Пример #2
0
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
Пример #3
0
    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)
Пример #5
0
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()
Пример #6
0
    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()
Пример #7
0
    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()
Пример #8
0
    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()
Пример #9
0
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()
Пример #10
0
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()