Beispiel #1
0
 def stop(cls):
     """Stop a timer."""
     # DO NOT call TimeDisplay.stop_time() again; it will double the time
     cls._set_mode_save()
     TimeDisplay.reset_time()
     if cls.stop_callback:
         cls.stop_callback()
Beispiel #2
0
    def pause(cls):
        """Pause a running timer."""

        cls._set_mode_paused()
        TimeDisplay.stop_time()
        if cls.pause_callback:
            cls.pause_callback()
Beispiel #3
0
    def prompt_stop(cls):
        """Pause timer and confirm stop."""

        cls._set_mode_prompt_stop()
        TimeDisplay.stop_time()
        if cls.pause_callback:
            cls.pause_callback()
Beispiel #4
0
    def start(cls):
        """Start a new timer."""

        cls._set_mode_running()
        TimeDisplay.start_time()
        if cls.start_callback:
            cls.start_callback()
Beispiel #5
0
    def resume(cls):
        """Resume a paused timer."""

        cls._set_mode_running()
        TimeDisplay.start_time()
        if cls.resume_callback:
            cls.resume_callback()
Beispiel #6
0
 def stop(cls):
     """Stop a timer."""
     # stop_time() already called from prompt_stop()
     cls._set_mode_save()
     TimeDisplay.reset_time()
     for callback in cls.stop_callback:
         callback()
Beispiel #7
0
    def reset(cls):
        """Discard a stopped timer's time instead of saving it."""

        cls._set_mode_stopped()
        TimeDisplay.show_default()
        Notes.clear()
        if cls.reset_callback:
            cls.reset_callback()
Beispiel #8
0
    def remember(cls, hours, minutes, seconds):
        # Don't store when clock resets, only if minutes or hours has a value
        if hours or minutes:
            # Grab data in advance
            data = [
                TimeDisplay.get_timestamp_string() + "\n",
                TimeDisplay.get_time_ms_string() + "\n",
                Notes.get_text() + "\n"
            ]

            # Write data to staging file
            with cls._staging.open('w') as file:
                file.writelines(data)
            cls._staging.replace(cls._storage)  # Move into place
Beispiel #9
0
 def check(cls):
     """Check if it's time to show a notification."""
     if not cls.interval:
         return
     if TimeDisplay.get_time_min() >= cls.next_at:
         return True
     return False
Beispiel #10
0
    def check_for_recall(cls):
        """Check if there is a backup that needs to be grabbed.
        If there are multiple backups, grab one (doesn't matter which)."""
        try:
            for backup in cls._backup_dir.glob('*.backup'):
                # Check for signs of life
                query = cls._backup_dir / f'{backup.stem}.query'
                # Create a query file to see if this instance ID is alive.
                query.touch(exist_ok=True)
                # Wait a literal second to let any other instances "respond"
                sleep(1)
                # If the other instance was alive, it would delete the query...
                if not query.exists():
                    # In that case, this one is occupied. Next!
                    continue

                with backup.open('r') as file:
                    recovered = [s.strip() for s in file.readlines()]

                    # Attempt to restore the recovered data.
                    timestamp = datetime(*(int(v)
                                           for v in recovered[0].split(':')))
                    duration = int(recovered[1])
                    TimeDisplay.restore_from_backup(timestamp, duration)
                    notes = recovered[2]
                    Notes.restore_from_backup(notes)

                    # Allow the user to either reset or save.
                    TimeControls._set_mode_save()

                # Clear the file we just loaded.
                backup.unlink(missing_ok=True)
                query.unlink(missing_ok=True)

                # We are now done. Quit.
                return

        except StopIteration:
            return  # Nothing to load, move along.
        except (IndexError, ValueError):
            # Warn about corrupted backup
            logging.warning(f"Corrupted backup file: {cls._backup_dir}")
            # Delete corrupted backup file
            backup.unlink(missing_ok=True)
            # Try again...maybe a different file will work?
            cls.check_for_recall()
