Beispiel #1
0
class CameraIcon(QObject):
    formats = []
    ui_controls = {}
    ui_format = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.v4l2_ctl = V4l2Ctl()
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.app.setApplicationName(APP_NAME)
        with open(LINUX_CSS, 'r') as css_file:
            self.app.setStyleSheet(css_file.read())
        self.create_icon()
        self.create_menu()
        self.run()

    def create_icon(self):
        self.icon = QSystemTrayIcon(QIcon(ICON_PATH))
        self.icon.activated.connect(self.activate)

    def create_menu(self):
        self.menu = QMenu()
        self.action = Action(self.menu)
        self.menu.addAction(self.action)

    def activate_exit(self, *args):
        os._exit(0)
        return True

    def on_format_value_changed(self, name, value):
        pix, res, fps = self.formats[value]
        self.v4l2_ctl.set_format(pix, res, fps)

    def on_format_change_finished(self):
        self.current_format = self.v4l2_ctl.get_format()
        self.update_format()

    def on_control_value_changed(self, name, value):
        self.v4l2_ctl.set_control(name, value)

    def on_control_change_finished(self):
        self.controls_data = self.v4l2_ctl.read_controls()
        self.update_controls()

    def on_export(self):
        file_name, _ = QFileDialog.getSaveFileName()
        if file_name:
            script = self.v4l2_ctl.export_script()
            with open(filename, 'w') as f:
                f.write(script)

    def append_widget_to_layout(self, widget):
        layout = self.action.layout
        layout.rowCount()
        layout.addWidget(widget, layout.rowCount(), 0, 1, 2)

    def add_export(self):
        button = QPushButton('Export settings script')
        button.clicked.connect(self.on_export)
        self.append_widget_to_layout(button)

    def add_quit(self):
        button = QPushButton('Exit')
        button.clicked.connect(lambda: os._exit(0))
        self.append_widget_to_layout(button)

    def add_format(self):
        if len(self.formats):
            formats = {
                str(k): f'{p} {r} {f}'
                for k, [p, r, f] in enumerate(self.formats)
            }
            self.ui_format = Dropdown(formats, 'formats')
            self.ui_format.valueChanged.connect(self.on_format_value_changed)
            self.ui_format.changeFinished.connect(
                self.on_format_change_finished)
            self.append_widget_to_layout(self.ui_format)
        else:
            self.ui_format = None

    def add_controls(self):
        self.ui_controls = {}
        layout = self.action.layout
        for row, [k, v] in enumerate(self.controls_data.items()):
            label = QLabel(k)
            layout.addWidget(label, row, 0, 1, 1, Qt.AlignRight)

            ctl_type = v[0]
            params = v[1]
            control = None
            if ctl_type == 'bool':
                control = CheckBox(k)
            elif ctl_type == 'int':
                control = Slider(
                    int(params['min']),
                    int(params['max']),
                    int(params['step']),
                    k,
                )
            elif ctl_type == 'menu':
                control = Dropdown(v[2], k)

            if control is None:
                continue

            control.valueChanged.connect(self.on_control_value_changed)
            control.changeFinished.connect(self.on_control_change_finished)
            add = getattr(
                self.action.layout,
                'addWidget' if control.isWidgetType() else 'addLayout')
            add(control, row, 1)
            self.ui_controls[k] = control

    def readd_widgets(self):
        clear_layout(self.action.layout)
        self.add_controls()
        self.add_format()
        self.add_export()
        self.add_quit()

    def update_controls(self):
        for k, v in self.ui_controls.items():
            data = self.controls_data[k][1]
            v.set_value(int(data['value']))
            disabled = 'flags' in data and data['flags'] == 'inactive'
            v.setDisabled(disabled)

    def update_format(self):
        if self.current_format != [None, None, None]:
            self.ui_format.set_value(self.formats.index(self.current_format))

    def update_menu_size(self):
        self.action.layout.invalidate()
        self.action.layout.activate()
        hint = self.action.widget.sizeHint()
        width = hint.width() + 20
        height = min(MENU_HEIGHT, hint.height())
        self.action.scroll.setFixedWidth(width)
        self.action.scroll.setFixedHeight(height)
        self.menu.setFixedWidth(width)
        self.menu.setFixedHeight(height)
        self.menu.setAttribute(Qt.WA_DontShowOnScreen, True)
        self.menu.show()
        self.menu.hide()
        self.menu.setAttribute(Qt.WA_DontShowOnScreen, False)

    def activate(self, reason):
        self.controls_data = self.v4l2_ctl.read_controls()
        self.formats = self.v4l2_ctl.read_formats()
        self.current_format = self.v4l2_ctl.get_format()
        self.readd_widgets()
        self.update_controls()
        self.update_format()
        icon_pos = self.icon.geometry().bottomRight()
        self.update_menu_size()
        self.menu.popup(icon_pos)
        return True

    def run(self):
        self.icon.show()
        sys.exit(self.app.exec_())
        color = QColor('#29AB87') if self.ok else QColor(255, 0, 0, 128)

        painter = QPainter(self)
        painter.setBrush(color)
        painter.setPen(color)
        painter.drawRect(self.rect())


