QMessageBox срабатывает только если окно программы находится в фокусе

Необходимо реализовать функции напоминания для пользователя.
Пользователь устанавливает текстовую информацию в поле , выбирает дату и время когда напомнить и это заносится в файл.json,
откуда в последующем считывается и про достижении нужного срока создается QMessageBox с напоминанием.

Сложность заключается в том, что при переводе фокуса на другое окно функция не срабатывает, до момента пока пользователь не вернётся обратно к программе.

Интересует как это победить и чтобы напоминания срабатывали даже есть пользователь переключился на другие приложения?

main.py:

import json
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QDateTimeEdit, QPushButton, \
    QMessageBox, QApplication
from PyQt6.QtCore import Qt, QDateTime, QTimer
from PyQt6.QtGui import QIcon

RECORDS_FILE = 'records.json'


class ReminderApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Reminder App")
        self.setWindowIcon(QIcon('AH2_icon.png'))

        layout = QVBoxLayout()

        # Текстовое поле для ввода информации
        self.info_input = QLineEdit()
        layout.addWidget(QLabel("Тестовая информация:"))
        layout.addWidget(self.info_input)

        # Выбор даты и времени для напоминания
        self.datetime_picker = QDateTimeEdit()
        self.datetime_picker.setCalendarPopup(True)
        self.datetime_picker.setDateTime(QDateTime.currentDateTime())
        layout.addWidget(QLabel("Дата и время напоминания:"))
        layout.addWidget(self.datetime_picker)

        # Кнопка для создания напоминания
        self.reminder_button = QPushButton("Создать напоминание")
        self.reminder_button.clicked.connect(self.create_reminder)
        layout.addWidget(self.reminder_button)
        self.setLayout(layout)

        # Таймер для проверки напоминаний
        self.timer = QTimer()
        self.timer.timeout.connect(self.check_reminders)
        self.timer.start(1000)  # каждую секунду

    def create_reminder(self):
        info = self.info_input.text()
        datetime = self.datetime_picker.dateTime().toString('yyyy-MM-dd HH:mm:ss')

        # Сохранение записи в базу данных
        save_record(info, datetime)
        # Очистка тестового поля
        self.datetime_picker.clear()

    def check_reminders(self):
        records = load_records()
        current_datetime = QDateTime.currentDateTime()

        for record in records:
            reminder_datetime = QDateTime.fromString(record['datetime'], 'yyyy-MM-dd HH:mm:ss')

            if current_datetime >= reminder_datetime:
                # Всплывающее окно для напоминания
                reminder_msg_box = QMessageBox()
                reminder_msg_box.setWindowTitle("Напоминание")
                reminder_msg_box.setText("Напоминаю: " + record['info'])
                reminder_msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
                reminder_msg_box.exec()

                # Удаление выполненного напоминания из базы данных
                records.remove(record)
        # Сохранение обновленной базы данных
        save_records(records)

def save_record(info, datetime):
    records = load_records()
    records.append({"info": info, "datetime": datetime})
    save_records(records)

def save_records(records):
    with open(RECORDS_FILE, 'w') as file:
        json.dump(records, file)

def load_records():
    try:
        with open(RECORDS_FILE, 'r') as file:
            records = json.load(file)
    except FileNotFoundError:
        records = []
    return records


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ReminderApp()
    window.show()
    sys.exit(app.exec())

Ответы (2 шт):

Автор решения: Amgarak

Могу предложить использовать уведомления, если пишите для окон. Вот можете рассмотреть мой шуточный код, а дальше доработать и адаптировать если он вам подойдёт.

import ctypes
from ctypes import Structure, c_uint, sizeof, byref
from ctypes.wintypes import HWND, UINT
import random

class NOTIFYICONDATA(Structure):
    _fields_ = [
        ("cbSize", c_uint),
        ("hWnd", HWND),
        ("uID", UINT),
        ("uFlags", UINT),
        ("uCallbackMessage", UINT),
        ("hIcon", HWND),
        ("szTip", ctypes.c_wchar * 128),
        ("dwState", UINT),
        ("dwStateMask", UINT),
        ("szInfo", ctypes.c_wchar * 256),
        ("uVersion", UINT),
        ("szInfoTitle", ctypes.c_wchar * 64),
        ("dwInfoFlags", UINT),
        ("guidItem", ctypes.c_byte * 16),
        ("hBalloonIcon", HWND),
    ]

