def drawIconWithShadow(icon: QIcon, rect: QRect, p: QPainter, iconMode: QIcon.Mode = None, dipRadius: int = None, color: QColor = None, dipOffset: QPoint = None): if iconMode is None: iconMode = QIcon.Normal if color is None: color = QColor(0, 0, 0, 130) if dipRadius is None: dipRadius = 3 if dipOffset is None: dipOffset = QPoint(1, -2) devicePixelRatio: int = p.device().devicePixelRatio() pixmapName = "icon %s %s %s %s" % (icon.cacheKey(), iconMode, rect.height(), devicePixelRatio) cache = QPixmapCache.find(pixmapName) if cache is None: # High-dpi support: The in parameters (rect, radius, offset) are in # device-independent pixels. The call to QIcon::pixmap() below might # return a high-dpi pixmap, which will in that case have a devicePixelRatio # different than 1. The shadow drawing caluculations are done in device # pixels. window = p.device().window().windowHandle() px = icon.pixmap(window, rect.size(), iconMode) radius = dipRadius * devicePixelRatio offset = dipOffset * devicePixelRatio cache = QPixmap(px.size() + QSize(radius * 2, radius * 2)) cache.fill(Qt.transparent) cachePainter = QPainter(cache) if iconMode == QIcon.Disabled: hasDisabledState = len(icon.availableSizes()) == len( icon.availableSizes(QIcon.Disabled)) if not hasDisabledState: px = disabledSideBarIcon(icon.pixmap(window, rect.size())) elif TOOLBAR_ICON_SHADOW: # Draw shadow tmp = QImage(px.size() + QSize(radius * 2, radius * 2 + 1), QImage.Format_ARGB32_Premultiplied) tmp.fill(Qt.transparent) tmpPainter = QPainter(tmp) tmpPainter.setCompositionMode(QPainter.CompositionMode_Source) tmpPainter.drawPixmap( QRect(radius, radius, px.width(), px.height()), px) tmpPainter.end() # blur the alpha channel blurred = QImage(tmp.size(), QImage.Format_ARGB32_Premultiplied) blurred.fill(Qt.transparent) blurPainter = QPainter(blurred) #qt_blurImage(blurPainter, tmp, radius, False, True) # implement qt_blurImage via QLabel with QGraphicsBlurEffect # FIXME: alignment is broken scene = QGraphicsScene() item = QGraphicsPixmapItem(QPixmap.fromImage(tmp)) effect = QGraphicsBlurEffect() effect.setBlurRadius(radius) item.setGraphicsEffect(effect) scene.addItem(item) scene.render(blurPainter) blurPainter.end() tmp = blurred # blacken the image... tmpPainter.begin(tmp) tmpPainter.setCompositionMode(QPainter.CompositionMode_SourceIn) tmpPainter.fillRect(tmp.rect(), color) tmpPainter.end() tmpPainter.begin(tmp) tmpPainter.setCompositionMode(QPainter.CompositionMode_SourceIn) tmpPainter.fillRect(tmp.rect(), color) tmpPainter.end() # draw the blurred drop shadow... cachePainter.drawImage( QRect(0, 0, cache.rect().width(), cache.rect().height()), tmp) # Draw the actual pixmap... cachePainter.drawPixmap( QRect( QPoint(radius, radius) + offset, QSize(px.width(), px.height())), px) cachePainter.end() cache.setDevicePixelRatio(devicePixelRatio) QPixmapCache.insert(pixmapName, cache) targetRect = cache.rect() targetRect.setSize(targetRect.size() / cache.devicePixelRatio()) targetRect.moveCenter(rect.center() - dipOffset) p.drawPixmap(targetRect, cache)
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_())