Source code for _clockalarm.main
# ClockAlarm is a cross-platform alarm manager
# Copyright (C) 2017 Loïc Charrière, Samuel Gauthier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser
import logging
import sys
from os.path import dirname, abspath, join, isfile
from PyQt5.Qt import QIcon
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QApplication
from _clockalarm import Clock
from _clockalarm.AlertCollection import AlertCollection
from _clockalarm.NotificationCenter import NotificationCenter
from _clockalarm.UI.MainWindow import MainWindow
from _clockalarm.utils import importExportUtils
from _clockalarm.utils.importExportUtils import get_default_config
EXIT_CODE_REBOOT = -11231351 # error code launch by App in case of reboot
app = None # global
log_format = '%(asctime)s - %(levelname)-8s : %(message)s'
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format=log_format) # set logging level and format
[docs]class App(QApplication):
"""Main Application extending QApplication
"""
CLOCK_FREQUENCY = None # frequency of time checks
MUTE = None # general mute of the application
# Override the class constructor
def __init__(self, default_config_path, alert_db_path, *argv):
"""Default App constructor
Override the class constructor
Attributes:
default_config_path: complete path to the .cfg config file
alert_db_path: complete path to the .json alert_db file
*argv: pointer to the system arguments
"""
super(App, self).__init__(*argv)
if not isfile(default_config_path) or not default_config_path.lower().endswith('.cfg'):
raise ValueError("Incorrect configuration file, give a .cfg file")
if not isfile(alert_db_path) or not alert_db_path.lower().endswith('.json'):
raise ValueError("Incorrect database file, give a .json file")
importExportUtils.DEFAULT_CONFIG_PATH = default_config_path
importExportUtils.ALERT_DB_PATH = alert_db_path
self.main_window = None
self.notification_center = None
self.alert_collection = None
self.clock_thread = None
screen_geometry = self.desktop().screenGeometry() # get the dimensions of the current screen
logging.info("screen dimensions = (" + str(screen_geometry.width()) + "," + str(screen_geometry.height()) + ")")
# pass the screen dimensions to the NotificationCenter constructor to init the notification zone
self.notification_center = NotificationCenter(screen_geometry, parent=self)
self.init_config()
self.init_clock(self.CLOCK_FREQUENCY)
self.init_ui()
[docs] def init_config(self):
"""Open the config_test.cfg file and use a parser to retrieve default values
"""
logging.debug("loading configuration file ...")
config = configparser.RawConfigParser() # instantiate a parser to read the stream
config.read(importExportUtils.DEFAULT_CONFIG_PATH)
self.CLOCK_FREQUENCY = config.getint("default", "CLOCK_FREQUENCY")
self.MUTE = config.getboolean("default", "MUTE")
logging.debug("success")
[docs] def init_clock(self, freq):
"""Init and start the Clock with the given frequency
Attributes:
freq: frequency of the clock's ticks
"""
self.clock_thread = Clock(freq)
self.clock_thread.start()
[docs] 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)
[docs] def init_alert_collection(self):
"""Init AlertCollection object and connect the clock to it
Note:
The alerts will be loaded from the default database
"""
self.alert_collection = AlertCollection(self)
self.clock_thread.tick.connect(self.alert_collection.check_timers)
[docs]def main(argv):
"""Main function called when application starts
Attributes:
argv (optional): path of the config file and the alertDB file
usage: program configFile alrtDBFile
If no argument entered, default is config_test.cfg and alertDB.json
Returns:
the system exit code
"""
global app
exit_code = EXIT_CODE_REBOOT # exit_code is initially REBOOT
if sys.platform == "win32": #
# workaround to display app icon in task-bar on Windows OS (from http://stackoverflow.com/a/27872625)
import ctypes
myappid = u'bfh.project1.clockalarm.1-2' # arbitrary string (unicode)
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
default_config_path = join(dirname(dirname(abspath(__file__))), "config.cfg") # default config path
alert_db_path = join(dirname(dirname(abspath(__file__))), "alertsDB.json") # default db path
if len(argv) > 1: # parse the config path argument
default_config_path = argv[1]
if len(argv) > 2: # parse the db path argument
alert_db_path = argv[2]
logging.info("Default configuration file path: " + default_config_path)
logging.info("Default alertDB path: " + alert_db_path)
while exit_code == EXIT_CODE_REBOOT:
# start or reboot
try:
app = App(default_config_path, alert_db_path, argv)
app.init_alert_collection()
except RuntimeError as e:
logging.error("RuntimeError : " + e.args[0])
app = QCoreApplication.instance()
exit_code = app.exec() # update exit code
# properly close the App
app.clock_thread.stop()
app.alert_collection.db.close()
app.main_window.setVisible(False)
app.main_window.tray_icon.setVisible(False)
app = None
return sys.exit(exit_code)
if __name__ == '__main__':
# to start from bin
main(sys.argv)