class PatsanNotification:
    def __init__(self):
        title = "Patsan Notification"
        self.nid = NOTIFYICONDATA()
        self.nid.cbSize = sizeof(NOTIFYICONDATA)
        self.nid.hWnd = None
        self.nid.uID = 0
        self.nid.uFlags = 0x00000010 | 0x00000001 | 0x00000200
        self.nid.uCallbackMessage = 1024
        self.nid.szTip = title
        self.nid.szInfoTitle = title
        self.nid.dwState = 0
        self.nid.dwStateMask = 0
        self.nid.uVersion = 2000
        self.nid.dwInfoFlags = 1

        self.shell32 = ctypes.windll.shell32
        
        self.text_list = ["Сильный пацан знает, что его сила в том, чтобы не злоупотреблять ею.",  
                "Друзья – это семья, которую мы сами выбираем.",  
                "Победа, которая достигнута честным путем, вдвойне сладка.",  
                "Ваша первая обязанность – к самому себе быть честным. Мораль – это прежде всего самоконтроль.", 
                "Если не можешь быть хорошим примером, будь забавным предостережением.", 
                "Хорошо там, где нас нет, а там, где мы, там не очень." 
]

    def show_notification(self):
        random_text = random.choice(self.text_list)
        self.nid.szInfo = random_text
        self.shell32.Shell_NotifyIconW(0, byref(self.nid))

if __name__ == "__main__":
# Создаем объект класса PatsanNotification
 patsan_notifier = PatsanNotification()
 patsan_notifier.show_notification()
→ Ссылка
Автор решения: S. Nick

Попробуйте так:

import json
import sys
'''
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, \
    QLineEdit, QDateTimeEdit, QPushButton, QMessageBox, QApplication
from PyQt6.QtCore import Qt, QDateTime, QTimer
from PyQt6.QtGui import QIcon
'''
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *


RECORDS_FILE = 'records.json'


class ReminderApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Reminder App")
        self.setWindowIcon(QIcon('AH2_icon.png'))

        layout = QVBoxLayout()

        # Текстовое поле для ввода информации
        self.info_input = QLineEdit()
# +++
        self.label = QLabel("Тестовая информация:")                         # +
        layout.addWidget(self.label)
        layout.addWidget(self.info_input)

        # Выбор даты и времени для напоминания
        self.datetime_picker = QDateTimeEdit()
        self.datetime_picker.setCalendarPopup(True)
        self.datetime_picker.setDateTime(QDateTime.currentDateTime())
# +++        
        self.datetime_picker.setDisplayFormat("yyyy-MM-dd HH:mm:ss")        # +
                
        layout.addWidget(QLabel("Дата и время напоминания:"))
        layout.addWidget(self.datetime_picker)

        # Кнопка для создания напоминания
        self.reminder_button = QPushButton("Создать напоминание")
        self.reminder_button.clicked.connect(self.create_reminder)
        layout.addWidget(self.reminder_button)
        self.setLayout(layout)

        # Таймер для проверки напоминаний
        self.timer = QTimer()
        self.timer.timeout.connect(self.check_reminders)
        self.timer.start(1000)                                    

    def create_reminder(self):
        info = self.info_input.text()
        datetime = self.datetime_picker.dateTime().toString(
            'yyyy-MM-dd HH:mm:ss')

        # Сохранение записи в базу данных
        save_record(info, datetime)
        # Очистка тестового поля
        self.datetime_picker.clear()
# +++
        self.datetime_picker.setFocus()                                     # +

    def check_reminders(self):
        records = load_records()
        current_datetime = QDateTime.currentDateTime()
# +++   
        self.label.setText(     
            f'Текущая дата и время: {current_datetime.toString("yyyy-MM-dd hh:mm:ss")}')

        for record in records:
            reminder_datetime = QDateTime.fromString(
                record['datetime'], 'yyyy-MM-dd HH:mm:ss')

            if current_datetime >= reminder_datetime:
                # Всплывающее окно для напоминания
                
#                reminder_msg_box = QMessageBox()             
# -------------------------------------------> vvvv <-----------------------------
                reminder_msg_box = QMessageBox(self)                     # !!! +++
                reminder_msg_box.setWindowTitle("Напоминание")
                reminder_msg_box.setText(
                    f"Напоминаю: {record['info']} \n{record['datetime']}")
                reminder_msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
#                reminder_msg_box.exec()
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                reminder_msg_box.setWindowFlags(                          # !!! +++
                    Qt.WindowStaysOnTopHint | Qt.Tool)                    # !!! +++
                reminder_msg_box.show()                                   # !!! +++
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                # Удаление выполненного напоминания из базы данных
                records.remove(record)

        # Сохранение обновленной базы данных
        save_records(records)


def save_record(info, datetime):
    records = load_records()
    records.append({"info": info, "datetime": datetime})
    save_records(records)

def save_records(records):
    with open(RECORDS_FILE, 'w') as file:
        json.dump(records, file)

def load_records():
    try:
        with open(RECORDS_FILE, 'r') as file:
            records = json.load(file)
    except FileNotFoundError:
        records = []
    return records


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle(QStyleFactory.create("Fusion"))                          # +
    app.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold))               # +
    window = ReminderApp()
    window.resize(400, 200)                                               # +
    window.show()
    sys.exit(app.exec())

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка