File: Duration.py

package info (click to toggle)
uranium 5.0.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,328 kB
  • sloc: python: 31,765; sh: 132; makefile: 12
file content (153 lines) | stat: -rw-r--r-- 6,226 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright (c) 2022 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.

import enum
from typing import Optional

from PyQt6.QtCore import QObject, pyqtProperty, pyqtEnum, pyqtSignal
from UM.FlameProfiler import pyqtSlot

from datetime import timedelta
import math

from UM.Logger import Logger
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium")


class DurationFormat(QObject):
    class Format(enum.IntEnum):
        Seconds = 0
        Short = 1
        Long = 2
        ISO8601 = 3
    pyqtEnum(Format)


class Duration(QObject):
    """A class representing a time duration.

    This is primarily used as a value type to QML so we can report things
    like "How long will this print take" without needing a bunch of logic
    in the QML.
    """

    def __init__(self, duration: Optional[int] = None, parent = None) -> None:
        """Create a duration object.

        :param duration: The duration in seconds. If this is None (the default), an invalid Duration object will be created.
        :param parent: The QObject parent.
        """

        super().__init__(parent)

        self._days = -1
        self._hours = -1
        self._minutes = -1
        self._seconds = -1

        if duration is not None:
            self.setDuration(duration)

    durationChanged = pyqtSignal()

    @pyqtProperty(int, notify = durationChanged)
    def days(self):
        return self._days

    @pyqtProperty(int, notify = durationChanged)
    def hours(self):
        return self._hours

    @pyqtProperty(int, notify = durationChanged)
    def minutes(self):
        return self._minutes

    @pyqtProperty(int, notify = durationChanged)
    def seconds(self):
        return self._seconds

    @pyqtProperty(bool, notify = durationChanged)
    def valid(self):
        return self._days != -1 and self._hours != -1 and self._minutes != -1 and self._seconds != -1

    @pyqtProperty(bool, notify = durationChanged)
    def isTotalDurationZero(self):
        return self._days == 0 and self._hours == 0 and self._minutes == 0 and self._seconds == 0

    def setDuration(self, duration: int) -> None:
        """Set the duration in seconds.

        This will convert the given amount of seconds into an amount of days, hours, minutes and seconds.
        Note that this is mostly a workaround for issues with PyQt, as a value type this class should not
        really have a setter.
        """

        if duration < 0:
            self._days = -1
            self._hours = -1
            self._minutes = -1
            self._seconds = -1
        else:
            try:
                duration = round(duration)
            except OverflowError:
                Logger.log("w", "Duration was too large to convert, so resetting it.")
                duration = 0
            # If a Python int goes above the upper bound of C++ int, which is 2^16 - 1, you will get a error when Qt
            # tries to convert the Python int to C++ int:
            #    TypeError: unable to convert a Python 'int' object to a C++ 'int' instance
            # So we make sure here that the number won't exceed the limit due to CuraEngine bug or whatever, and
            # Cura won't crash.
            if int(duration) >= (2**31):
                Logger.log("w", "Duration was too large to convert, so resetting it.")
                duration = 0

            self._days = math.floor(duration / (3600 * 24))
            duration -= self._days * 3600 * 24
            self._hours = math.floor(duration / 3600)
            duration -= self._hours * 3600
            self._minutes = math.floor(duration / 60)
            duration -= self._minutes * 60
            self._seconds = duration

        self.durationChanged.emit()

    @pyqtSlot(int, result = str)
    def getDisplayString(self, display_format = DurationFormat.Format.Short):
        """Get a string representation of this object that can be used to display
        in interfaces.

        This is not called toString() primarily because that conflicts with
        JavaScript's toString().
        :return: A human-readable string representation of this duration.
        """

        if display_format == DurationFormat.Format.Seconds:
            return str(((self._days * 24 + self._hours)* 60 + self._minutes) * 60 + self._seconds )
        elif display_format == DurationFormat.Format.Short:
            if self._days > 0:
                return i18n_catalog.i18nc("@label Short days-hours-minutes format. {0} is days, {1} is hours, {2} is minutes", "{0:0>2}d {1:0>2}h {2:0>2}min", self._days, self._hours, self._minutes)
            else:
                return i18n_catalog.i18nc("@label Short hours-minutes format. {0} is hours, {1} is minutes", "{0:0>2}h {1:0>2}min", self._hours, self._minutes)
        elif display_format == DurationFormat.Format.Long:
            if self._days > 0:
                return i18n_catalog.i18ncp("@label Long duration format. {0} is days", "{0} day", "{0} days", self._days) + " " + i18n_catalog.i18ncp("@label Long duration format. {0} is hours", "{0} hour", "{0} hours", self._hours) + " " + i18n_catalog.i18ncp("@label Long duration format. {0} is minutes", "{0} minute", "{0} minutes", self._minutes)
            elif self._hours > 0:
                return i18n_catalog.i18ncp("@label Long duration format. {0} is hours", "{0} hour", "{0} hours", self._hours) + " " + i18n_catalog.i18ncp("@label Long duration format. {0} is minutes", "{0} minute", "{0} minutes", self._minutes)
            else:
                return i18n_catalog.i18ncp("@label Long duration format. {0} is minutes", "{0} minute", "{0} minutes", self._minutes)
        elif display_format == DurationFormat.Format.ISO8601:
            return "%02d:%02d:%02d" % (self._days * 24 + self._hours, self._minutes, self._seconds)

        return ""

    def __int__(self):
        """Get an integer representation of this duration.

        The integer contains the number of seconds in the duration. Convert it
        back to a Duration instance by providing the number of seconds to the
        constructor.
        """

        return self._days * 3600 * 24 + self._hours * 3600 + self._minutes * 60 + self._seconds