# TODO: Нарисовать график
if __name__ == '__main__':
    app = QApplication([])
    app.setQuitOnLastWindowClosed(False)

    tray = QSystemTrayIcon(QIcon(TRAY_ICON))

    job_report_widget = JobReportWidget()
    job_report_widget.setFixedSize(230, 130)
    job_report_widget_action = QWidgetAction(job_report_widget)
    job_report_widget_action.setDefaultWidget(job_report_widget)

    menu = QMenu()
    menu.addAction(job_report_widget_action)

    tray.setContextMenu(menu)
    tray.activated.connect(lambda x: menu.exec(tray.geometry().center()))

    tray.setToolTip('Compass Plus. Рапорт учета рабочего времени')
    tray.show()

    app.exec()
Beispiel #3
0
class SoundIcon(QObject, VolumeMixin, MediaKeysMixin):
    icon_updated = pyqtSignal(object)
    media_key_pressed = pyqtSignal(list)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.app.setApplicationName(APP_NAME)
        self.screen_height = self.app.primaryScreen().geometry().height()
        with open(LINUX_CSS, 'r') as css_file:
            self.app.setStyleSheet(css_file.read())
        self.mixer = PulseMixer()
        self.init_dbus()
        self.create_icon()
        self.mixer.start_listener(self.get_pulse_callback)
        self.icon_updated.connect(self.update_icon)
        self.create_menu()
        self.update_icon()
        self.init_keys()
        self.media_key_pressed.connect(self.on_media_key_pressed)

        self.run()

    def get_pulse_callback(self):
        return lambda e: self.icon_updated.emit(e)

    def get_notify_callback(self):
        return lambda k, t: self.media_key_pressed.emit([k, t])

    def create_icon(self):
        self.icon_name = ''
        self.icon = QSystemTrayIcon()
        self.icon.activated.connect(self.activate)
        self.icon.eventFilter = self.on_scroll
        self.icon.installEventFilter(self.icon)

    def create_menu(self):
        self.menu = QMenu()
        self.slider_item = SliderItem(self.menu)
        self.slider_item.value_changed.connect(self.on_value_changed)
        mixer_item = self.create_mixer()
        exit_item = self.create_exit()
        self.menu.addAction(self.slider_item)
        self.menu.addAction(mixer_item)
        self.menu.addSeparator()
        self.menu.addSeparator()
        self.menu.addAction(exit_item)

    def create_mixer(self):
        item = MenuItem(LABEL_MIXER, self.menu)
        item.triggered.connect(self.activate_mixer)
        return item

    def create_exit(self):
        item = MenuItem(LABEL_EXIT, self.menu)
        item.triggered.connect(self.activate_exit)
        return item

    def activate_exit(self, *args):
        os._exit(0)
        return True

    def on_value_changed(self, value):
        self.mixer.set_volume(value)
        return True

    def activate(self, reason):
        if reason == QSystemTrayIcon.Context:
            self.mixer.toggle_mute()
            return True
        if reason != QSystemTrayIcon.Trigger:
            return False
        self.update_menu()
        volume, mute = self.mixer.get_sink_volume_and_mute()
        self.slider_item.setValue(int(volume))
        self.slider_item.setEnabled(not mute)

        icon_pos = self.icon.geometry().bottomRight()
        width = min(self.menu.geometry().width(), VOLUME_WIDTH)
        if icon_pos.y() > self.screen_height / 2:
            pos = icon_pos - QPoint(width, 40)
            self.menu.popup(pos, self.menu.actions()[-1])
        else:
            pos = icon_pos - QPoint(width, 0)
            self.menu.popup(pos)
        return True

    def destroy_item(self, item):
        self.menu.removeAction(item)
        item.deleteLater()

    def insert_label_item(self, label, pos):
        item = MenuItem(label, self.menu)
        item.label.setProperty('objectName', 'section')
        before = self.menu.actions()[pos]
        self.menu.insertAction(before, item)
        item.setEnabled(False)
        self.profile_items.append(item)

    def insert_subaction_item(self, profile, link, pos):
        item = MenuItem(profile, self.menu)
        item.label.setProperty('objectName', 'subaction')
        item.triggered.connect(lambda c: self.mixer.set_profile(link))
        before = self.menu.actions()[pos]
        self.menu.insertAction(before, item)
        if link == self.mixer.current_profile:
            item.setEnabled(False)
        self.profile_items.append(item)

    def on_scroll(self, obj, event):
        if not isinstance(event, QWheelEvent):
            return False
        delta_y = event.angleDelta().y()
        if delta_y == 0:
            return
        change = SCROLL_BY if delta_y > 0 else -SCROLL_BY
        self.mixer.change_volume(change)
        return True

    def set_theme_icon(self, icon_name):
        qicon = QIcon.fromTheme(icon_name)
        self.icon.setIcon(qicon)

    def is_menu_visible(self):
        return self.menu.isVisible()

    def run(self):
        self.icon.show()
        sys.exit(self.app.exec_())
