예제 #1
0
 def loading_message(self) -> QDialog:
     """Returns the message box to be displayed while the user waits for the input data to load."""
     message = QDialog(self)
     message.setWindowTitle('Loading')
     message.resize(300, 50)
     label = QLabel('loading DataFrame into memory...', message)
     label.setStyleSheet(self.style['label'])
     label.adjustSize()
     label.setAlignment(Qt.AlignCenter)
     label.move(int((message.width() - label.width()) / 2),
                int((message.height() - label.height()) / 2))
     return message
예제 #2
0
 def quit_message(self) -> QDialog:
     """Displays a window while SCOUTS is exiting"""
     message = QDialog(self)
     message.setWindowTitle('Exiting SCOUTS')
     message.resize(300, 50)
     label = QLabel('SCOUTS is exiting, please wait...', message)
     label.setStyleSheet(self.style['label'])
     label.adjustSize()
     label.setAlignment(Qt.AlignCenter)
     label.move(int((message.width() - label.width()) / 2),
                int((message.height() - label.height()) / 2))
     return message
예제 #3
0
class DeviceListDialog(QObject):
    show_tray_notification = Signal(str)
    management_action = Signal(
        str,  # action name
        str,  # action type
        str,  # node id
        bool)  # is_itself
    start_transfers = Signal()
    _update = Signal(list)

    def __init__(self,
                 parent=None,
                 initial_data=(),
                 disk_usage=0,
                 node_status=SS_STATUS_SYNCED,
                 node_substatus=None,
                 dp=1,
                 nodes_actions=(),
                 license_type=None):
        QObject.__init__(self)
        self._update.connect(self._update_data, Qt.QueuedConnection)
        self._dialog = QDialog(parent)
        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._ui = Ui_Dialog()
        self._ui.setupUi(self._dialog)
        self._dialog.setAttribute(Qt.WA_MacFrameworkScaled)
        self._ui.device_list_view.setFont(QFont('Nano', 10 * dp))
        self._license_type = license_type

        self._model = TableModel(disk_usage, node_status, node_substatus)
        QTimer.singleShot(100, lambda: self.update(initial_data))

        self._view = self._ui.device_list_view
        self._view.setModel(self._model)
        self._view.setSelectionMode(QAbstractItemView.NoSelection)

        self._ui.centralWidget.setFrameShape(QFrame.NoFrame)
        self._ui.centralWidget.setLineWidth(1)

        self._nodes_actions = nodes_actions

    def show(self, on_finished):
        def finished():
            self._dialog.finished.disconnect(finished)
            self._view.resizeRowsToContents()
            self._model.beginResetModel()
            on_finished()

        screen_width = QApplication.desktop().width()
        parent_x = self._dialog.parent().x()
        parent_width = self._dialog.parent().width()
        width = self._dialog.width()
        offset = 16
        if parent_x + parent_width / 2 > screen_width / 2:
            x = parent_x - width - offset
            if x < 0:
                x = 0
        else:
            x = parent_x + parent_width + offset
            diff = x + width - screen_width
            if diff > 0:
                x -= diff
        self._dialog.move(x, self._dialog.parent().y())
        if width > screen_width - offset:
            self._dialog.resize(screen_width - offset, self._dialog.height())

        self._view.setMouseTracking(True)
        self._old_mouse_move_event = self._view.mouseMoveEvent
        self._view.mouseMoveEvent = self._mouse_moved
        self._old_mouse_release_event = self._view.mouseMoveEvent
        self._view.mouseReleaseEvent = self._mouse_released

        logger.info("Opening device list dialog...")
        # Execute dialog
        self._dialog.finished.connect(finished)
        self._dialog.raise_()
        self._dialog.show()

    def update(self, nodes_info):
        self._update.emit(nodes_info)

    def _update_data(self, nodes_info):
        changed_nodes, deleted_nodes = self._model.update(nodes_info)
        for node_id in changed_nodes | deleted_nodes:
            self._nodes_actions.pop(node_id, None)
        self._view.resizeRowsToContents()

    def update_download_speed(self, value):
        self._model.update_node_download_speed(value)
        self._view.resizeRowsToContents()

    def update_upload_speed(self, value):
        self._model.update_node_upload_speed(value)
        self._view.resizeRowsToContents()

    def update_sync_dir_size(self, value):
        self._model.update_node_sync_dir_size(int(value))
        self._view.resizeRowsToContents()

    def update_node_status(self, value, substatus):
        self._model.update_node_status(value, substatus)
        self._view.resizeRowsToContents()

    def close(self):
        self._dialog.reject()

    def set_license_type(self, license_type):
        self._license_type = license_type

    def _mouse_moved(self, event):
        pos = event.pos()
        index = self._view.indexAt(pos)
        if index.isValid():
            if self._model.to_manage(index) and \
                    not self._pos_is_in_scrollbar_header(pos):
                self._view.setCursor(Qt.PointingHandCursor)
            else:
                self._view.setCursor(Qt.ArrowCursor)
        else:
            self._view.setCursor(Qt.ArrowCursor)
        self._old_mouse_move_event(event)

    def _mouse_released(self, event):
        pos = event.pos()
        index = self._view.indexAt(pos)
        if index.isValid():
            if self._model.to_manage(index) and \
                    not self._pos_is_in_scrollbar_header(pos):
                self._show_menu(index, pos)
        self._old_mouse_release_event(event)

    def _pos_is_in_scrollbar_header(self, pos):
        # mouse is not tracked as in view when in header or scrollbar area
        # so pretend we are there if we are near
        pos_in_header = pos.y() < 10
        if pos_in_header:
            return True

        scrollbars = self._view.findChildren(QScrollBar)
        if not scrollbars:
            return False

        pos_x = self._view.mapToGlobal(pos).x()
        for scrollbar in scrollbars:
            if not scrollbar.isVisible():
                continue

            scrollbar_x = scrollbar.mapToGlobal(QPoint(0, 0)).x()
            if scrollbar_x - 10 <= pos_x <= scrollbar_x + scrollbar.width():
                return True

        return False

    def _show_menu(self, index, pos):
        node_id, \
        node_name, \
        is_online, \
        is_itself, \
        is_wiped = self._model.get_node_id_online_itself(index)
        if not node_id:
            return

        license_free = self._license_type == FREE_LICENSE

        menu = QMenu(self._view)
        menu.setStyleSheet("background-color: #EFEFF4; ")
        menu.setToolTipsVisible(license_free)
        if license_free:
            menu.setStyleSheet(
                'QToolTip {{background-color: #222222; color: white;}}')
            menu.hovered.connect(lambda a: self._on_menu_hovered(a, menu))

        def add_menu_item(caption,
                          index=None,
                          action_name=None,
                          action_type="",
                          start_transfers=False,
                          disabled=False,
                          tooltip=""):
            action = menu.addAction(caption)
            action.setEnabled(not disabled)
            action.tooltip = tooltip if tooltip else ""
            if not start_transfers:
                action.triggered.connect(lambda: self._on_menu_clicked(
                    index, action_name, action_type))
            else:
                action.triggered.connect(self.start_transfers.emit)

        tooltip = tr("Not available for free license") \
            if license_free and not is_itself else ""
        if not is_online:
            action_in_progress = ("hideNode", "") in \
                                 self._nodes_actions.get(node_id, set())
            item_text = tr("Remove node") if not action_in_progress \
                else tr("Remove node in progress...")
            add_menu_item(item_text,
                          index,
                          "hideNode",
                          disabled=action_in_progress)
        elif is_itself:
            add_menu_item(tr("Transfers..."), start_transfers=True)
        if not is_wiped:
            wipe_in_progress = ("execute_remote_action", "wipe") in \
                                 self._nodes_actions.get(node_id, set())
            if not wipe_in_progress:
                action_in_progress = ("execute_remote_action", "logout") in \
                                     self._nodes_actions.get(node_id, set())
                item_text = tr("Log out") if not action_in_progress \
                    else tr("Log out in progress...")
                add_menu_item(item_text,
                              index,
                              "execute_remote_action",
                              "logout",
                              disabled=action_in_progress
                              or license_free and not is_itself,
                              tooltip=tooltip)
            item_text = tr("Log out && wipe") if not wipe_in_progress \
                else tr("Wipe in progress...")
            add_menu_item(item_text,
                          index,
                          "execute_remote_action",
                          "wipe",
                          disabled=wipe_in_progress
                          or license_free and not is_itself,
                          tooltip=tooltip)

        pos_to_show = QPoint(pos.x(), pos.y() + 20)
        menu.exec_(self._view.mapToGlobal(pos_to_show))

    def _on_menu_clicked(self, index, action_name, action_type):
        node_id, \
        node_name, \
        is_online, \
        is_itself, \
        is_wiped = self._model.get_node_id_online_itself(index)
        if action_name == "hideNode" and is_online:
            self.show_tray_notification.emit(
                tr("Action unavailable for online node"))
            return

        if (action_name == "hideNode" or action_type == "wipe"):
            if action_name == "hideNode":
                alert_str = tr(
                    '"{}" node will be removed '
                    'from list of devices. Files will not be wiped.'.format(
                        node_name))
            else:
                alert_str = tr(
                    'All files from "{}" node\'s '
                    'pvtbox secured folder will be wiped. '.format(node_name))
            if not self._user_confirmed_action(alert_str):
                return

        if not is_itself:
            self._nodes_actions[node_id].add((action_name, action_type))
        self.management_action.emit(action_name, action_type, node_id,
                                    is_itself)

    def _on_menu_hovered(self, action, menu):
        if not action.tooltip:
            return

        a_geometry = menu.actionGeometry(action)
        point = menu.mapToGlobal(
            QPoint(a_geometry.x() + 30,
                   a_geometry.y() + 5))
        QToolTip.showText(point, action.tooltip, menu, a_geometry,
                          60 * 60 * 1000)

    def _user_confirmed_action(self, alert_str):
        msg = tr("<b>Are</b> you <b>sure</b>?<br><br>{}".format(alert_str))
        userAnswer = msgbox(msg,
                            title=' ',
                            buttons=[
                                (tr('Cancel'), 'Cancel'),
                                (tr('Yes'), 'Yes'),
                            ],
                            parent=self._dialog,
                            default_index=0,
                            enable_close_button=True)

        return userAnswer == 'Yes'

    def on_management_action_in_progress(self, action_name, action_type,
                                         node_id):
        self._nodes_actions[node_id].add((action_name, action_type))