def refresh(self): """Refresh the display off notifications in the display zone Compact the remaining notifications in the display zone, if there is free slots, pop the notification queue and display the popup. """ if len(self._displayed_popups ) >= self._max_popups: # display zone is full return WIDGET_HEIGHT = get_default_config("WIDGET_HEIGHT", "int") WIDGET_WIDTH = get_default_config("WIDGET_WIDTH", "int") PADDING = get_default_config("WIDGET_PADDING", "int") i = 0 # position of the popup in the display zone """Compact the remaining popups""" for popup in self._displayed_popups: popup.setGeometry( QRect(self.ax, self.ay + i * (WIDGET_HEIGHT + PADDING), WIDGET_WIDTH, WIDGET_HEIGHT)) i += 1 """Add new popups""" self._lock.acquire() if len(self._popup_queue) == 0: # empty queue self._lock.release() else: # notification waiting in queue new_notification = self._popup_queue.popleft() self._lock.release() self.display_popup( QRect(self.ax, self.ay + i * (WIDGET_HEIGHT + PADDING), WIDGET_WIDTH, WIDGET_HEIGHT), new_notification)
def test_get_font_miss_two_arg(init_paths): """Tests the :class:`~_clockalarm.Notification.get_font` method without any font given. """ notification = Notification("Test") font = notification.get_font() assert isinstance(font, QFont) assert font.family() == importExportUtils.get_default_config( "NOTIFICATION_FONT_FAMILY") assert font.pointSize() == importExportUtils.get_default_config( "NOTIFICATION_FONT_SIZE", "int")
def get_sound(self): """Build a mixer.Sound object from sound parameter If the sound path parameter is missing, replace it with de default configuration. Returns: The mixer.Sound file of the notification """ _sound_path = None if self.sound: # existing sound path in Notification parameters _sound_path = pathlib.Path( join(base_path, "_clockalarm", "resources", "sounds", self.sound)).as_posix() if _sound_path is None or not isfile( _sound_path): # incorrect or missing sound path _sound_path = pathlib.Path( join(base_path, "_clockalarm", "resources", "sounds", get_default_config("NOTIFICATION_SOUND"))).as_posix( ) # replace with default sound path logging.log(1, "notification sound path: " + _sound_path) mixer.init() return mixer.Sound( _sound_path) # returns the sound in a mixer.Sound object
def test_mute_buton_click(init_paths, qtbot): """Test :class:~_clockalarm.UI.MainWindow.mute_button_click method.""" global mw with qtbot.waitExposed(mw): mw.show() mute_before_click = importExportUtils.get_default_config('MUTE', 'bool') qtbot.mouseMove(mw, mw.mute_pushbutton.pos() + QPoint(10, 10)) qtbot.mouseClick(mw.mute_pushbutton, QtCore.Qt.LeftButton) mute_after_click = importExportUtils.get_default_config('MUTE', 'bool') # Need to click again otherwise the some other tests may fail qtbot.mouseClick(mw.mute_pushbutton, QtCore.Qt.LeftButton) assert mute_before_click != mute_after_click
def test_get_font_miss_one_arg(init_paths): """Tests the :class:`~_clockalarm.Notification.get_font` method with one parameter missing """ notification = Notification("Test", font_size=10) font = notification.get_font() assert isinstance(font, QFont) assert font.family() == importExportUtils.get_default_config( "NOTIFICATION_FONT_FAMILY") assert font.pointSize() == 10 notification = Notification("Test", font_family="helvetica") font = notification.get_font() assert isinstance(font, QFont) assert font.family() == "helvetica" assert font.pointSize() == importExportUtils.get_default_config( "NOTIFICATION_FONT_SIZE", "int")
def test_app_constructor(before): """Tests the :class:`~_clockalarm.main.App` constructor. """ argv = ["file", test_config_path, test_alertsDB_path] app = main.App(argv[1], argv[2], argv) assert app.CLOCK_FREQUENCY == importExportUtils.get_default_config("CLOCK_FREQUENCY", "int") assert app.MUTE == importExportUtils.get_default_config("MUTE", "bool") assert isinstance(app.main_window, MainWindow.MainWindow) assert isinstance(app.notification_center, NotificationCenter.NotificationCenter) assert isinstance(app.clock_thread, Clock) assert app.alert_collection is None # alert_collection is not initializes in constructor app.init_alert_collection() assert isinstance(app.alert_collection, AlertCollection.AlertCollection) app.clock_thread.stop() app.alert_collection.db.close()
def test_get_color_miss_arg(init_paths): """Tests the :class:`~_clockalarm.Notification.get_color` method without any color given. """ notification = Notification("Test") color = notification.get_color() assert isinstance(color, QColor) assert color.name() == importExportUtils.get_default_config( "NOTIFICATION_COLOR_HEX")
def get_font(self): """Build a QFont from font_family and font_size If font_family or font_size parameter is missing, replace it with de default configuration. Returns: The QFont of the notification """ if self.font_family and self.font_size: # both parameters available return QFont(self.font_family, self.font_size) elif self.font_family: # missing font_size return QFont(self.font_family, get_default_config("NOTIFICATION_FONT_SIZE", "int")) elif self.font_size: # missing font_family return QFont(get_default_config("NOTIFICATION_FONT_FAMILY"), self.font_size) return QFont(get_default_config("NOTIFICATION_FONT_FAMILY"), get_default_config("NOTIFICATION_FONT_SIZE", "int")) # missing both parameters
def init_ui(self, geom): """Helper method that sets the style of the NotificationWidget. Attributes: geom: The position and size of the widget on the screen """ self.setGeometry(geom) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_ShowWithoutActivating) """Background Image""" im_name = get_default_config("WIDGET_FILE_NAME") im_path = join(dirname(dirname(abspath(__file__))), 'resources', 'images', im_name) lbl_im = QLabel(self) lbl_im.setPixmap(QPixmap(im_path)) """Notification message""" color = self.notification.get_color() alpha = get_default_config("WIDGET_TRANSPARENCY", "int") rgba = "{r}, {g}, {b}, {a}".format(r=color.red(), g=color.green(), b=color.blue(), a=alpha) lbl = QLabel(self.notification.message, self) lbl.setAlignment(Qt.AlignVCenter) lbl.setWordWrap(True) padding_top = get_default_config("WIDGET_TEXT_PADDING_TOP", "int") padding_left = get_default_config("WIDGET_TEXT_PADDING_LEFT", "int") text_width = get_default_config("WIDGET_TEXT_WIDTH", "int") text_height = get_default_config("WIDGET_TEXT_HEIGHT", "int") lbl.setGeometry( QRect(padding_left, padding_top, text_width, text_height)) lbl.setFont(self.notification.get_font()) lbl.setStyleSheet("QLabel { color : rgba(" + rgba + ")}")
def get_color(self): """Build a QColor from color_hex parameter If color_hex parameter is missing, replace it with de default configuration. Returns: The QColor of the notification """ if self.color_hex: return QColor(self.color_hex) return QColor(get_default_config( "NOTIFICATION_COLOR_HEX")) # missing color_hex parameter
def init_ui(self): """Initialisation of the main window GUI """ icon_path = join(dirname(abspath(__file__)), 'resources', 'images', get_default_config("ICON_FILE_NAME")) icon = QIcon(icon_path) self.setWindowIcon(icon) # application icon for OSx and linux self.main_window = MainWindow(self) self.main_window.show() self.setQuitOnLastWindowClosed( False ) # app don't quit when last window is closed (reduced in tray)
def __init__(self, screen_geometry, parent=None): """Default NotificationCenter constructor Initialize the waiting queue and the list of displayed popups. Compute the maximum number of popup one can display on the screen. Attributes: screen_geometry (QRect): dimensions of the screen displaying the app parent (optional): parent class for NotificationCenter (usually main.App object) Exceptions: ValueError: In case of incorrect screen_geometry parameter """ super(NotificationCenter, self).__init__() if not isinstance(screen_geometry, QRect) or screen_geometry is None: raise ValueError("screen_geometry must be a QRect object") self.parent = parent self._screen_geometry = screen_geometry self._max_popups = math.floor( (screen_geometry.height() * 0.9) / (get_default_config("WIDGET_HEIGHT", "int") + get_default_config("WIDGET_PADDING", "int")) ) # number of widget one can display with the given screen geometry self._popup_queue = deque([]) # list as a queue self._displayed_popups = [] self._lock = threading.RLock() # lock to protect the queue self.ax = self._screen_geometry.width() - get_default_config( "WIDGET_WIDTH", "int") - 20 # x coordinate of the notification zone in pixels self.ay = round( self._screen_geometry.height() * 0.04) # y coordinate of the notification zone in pixels
def init_ui(self): """Init helper method to set up the main window.""" self.setMinimumSize(QSize(300, 100)) # Set sizes self.setWindowTitle("ClockAlarm Manager") # Set a title self.resize(get_default_config("MAIN_WINDOW_WIDTH", "int"), get_default_config("MAIN_WINDOW_HEIGHT", "int")) import_action = QAction("Import Alerts File", self) import_action.triggered.connect(self.import_json_db) export_action = QAction("Export Alerts File", self) export_action.triggered.connect(self.export_json_db) quit_action = QAction("Exit", self) quit_action.triggered.connect(qApp.quit) new_alert_action = QAction("New Simple Alert", self) new_alert_action.triggered.connect(self.add_simple_alert) delete_alert_action = QAction("Delete Alert", self) delete_alert_action.triggered.connect(self.delete_alerts) edit_alert_action = QAction("Edit Alert", self) edit_alert_action.triggered.connect(self.edit_simple_alert) menu_bar = self.menuBar() file_menu = menu_bar.addMenu('&File') edit_menu = menu_bar.addMenu('&Edit') file_menu.addAction(import_action) file_menu.addAction(export_action) file_menu.addSeparator() file_menu.addAction(quit_action) edit_menu.addAction(new_alert_action) edit_menu.addAction(delete_alert_action) edit_menu.addAction(edit_alert_action) central_widget = QWidget(self) # Create a central widget self.setCentralWidget(central_widget) # Set the central widget grid_layout = QGridLayout(central_widget) # Create a QGridLayout central_widget.setLayout( grid_layout) # Set the layout into the central widget self.alert_list_widget = AlertListWidget() self.mute_pushbutton = QPushButton() if get_default_config("MUTE", "bool"): self.mute_pushbutton.setIcon(self.style().standardIcon( QStyle.SP_MediaVolumeMuted)) else: self.mute_pushbutton.setIcon(self.style().standardIcon( QStyle.SP_MediaVolume)) self.mute_pushbutton.clicked.connect(self.mute_button_click) grid_layout.addWidget(self.alert_list_widget, 0, 0) grid_layout.addWidget(self.mute_pushbutton, 1, 0, Qt.AlignRight) # Init QSystemTrayIcon icon_path = join(dirname(dirname(abspath(__file__))), 'resources', 'images', get_default_config("ICON_FILE_NAME")) icon = QIcon(icon_path) self.tray_icon = QSystemTrayIcon(self) self.tray_icon.setIcon(icon) self.tray_icon.activated.connect(self.tray_icon_click) tray_menu = QMenu() tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) self.tray_icon.show()