Beispiel #4
0
class LoginDialog(QDialog):
    def __init__(self, steam, theme):
        super().__init__()
        self.steam = steam
        self.theme = theme
        self.trayicon = None
        self.wait_task = None
        self.process = None
        self._exit = False
        self._login = None
        self.user_widgets = []
        self.setLayout(QVBoxLayout())
        self.setWindowTitle("Steam Acolyte")
        self.setWindowIcon(theme.window_icon)
        self.setStyleSheet(theme.window_style)
        steam.command_received.connect(lambda *_: self.activateWindow())
        self.update_userlist()

    def update_userlist(self):
        """Update the user list widget from the config file."""
        self.clear_layout()
        users = sorted(self.steam.users(),
                       key=lambda u:
                       (u.persona_name.lower(), u.account_name.lower()))
        users.append(SteamUser('', '', '', ''))
        for user in users:
            self.layout().addWidget(UserWidget(self, user))

    def clear_layout(self):
        """Remove all users from the user list widget."""
        # The safest way I found to clear a QLayout is to reparent it to a
        # temporary widget. This also recursively reparents, hides and later
        # destroys any child widgets.
        layout = self.layout()
        if layout is not None:
            dump = QWidget()
            dump.setLayout(layout)
            dump.deleteLater()
        self.setLayout(QVBoxLayout())

    @trace.method
    def wait_for_lock(self):
        """Start waiting for the steam instance lock asynchronously, and
        show/activate the window when we acquire the lock."""
        if self._exit:
            self.close()
            return
        self.wait_task = AsyncTask(self.steam.wait_for_lock)
        self.wait_task.finished.connect(self._on_locked)
        self.wait_task.start()

    @trace.method
    def _on_locked(self):
        """Executed when steam instance lock is acquired. Executes any queued
        login command, or activates the user list widget if no command was
        queued."""
        if self._exit:
            self.close()
            return
        self.stopAction.setEnabled(False)
        self.wait_task = None
        self.steam.store_login_cookie()
        self.update_userlist()
        if self._login:
            self.run_steam(self._login)
            self._login = None
            return
        self.show()

    @trace.method
    def show_trayicon(self):
        """Create and show the tray icon."""
        # The conversion to QPixmap and back to QIcon is needed to prevent a
        # bug that leads to the icon not being displayed in plasma. See:
        # - https://github.com/coldfix/steam-acolyte/issues/8
        # - https://bugreports.qt.io/browse/QTBUG-53550
        icon = QIcon(self.theme.window_icon.pixmap(64))
        self.trayicon = QSystemTrayIcon(icon)
        self.trayicon.setVisible(True)
        self.trayicon.setToolTip("acolyte - lightweight steam account manager")
        self.trayicon.activated.connect(self.trayicon_clicked)
        self.trayicon.setContextMenu(self.createMenu())

    @trace.method
    def hide_trayicon(self):
        """Hide and destroy the tray icon."""
        if self.trayicon is not None:
            self.trayicon.setVisible(False)
            self.trayicon.deleteLater()
            self.trayicon = None

    @trace.method
    def trayicon_clicked(self, reason):
        """Activate window when tray icon is left-clicked."""
        if reason == QSystemTrayIcon.Trigger:
            if self.steam.has_steam_lock():
                self.activateWindow()

    def createMenu(self):
        """Compose tray menu."""
        style = self.style()
        stop = self.stopAction = QAction('&Exit Steam', self)
        stop.setToolTip('Signal steam to exit.')
        stop.setIcon(style.standardIcon(QStyle.SP_MediaStop))
        stop.triggered.connect(self.exit_steam)
        stop.setEnabled(False)
        exit = QAction('&Quit', self)
        exit.setToolTip('Exit acolyte.')
        exit.setIcon(style.standardIcon(QStyle.SP_DialogCloseButton))
        exit.triggered.connect(self._on_exit, QueuedConnection)

        self.newUserAction = make_user_action(self, SteamUser('', '', '', ''))
        self.userActions = []
        menu = QMenu()
        menu.addSection('Login')
        menu.addAction(self.newUserAction)
        menu.addSeparator()
        menu.addAction(stop)
        menu.addAction(exit)
        menu.aboutToShow.connect(self.update_menu, QueuedConnection)
        return menu

    def update_menu(self):
        """Update menu just before showing: populate with current user list
        and set position from tray icon."""
        self.populate_menu()
        self.position_menu()

    def populate_menu(self):
        """Update user list menuitems in tray menu."""
        menu = self.trayicon.contextMenu()
        for action in self.userActions:
            menu.removeAction(action)
        users = sorted(self.steam.users(),
                       key=lambda u:
                       (u.persona_name.lower(), u.account_name.lower()))
        self.userActions = [make_user_action(self, user) for user in users]
        menu.insertActions(self.newUserAction, self.userActions)

    def position_menu(self):
        """Set menu position from tray icon."""
        menu = self.trayicon.contextMenu()
        desktop = QApplication.desktop()
        screen = QApplication.screens()[desktop.screenNumber(menu)]

        screen_geom = screen.availableGeometry()
        menu_size = menu.sizeHint()
        icon_geom = self.trayicon.geometry()

        if icon_geom.left() + menu_size.width() <= screen_geom.right():
            left = icon_geom.left()
        elif icon_geom.right() - menu_size.width() >= screen_geom.left():
            left = icon_geom.right() - menu_size.width()
        else:
            return

        if icon_geom.bottom() + menu_size.height() <= screen_geom.bottom():
            top = icon_geom.bottom()
        elif icon_geom.top() - menu_size.height() >= screen_geom.top():
            top = icon_geom.top() - menu_size.height()
        else:
            return

        menu.move(left, top)

    @trace.method
    def exit_steam(self):
        """Send shutdown command to steam."""
        self.stopAction.setEnabled(False)
        self.steam.stop()

    @trace.method
    def _on_exit(self):
        """Exit acolyte."""
        # We can't quit if steam is still running because QProcess would
        # terminate the child with us. In this case, we hide the trayicon and
        # set an exit flag to remind us about to exit as soon as steam is
        # finished.
        self.hide_trayicon()
        if self.steam.has_steam_lock():
            self.close()
        else:
            self._exit = True
            self.steam.unlock()
            self.steam.release_acolyte_instance_lock()

    @trace.method
    def login(self, username):
        """
        Exit steam if open, and login the user with the given username.
        """
        if self.steam.has_steam_lock():
            self.run_steam(username)
        else:
            self._login = username
            self.exit_steam()

    @trace.method
    def run_steam(self, username):
        """Run steam as the given user."""
        # Close and recreate after steam is finished. This serves two purposes:
        # 1. update user list and widget state
        # 2. fix ":hover" selector not working on linux after hide+show
        self.hide()
        self.steam.switch_user(username)
        self.steam.unlock()
        self.stopAction.setEnabled(True)
        self.process = self.steam.run()
        self.process.finished.connect(self.wait_for_lock)

    @trace.method
    def show_waiting_message(self):
        """If we are in the background, show waiting message as balloon."""
        if self.trayicon is not None:
            self.trayicon.showMessage("steam-acolyte",
                                      "The damned stand ready.")
Beispiel #5
0
class Reminder(ShowCenterMixin, OsxMenuMixin, TimerMixin):
    def __init__(self):
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.app.setApplicationName(SPINBOX_WINDOW_TITLE)
        self.screen_height = self.app.primaryScreen().geometry().height()
        styleSheet = open(APP_STYLESHEET).read()
        if sys.platform == 'darwin':
            c = self.app.palette().color(QPalette.Highlight)
            color = 'rgba({},{},{},10%)'.format(c.red(), c.green(), c.blue())
            styleSheet = styleSheet.replace('palette(highlight)', color)
        self.app.setStyleSheet(styleSheet)

        self.setup_icon()
        self.setup_menu()
        self.setup_popup()
        self.setup_window()
        self.init_saved_alarm()
        self.idle()
        self.run()

    def on_button_clicked(self, *args):
        self.window.hide()
        h = self.spins[0].value()
        m = self.spins[1].value()
        s = self.spins[2].value()
        self.start_timer(h, m, s)

    def setup_window(self):
        self.window = QWidget()
        self.window.setProperty('objectName', 'window')
        self.window.setWindowTitle(SPINBOX_WINDOW_TITLE)
        self.window.setWindowFlags(self.window.windowFlags()
                                   | Qt.WindowStaysOnTopHint | Qt.Dialog)
        vlayout = QVBoxLayout(self.window)
        hlayout = QHBoxLayout()
        hlayout.setSpacing(SPINBOX_WINDOW_SPACING)

        self.spins = []
        for label in ['h', 'm', 's']:
            button_spin = ButtonSpinBox(label=label)
            spin = button_spin.spin
            self.spins.append(spin)
            hlayout.addLayout(button_spin)

        button = QPushButton(BUTTON_LABEL)
        button.setProperty('objectName', 'start')

        vlayout.addLayout(hlayout)
        vlayout.addWidget(button)

        button.clicked.connect(self.on_button_clicked)
        self.window.closeEvent = self.on_window_close

    def on_window_close(self, *args):
        self.window.hide()

    def setup_icon(self):
        self.icon_normal = QIcon(ALARM_PATH)
        self.icon = QSystemTrayIcon(self.icon_normal)
        self.icon_urgent = QIcon(ALARM_URGENT_PATH)
        self.icon_active = QIcon(ALARM_ACTIVE_PATH)
        self.icon.activated.connect(self.activate_menu)

    def setup_menu(self):
        self.menu = QMenu()
        clock_item = QWidgetAction(self.menu)
        self.clock = QPushButton(' ')
        self.clock.setProperty('objectName', 'menu')
        font = self.clock.font()
        font.setPixelSize(CLOCK_FONT_SIZE)
        self.clock.setFont(font)
        clock_item.setDefaultWidget(self.clock)
        self.clock.clicked.connect(self.activate_window)

        exit_item = QWidgetAction(self.menu)
        label = QPushButton(EXIT_LABEL)
        label.setProperty('objectName', 'menu')
        exit_item.setDefaultWidget(label)
        label.clicked.connect(self.activate_exit)

        self.menu.addAction(clock_item)
        self.menu.addAction(exit_item)

        if sys.platform == 'darwin':
            clock_item.button = self.clock
            exit_item.button = label
            self.menu.actions = [clock_item, exit_item]
            self.set_up_menu_macos(self.menu)

    def setup_popup(self):
        self.popup = QWidget()
        self.popup.setProperty('objectName', 'popup')
        vlayout = QVBoxLayout(self.popup)
        label = QLabel(ALERT_TEXT)
        vlayout.addWidget(label)
        self.popup.setWindowFlags(Qt.Sheet | Qt.Popup | Qt.WindowStaysOnTopHint
                                  | Qt.FramelessWindowHint)

        self.popup.mouseReleaseEvent = self.on_popup_release

    def on_popup_release(self, *args):
        self.popup.hide()

    def activate_menu(self, reason):
        if reason != QSystemTrayIcon.Trigger:
            return
        if self.icon.icon().cacheKey() == self.icon_urgent.cacheKey():
            self.clear_alarm()
            self.set_icon(self.icon_normal)
        self.update_clock()
        if sys.platform == 'darwin':
            self.on_menu_activated_macos()
            self.icon.setContextMenu(self.menu)
            self.icon.setContextMenu(None)
            return
        icon_pos = self.icon.geometry().bottomRight()
        width = min(self.menu.geometry().width(), 135)
        if icon_pos.y() > self.screen_height / 2:
            pos = icon_pos - QPoint(width, 40)
            self.menu.popup(pos, self.menu.actions()[-1])
        else:
            pos = icon_pos - QPoint(width, 0)
            self.menu.popup(pos)

    def activate_window(self, *args):
        if sys.platform == 'darwin':
            clock_action = self.menu.actions[0]
            clock_action.activate(clock_action.Trigger)
        self.show_center(self.window)

    def activate_exit(self, *args):
        os._exit(0)

    def set_icon(self, icon):
        self.icon.setIcon(icon)

    def update_clock_text(self, text):
        self.clock.setText(text)

    def show_popup(self):
        self.show_center(self.popup)

    def is_menu_visible(self):
        if sys.platform == 'darwin':
            return True
        return self.menu.isVisible()

    def run(self):
        timer = QTimer()
        timer.setInterval(1000)
        timer.timeout.connect(self.idle)
        timer.start(1000)
        self.icon.show()
        sys.exit(self.app.exec_())