Beispiel #11
0
def build():
    """Construct the interface."""
    App.build()
    App.add_widget(TimeDisplay.build())
    App.add_widget(Notes.build())
    App.add_widget(TimeControls.build())
    App.add_widget(Workspace.build())
    App.add_widget(AppControls.build())
    SysTray.build()
Beispiel #12
0
    def start_monitoring(cls):
        """Subscribe to the TimeDisplay events for purposes of backing up
        the timer in case of crash.
        """
        if cls._active:
            return

        cls._backup_dir.mkdir(parents=True, exist_ok=True)

        cls._active = True

        def tick(hours, minutes, seconds):
            # Prevent any other instances of Timecard from claiming recoveries
            cls._query.unlink(missing_ok=True)
            cls.remember(hours, minutes, seconds)

        def stop(erase):
            if erase:
                cls.forget()

        TimeDisplay.connect(on_minute=tick, on_stop=stop)
Beispiel #13
0
    def build(cls):
        """Construct the system tray"""
        cls.systray.setIcon(App.icon)

        cls.act_status.setText("00:00:00")
        cls.menu.addAction(cls.act_status)

        cls.menu.addAction(cls.act_time)
        cls.set_mode_stopped()

        cls.menu.addSeparator()

        cls.act_toggle.setIcon(QIcon.fromTheme('view-restore'))
        cls.act_toggle.setText("Show/Hide Window")
        cls.act_toggle.triggered.connect(cls.toggle_window)
        cls.menu.addAction(cls.act_toggle)

        cls.menu.addSeparator()

        cls.act_quit.setIcon(QIcon.fromTheme('application-exit'))
        cls.act_quit.setText("Quit Timecard")
        cls.act_quit.triggered.connect(cls.quit_app)
        cls.menu.addAction(cls.act_quit)

        cls.systray.setContextMenu(cls.menu)
        cls.systray.show()

        TimeDisplay.connect(on_tick=cls.update_time)
        TimeControls.connect(on_start=cls.set_mode_running,
                             on_resume=cls.set_mode_running,
                             on_pause=cls.set_mode_paused,
                             on_stop=cls.set_mode_save,
                             on_save=cls.set_mode_stopped,
                             on_reset=cls.set_mode_stopped)
        App.connect(on_hide=cls.popup)

        return cls.systray
Beispiel #14
0
    def notify(cls, hours, minutes, seconds):
        """Display notification."""
        if not cls.check():
            return
        cls.last_at = TimeDisplay.get_time_min()
        activity = Notes.get_text()
        if activity:
            message = random.choice(
                cls.prompt_with_note).substitute(task=activity)
        else:
            message = random.choice(cls.prompt_no_note)

        # Show the notification
        SysTray.popup(message)
        # Determine when next notification should appear
        cls.calculate_next()
Beispiel #15
0
    def save(cls):
        """Save a stopped timer's time to the log."""

        cls._set_mode_stopped()
        notes = Notes.get_text()
        timestamp = TimeDisplay.get_timestamp()
        TimeLog.add_to_log(timestamp, *TimeDisplay.get_time(), notes)
        LogView.refresh()
        Notes.clear()
        TimeDisplay.stop_time()
        TimeDisplay.reset_time()
        if cls.save_callback:
            cls.save_callback()
Beispiel #16
0
def build():
    """Construct the interface."""
    # Build the actual interface.
    App.build()
    App.add_widget(TimeDisplay.build())
    App.add_widget(Notes.build())
    App.add_widget(TimeControls.build())
    App.add_widget(Workspace.build())
    App.add_widget(AppControls.build())
    SysTray.build()

    # See if there's anything to recover from a damaged session.
    Backup.check_for_recall()
    # Start monitoring new timers.
    Backup.start_monitoring()

    # Initialize systems
    Focus.initialize()

    # Start the clock!
    Clock.start()
Beispiel #17
0
 def initialize(cls):
     """Initialize focus popup notification system."""
     cls.reload_settings()
     TimeDisplay.connect(on_minute=cls.notify)